Compare commits

...

279 Commits

Author SHA1 Message Date
xiaoxia 916e624516 test: remove deleted module imports from smoke.test.tsx
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 48s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 58s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m7s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m9s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 2m32s
AI Code Review / AI Code Review (pull_request) Successful in 6m40s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 9m21s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 11m6s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 12m4s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 30m13s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 3s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 12s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m12s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 40h7m36s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 40h37m45s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 40h37m48s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 40h37m51s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 40h37m14s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 40h37m9s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 40h37m12s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 40h37m14s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 40h37m14s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 40h37m19s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 40h37m20s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 40h7m2s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 40h7m2s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 40h7m5s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 40h7m5s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 40h37m8s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 40h37m9s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 40h37m19s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 40h37m22s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 40h37m19s
2026-09-14 22:45:51 +08:00
xiaoxia b3fc8f1a58 test: remove obsolete test for deleted page/hook (useTemplateSelection.test.tsx)
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 40m48s
CI/CD Pipeline / Check if frontend-only change (pull_request) Failing after 40m53s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Failing after 40m54s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 1m38s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 40h41m1s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 40h41m7s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 40h41m12s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 40h40m41s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 40h40m41s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 40h40m32s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 40h40m34s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 40h40m34s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 40h40m35s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 40h40m36s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 40h40m41s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 40h40m30s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 40h40m30s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 40h40m41s
2026-09-14 21:49:29 +08:00
xiaoxia 0dbb44b8e5 test: remove obsolete test for deleted page/hook (TemplateLibrary.test.tsx)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
2026-09-14 21:49:24 +08:00
xiaoxia b619aa4fe9 test: remove obsolete test for deleted page/hook (MyTemplates.test.tsx)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Failing after 40m47s
AI Code Review / AI Code Review (pull_request) Failing after 40m54s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m18s
2026-09-14 21:49:19 +08:00
xiaoxia 63b991f0c6 test: remove obsolete test for deleted page/hook (EditingPlanner.test.tsx)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:49:14 +08:00
xiaoxia 4a244f7530 style: reformat smart-match-pool.test.ts with repo prettier config (#1899 #1900)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 4s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 49s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m24s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m29s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m47s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 2m4s
AI Code Review / AI Code Review (pull_request) Successful in 6m33s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 10m8s
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 41h45m23s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 41h45m31s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 41h44m53s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 41h44m46s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 41h44m51s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 41h44m53s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 41h44m53s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 41h45m0s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 41h45m1s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 41h44m45s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 41h44m46s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 41h44m54s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 41h44m54s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 41h45m4s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 42h20m20s
2026-09-14 21:38:03 +08:00
xiaoxia fd7cc25ac5 style: reformat GeneratePage.test.tsx with repo prettier config (#1899 #1900)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Waiting to run
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:38:00 +08:00
xiaoxia 93b32eba51 style: reformat navigation.test.ts with repo prettier config (#1899 #1900) 2026-09-14 21:37:58 +08:00
xiaoxia b18e5964f6 style: reformat appRoutes.tsx with repo prettier config (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:37:55 +08:00
xiaoxia 37290c09d7 style: reformat useStepNavigation.ts with repo prettier config (#1899 #1900)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:37:51 +08:00
xiaoxia 0697d9c67d style: reformat useStep2Materials.ts with repo prettier config (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:37:48 +08:00
xiaoxia 0e2cefaa5f style: reformat index.ts with repo prettier config (#1899 #1900)
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:37:45 +08:00
xiaoxia 712e381ec6 style: reformat useSmartMatch.ts with repo prettier config (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:37:36 +08:00
xiaoxia ad8dda8e36 style: reformat constants.ts with repo prettier config (#1899 #1900) 2026-09-14 21:37:32 +08:00
xiaoxia a42310215e style: reformat Step2MaterialSelect.tsx with repo prettier config (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:37:27 +08:00
xiaoxia 763dee6fb9 style: reformat GenerateStepActions.tsx with repo prettier config (#1899 #1900)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:37:24 +08:00
xiaoxia 5a9c97d64c style: reformat GeneratePage.tsx with repo prettier config (#1899 #1900) 2026-09-14 21:37:22 +08:00
xiaoxia e40175d358 style: reformat types.ts with repo prettier config (#1899 #1900)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 0s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Waiting to run
CI/CD Pipeline / Validate - Style (pull_request) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Validate - Security (pull_request) Waiting to run
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Waiting to run
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Unit Tests (pull_request) Waiting to run
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Integration Tests (pull_request) Waiting to run
CI/CD Pipeline / Frontend Lint (pull_request) Waiting to run
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (pull_request) Waiting to run
CI/CD Pipeline / PR Build Web Image (pull_request) Waiting to run
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Waiting to run
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:37:20 +08:00
xiaoxia a74f586564 style: reformat navigation.ts with repo prettier config (#1899 #1900) 2026-09-14 21:37:18 +08:00
xiaoxia ac606fcf1e style: format appRoutes.tsx with prettier (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m43s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 1m58s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m0s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m33s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 3m32s
AI Code Review / AI Code Review (pull_request) Successful in 7m1s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 41h54m8s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 41h54m17s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 41h54m18s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 41h53m47s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 41h53m44s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 41h53m46s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 41h53m46s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 41h53m49s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 41h53m49s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 41h53m55s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 41h53m38s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 41h53m38s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 41h53m47s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 41h53m48s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 42h29m12s
2026-09-14 21:29:13 +08:00
xiaoxia d176589c53 style: format useStepNavigation.ts with prettier (#1899 #1900) 2026-09-14 21:29:09 +08:00
xiaoxia 6f06de232b style: format useStep2Materials.ts with prettier (#1899 #1900)
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:29:05 +08:00
xiaoxia 1962b8abc3 style: format index.ts with prettier (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
2026-09-14 21:29:02 +08:00
xiaoxia 517d05f9cc style: format useSmartMatch.ts with prettier (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:28:59 +08:00
xiaoxia 8fcb0c99b0 style: format constants.ts with prettier (#1899 #1900) 2026-09-14 21:28:57 +08:00
xiaoxia f14a83ff35 style: format Step2MaterialSelect.tsx with prettier (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 41h54m48s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 41h54m19s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 41h54m18s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 41h54m20s
2026-09-14 21:28:55 +08:00
xiaoxia b86b500152 style: format GenerateStepActions.tsx with prettier (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:28:53 +08:00
xiaoxia c3503523e6 style: format navigation.ts with prettier (#1899 #1900) 2026-09-14 21:28:52 +08:00
xiaoxia ab1896b609 style: format editing-planner/types.ts with prettier (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 48s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 58s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 46s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m13s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 3m30s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 41h58m13s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 41h58m25s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 42h0m10s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 41h57m55s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 41h57m57s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 41h57m30s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 41h57m30s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 41h57m43s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 41h58m44s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 41h59m39s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 41h59m42s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 41h57m30s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 41h57m36s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 41h57m44s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 42h33m24s
2026-09-14 21:23:26 +08:00
xiaoxia 5d3ef34857 style: format GeneratePage.tsx with prettier (#1899 #1900) 2026-09-14 21:23:24 +08:00
xiaoxia d201d6fd91 chore: remove obsolete test file (#1899 #1900)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 36s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m1s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 37s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 42h0m36s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 42h1m33s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 42h2m10s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 42h1m4s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 42h1m36s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 42h0m6s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 42h0m7s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 42h0m8s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 42h1m4s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 42h1m4s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 42h0m6s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 42h0m6s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 42h1m41s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 42h1m45s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 42h35m33s
2026-09-14 21:21:26 +08:00
xiaoxia 85ad3827ad chore: remove obsolete test file (#1899 #1900) 2026-09-14 21:21:24 +08:00
xiaoxia dbe9adf04e chore: remove obsolete test file (#1899 #1900)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:21:20 +08:00
xiaoxia 968176f036 chore: remove obsolete test file (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:21:18 +08:00
xiaoxia 0619f096dd chore: remove obsolete test file (#1899 #1900)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:21:13 +08:00
xiaoxia 1e9a981586 chore: remove obsolete test file (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:21:10 +08:00
xiaoxia e866851284 chore: remove obsolete test file (#1899 #1900) 2026-09-14 21:21:07 +08:00
xiaoxia dcae4fcef8 chore: remove obsolete test file (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:21:05 +08:00
xiaoxia caa7d9958b chore: remove obsolete test file (#1899 #1900)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:21:03 +08:00
xiaoxia 0b9d3cb523 chore: remove obsolete test file (#1899 #1900)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:20:41 +08:00
xiaoxia 75bec86e9f style: format GeneratePage.test.tsx with prettier
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:20:16 +08:00
xiaoxia 81fd9eceb3 style: format navigation.test.ts with prettier
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Validate - Style (pull_request) Waiting to run
CI/CD Pipeline / Validate - Security (pull_request) Waiting to run
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Waiting to run
CI/CD Pipeline / Unit Tests (pull_request) Waiting to run
CI/CD Pipeline / Integration Tests (pull_request) Waiting to run
CI/CD Pipeline / Frontend Lint (pull_request) Waiting to run
CI/CD Pipeline / Frontend Unit Tests (pull_request) Waiting to run
CI/CD Pipeline / PR Build API Image (pull_request) Waiting to run
CI/CD Pipeline / PR Build Web Image (pull_request) Waiting to run
CI/CD Pipeline / PR Build Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging Web Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:20:11 +08:00
xiaoxia 6451165e0e style: format smart-match-pool.test.ts with prettier
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:20:07 +08:00
xiaoxia f7fc8d0741 test: add isAssetUsable mock to api/assets (#1899)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 43s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 30s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 2m18s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m2s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 42h3m48s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 42h6m57s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 42h5m44s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 42h6m31s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 42h6m31s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 42h6m32s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 42h3m14s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 42h4m5s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 42h5m13s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 42h5m14s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 42h5m14s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 42h6m27s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 42h6m34s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 42h40m40s
2026-09-14 21:16:30 +08:00
xiaoxia 5e5ee488fe test: remove templates assertion from navigation test (#1899)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 4s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:16:26 +08:00
xiaoxia 0c70dbb7ca test: rewrite smart-match-pool test for computeLimitFromClipCount (#1899) 2026-09-14 21:16:23 +08:00
xiaoxia 44bc0a24ff fix: remove unused userTemplates/handleInvalidTemplate (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 42h8m0s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 42h7m35s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 42h7m31s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 42h7m29s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 42h7m28s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 42h7m27s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 42h7m34s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 42h7m41s
2026-09-14 21:15:26 +08:00
xiaoxia 7bf825dadb chore: remove test file ClipCard.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 29s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 46s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 1m8s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 42h9m17s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 42h9m22s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 42h9m33s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 42h9m3s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 42h9m2s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 42h9m2s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 42h8m49s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 42h8m52s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 42h8m52s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 42h9m0s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 42h9m1s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 42h8m47s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 42h8m47s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 42h9m17s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 42h44m27s
2026-09-14 21:13:50 +08:00
xiaoxia 5a6ac78df9 chore: remove test file AddClipPicker.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
2026-09-14 21:13:47 +08:00
xiaoxia 923e577630 chore: remove test file WatermarkPanel.test.tsx (#1899 #1900)
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
2026-09-14 21:13:41 +08:00
xiaoxia 9332c9d60e chore: remove test file useTtsPanel.test.ts (#1899 #1900) 2026-09-14 21:13:38 +08:00
xiaoxia b4f9d21ef8 chore: remove test file constants.test.ts (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
2026-09-14 21:13:36 +08:00
xiaoxia 90278d816c chore: remove test file VoiceSelector.test.tsx (#1899 #1900) 2026-09-14 21:13:32 +08:00
xiaoxia 3296a71166 chore: remove test file TtsSlider.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:13:29 +08:00
xiaoxia 6487e790a4 chore: remove test file TtsPanel.test.tsx (#1899 #1900)
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
2026-09-14 21:13:26 +08:00
xiaoxia 4fa1f35e05 chore: remove test file TtsPanel.smoke.test.tsx (#1899 #1900)
CI/CD Pipeline / Validate - Style (pull_request) Blocked by required conditions
CI/CD Pipeline / Validate - Security (pull_request) Blocked by required conditions
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Blocked by required conditions
CI/CD Pipeline / Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Waiting to run
PR Automation / Auto Merge on CI Green + Approved (pull_request) Blocked by required conditions
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:13:25 +08:00
xiaoxia b062d9fed2 chore: remove test file TransitionSelector.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Blocked by required conditions
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:13:21 +08:00
xiaoxia 14f36a3bb9 chore: remove test file TimelinePanel.test.tsx (#1899 #1900) 2026-09-14 21:13:18 +08:00
xiaoxia bc42f515bf chore: remove test file SubtitleStylePanel.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Waiting to run
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Waiting to run
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
2026-09-14 21:13:15 +08:00
xiaoxia c811d266f2 chore: remove test file useStickerPanel.test.ts (#1899 #1900)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
2026-09-14 21:13:10 +08:00
xiaoxia a7738dcfc3 chore: remove test file constants.test.ts (#1899 #1900) 2026-09-14 21:13:06 +08:00
xiaoxia bbc24b8363 chore: remove test file StickerTabs.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:13:02 +08:00
xiaoxia 86cd68ea8d chore: remove test file StickerPropsEditor.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:12:58 +08:00
xiaoxia c144b4adaf chore: remove test file StickerList.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
2026-09-14 21:12:55 +08:00
xiaoxia 1b6a10d594 chore: remove test file StickerPanel.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
2026-09-14 21:12:50 +08:00
xiaoxia c8a6209a70 chore: remove test file SpeedPanel.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Blocked by required conditions
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:12:47 +08:00
xiaoxia 380d64be7a chore: remove test file SaveModal.test.tsx (#1899 #1900) 2026-09-14 21:12:43 +08:00
xiaoxia 4efb7d2f3a chore: remove test file PreviewPlayer.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Waiting to run
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:12:38 +08:00
xiaoxia 0d491d7fe6 chore: remove test file usePipConfigPanel.test.ts (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Blocked by required conditions
CI/CD Pipeline / Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has started running
2026-09-14 21:12:34 +08:00
xiaoxia 0c71b8ccc8 chore: remove test file constants.test.ts (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:12:31 +08:00
xiaoxia 8ce9064cf9 chore: remove test file PipPreview.test.tsx (#1899 #1900)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:12:25 +08:00
xiaoxia d6d009d1ee chore: remove test file LayerList.test.tsx (#1899 #1900) 2026-09-14 21:12:20 +08:00
xiaoxia 0787911600 chore: remove test file LayerConfig.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:12:16 +08:00
xiaoxia 671a5d17c5 chore: remove test file PipConfigPanel.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Blocked by required conditions
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Blocked by required conditions
CI/CD Pipeline / Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:12:13 +08:00
xiaoxia 62a0ceea4d chore: remove test file MediaPanel.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:12:10 +08:00
xiaoxia 53bd275f3c chore: remove test file IntroOutroPanel.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Waiting to run
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging API Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging Web Image (pull_request) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (pull_request) Waiting to run
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
PR Automation / Auto Approve on CI Green (pull_request) Waiting to run
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
PR Automation / Auto Merge on CI Green + Approved (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:12:05 +08:00
xiaoxia c88cdbf41a chore: remove test file GreenScreenPanel.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Waiting to run
CI/CD Pipeline / Validate - Style (pull_request) Blocked by required conditions
CI/CD Pipeline / Validate - Security (pull_request) Blocked by required conditions
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Blocked by required conditions
CI/CD Pipeline / Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
Preview Deploy / Deploy Preview Environment (pull_request) Waiting to run
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
2026-09-14 21:12:03 +08:00
xiaoxia 919c7a8fec chore: remove test file FilterPanel.test.tsx (#1899 #1900)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:12:01 +08:00
xiaoxia 2813d7f9c8 chore: remove test file EditorClipList.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:11:59 +08:00
xiaoxia ef5265b0fb chore: remove test file ClipPropertiesPanel.test.tsx (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-09-14 21:11:58 +08:00
xiaoxia 00cf91930b chore: remove test file BgmSelector.test.tsx (#1899 #1900) 2026-09-14 21:11:57 +08:00
xiaoxia d5fe189c84 fix: 添加最小化 editing-planner/types.ts 供 api 层导入 (#1899 #1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 35s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 1m9s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m17s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 1m43s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 2m43s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m52s
AI Code Review / AI Code Review (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 42h14m44s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 42h14m56s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 42h14m58s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 42h14m27s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 42h14m21s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 42h14m26s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 42h14m26s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 42h14m29s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 42h14m29s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 42h14m29s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 42h14m14s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 42h14m15s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 42h14m28s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 42h14m26s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 42h14m30s
2026-09-14 21:08:43 +08:00
xiaoxia dc79fbfa7b refactor: remove templates/editing-planner/my-templates nav items (#1900)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 48s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 1m3s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m4s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 1m48s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 3m23s
AI Code Review / AI Code Review (pull_request) Successful in 6m48s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 10m18s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m38s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 12m27s
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 42h29m20s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 42h29m28s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 42h29m30s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 42h28m57s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 42h28m55s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 42h28m55s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 42h28m55s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 42h28m58s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 42h28m58s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 42h29m0s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 42h29m0s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 42h28m50s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 42h28m50s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 42h29m2s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 43h4m23s
2026-09-14 20:52:40 +08:00
xiaoxia 12afeee905 refactor: remove templates/editing-planner/my-templates routes (#1900) 2026-09-14 20:52:39 +08:00
xiaoxia 18702f58c0 refactor: rewrite GeneratePage for 5-step flow with clipCount (#1899) 2026-09-14 20:52:12 +08:00
xiaoxia 79f985de6b refactor: simplify useGenerateFormState - remove template UI, add clipCount (#1899) 2026-09-14 20:49:58 +08:00
xiaoxia 1cbcec7508 refactor: replace templateSegments with clipCount in useSmartMatch (#1899) 2026-09-14 20:48:55 +08:00
xiaoxia 0f443f794d refactor: replace templateSegments with clipCount in useStep2Materials (#1899) 2026-09-14 20:48:55 +08:00
xiaoxia 73cddd73ba refactor: update Step2MaterialSelect with clipCount prop, remove template props (#1899) 2026-09-14 20:47:55 +08:00
xiaoxia dd4193e7ba refactor: rewrite useStepNavigation for 5-step flow (#1899) 2026-09-14 20:47:55 +08:00
xiaoxia d446149304 refactor: rewrite GenerateStepActions for 5-step flow (#1899) 2026-09-14 20:47:05 +08:00
xiaoxia f8987dcca7 refactor: rewrite GenerateStepContent for 5-step flow (#1899) 2026-09-14 20:47:04 +08:00
xiaoxia d48e8d4bdf refactor: update STEPS to 5 steps + add clip count constants (#1899) 2026-09-14 20:45:58 +08:00
xiaoxia 347b2a367a refactor: delete useTitleCoverSync.ts (#1899 #1900) 2026-09-14 20:45:33 +08:00
xiaoxia d41b4cacbc refactor: delete useTemplateSelection.ts (#1899 #1900) 2026-09-14 20:45:33 +08:00
xiaoxia 1a11ee2b17 refactor: delete useStep1Template.ts (#1899 #1900) 2026-09-14 20:45:32 +08:00
xiaoxia 821cd85267 refactor: delete Step1TemplateSelect.tsx (#1899 #1900) 2026-09-14 20:45:32 +08:00
xiaoxia 32d6604fe3 refactor: delete useMyTemplates.ts (#1899 #1900) 2026-09-14 20:45:31 +08:00
xiaoxia 0cf2e0edc6 refactor: delete TemplateCard.tsx (#1899 #1900) 2026-09-14 20:45:31 +08:00
xiaoxia bc443e4dbb refactor: delete MyTemplates.tsx (#1899 #1900) 2026-09-14 20:45:30 +08:00
xiaoxia 550a834588 refactor: delete MyTemplates.css (#1899 #1900) 2026-09-14 20:45:30 +08:00
xiaoxia 0db752684e refactor: delete templateLibrary.ts (#1899 #1900) 2026-09-14 20:45:29 +08:00
xiaoxia 6e61d9ba5e refactor: delete templateLibrary.ts (#1899 #1900) 2026-09-14 20:45:29 +08:00
xiaoxia 11b9955bac refactor: delete templates.css (#1899 #1900) 2026-09-14 20:45:29 +08:00
xiaoxia 14e098ca8f refactor: delete useTemplateLibrary.ts (#1899 #1900) 2026-09-14 20:45:28 +08:00
xiaoxia 663e5ef777 refactor: delete useTemplateDetail.ts (#1899 #1900) 2026-09-14 20:45:28 +08:00
xiaoxia a181384ce5 refactor: delete templateLibrary.ts (#1899 #1900) 2026-09-14 20:45:27 +08:00
xiaoxia 8e6c42ad6d refactor: delete types.ts (#1899 #1900) 2026-09-14 20:45:27 +08:00
xiaoxia be960bf55e refactor: delete index.ts (#1899 #1900) 2026-09-14 20:45:26 +08:00
xiaoxia 0e6b305ba0 refactor: delete StyleConfig.tsx (#1899 #1900) 2026-09-14 20:45:26 +08:00
xiaoxia 88f62096b5 refactor: delete SegmentList.tsx (#1899 #1900) 2026-09-14 20:45:26 +08:00
xiaoxia 9f332aac38 refactor: delete PreviewArea.tsx (#1899 #1900) 2026-09-14 20:45:25 +08:00
xiaoxia a667239137 refactor: delete DetailFooter.tsx (#1899 #1900) 2026-09-14 20:45:24 +08:00
xiaoxia 78454c2bf2 refactor: delete TemplateToolbar.tsx (#1899 #1900) 2026-09-14 20:45:24 +08:00
xiaoxia b8643bb643 refactor: delete TemplateHeader.tsx (#1899 #1900) 2026-09-14 20:45:23 +08:00
xiaoxia 3eaa130670 refactor: delete TemplateGrid.tsx (#1899 #1900) 2026-09-14 20:45:23 +08:00
xiaoxia 56b6a72a04 refactor: delete TemplateDetailModal.tsx (#1899 #1900) 2026-09-14 20:45:21 +08:00
xiaoxia 6e96ef4c32 refactor: delete TemplateCard.tsx (#1899 #1900) 2026-09-14 20:45:21 +08:00
xiaoxia e31255dec6 refactor: delete TemplateLibrary.tsx (#1899 #1900) 2026-09-14 20:45:20 +08:00
xiaoxia 59ba499032 refactor: delete timeline.ts (#1899 #1900) 2026-09-14 20:45:20 +08:00
xiaoxia 0970edb2fd refactor: delete selectors.ts (#1899 #1900) 2026-09-14 20:45:19 +08:00
xiaoxia 211c4f4e05 refactor: delete clipProperties.ts (#1899 #1900) 2026-09-14 20:45:19 +08:00
xiaoxia 9c93911ae4 refactor: delete watermark.ts (#1899 #1900) 2026-09-14 20:45:18 +08:00
xiaoxia f67e6400ba refactor: delete tts.ts (#1899 #1900) 2026-09-14 20:45:17 +08:00
xiaoxia c8a2ea0b3b refactor: delete trim.ts (#1899 #1900) 2026-09-14 20:45:17 +08:00
xiaoxia ee7f37229a refactor: delete transition.ts (#1899 #1900) 2026-09-14 20:45:17 +08:00
xiaoxia 70fb1a69da refactor: delete title.ts (#1899 #1900) 2026-09-14 20:45:16 +08:00
xiaoxia 60d44c11dc refactor: delete subtitle.ts (#1899 #1900) 2026-09-14 20:45:15 +08:00
xiaoxia d8596fec28 refactor: delete sticker.ts (#1899 #1900) 2026-09-14 20:45:14 +08:00
xiaoxia 21335df135 refactor: delete speed.ts (#1899 #1900) 2026-09-14 20:45:14 +08:00
xiaoxia c7132f6350 refactor: delete pip.ts (#1899 #1900) 2026-09-14 20:45:13 +08:00
xiaoxia 32606867bf refactor: delete intro-outro.ts (#1899 #1900) 2026-09-14 20:45:13 +08:00
xiaoxia 9a9cfe0415 refactor: delete index.ts (#1899 #1900) 2026-09-14 20:45:12 +08:00
xiaoxia 055e1689db refactor: delete filter.ts (#1899 #1900) 2026-09-14 20:45:12 +08:00
xiaoxia 90d808d5fd refactor: delete clipProperties.ts (#1899 #1900) 2026-09-14 20:45:12 +08:00
xiaoxia e942e8b5bb refactor: delete clip.ts (#1899 #1900) 2026-09-14 20:45:11 +08:00
xiaoxia 4d21fdd36b refactor: delete chroma-key.ts (#1899 #1900) 2026-09-14 20:45:05 +08:00
xiaoxia 2fd67ed309 refactor: delete useWatermarkConfig.ts (#1899 #1900) 2026-09-14 20:45:05 +08:00
xiaoxia 726843a1cf refactor: delete useVoicePreview.ts (#1899 #1900) 2026-09-14 20:45:04 +08:00
xiaoxia b27d16c2fa refactor: delete useVoiceMaterials.ts (#1899 #1900) 2026-09-14 20:45:04 +08:00
xiaoxia 357943816f refactor: delete useUndoRedo.ts (#1899 #1900) 2026-09-14 20:45:03 +08:00
xiaoxia 19b93deea8 refactor: delete useTtsPanel.ts (#1899 #1900) 2026-09-14 20:45:02 +08:00
xiaoxia b2b20535e4 refactor: delete useTrimDrag.ts (#1899 #1900) 2026-09-14 20:45:02 +08:00
xiaoxia 9df56657f0 refactor: delete useTimelineMenus.ts (#1899 #1900) 2026-09-14 20:45:01 +08:00
xiaoxia 8e6a46ae0a refactor: delete useTemplateManagement.ts (#1899 #1900) 2026-09-14 20:45:01 +08:00
xiaoxia f98ad2d9bb refactor: delete useStickerItems.ts (#1899 #1900) 2026-09-14 20:45:01 +08:00
xiaoxia 9e2a84b2c9 refactor: delete usePlaybackControl.ts (#1899 #1900) 2026-09-14 20:45:00 +08:00
xiaoxia 99186d30c0 refactor: delete usePipLayers.ts (#1899 #1900) 2026-09-14 20:44:59 +08:00
xiaoxia fa7ec8fc67 refactor: delete useGlobalSettings.ts (#1899 #1900) 2026-09-14 20:44:59 +08:00
xiaoxia 0213447bdc refactor: delete useEditorDrawers.ts (#1899 #1900) 2026-09-14 20:44:59 +08:00
xiaoxia 59c74e344f refactor: delete useEditPlanClipMutations.ts (#1899 #1900) 2026-09-14 20:44:58 +08:00
xiaoxia 2a65263b7d refactor: delete useEditPlanClipList.ts (#1899 #1900) 2026-09-14 20:44:58 +08:00
xiaoxia 0ec30fad27 refactor: delete useClipReorder.ts (#1899 #1900) 2026-09-14 20:44:57 +08:00
xiaoxia 87b2fec441 refactor: delete useClipImport.ts (#1899 #1900) 2026-09-14 20:44:57 +08:00
xiaoxia 118b364520 refactor: delete index.ts (#1899 #1900) 2026-09-14 20:44:57 +08:00
xiaoxia 231f6063e4 refactor: delete useClipTrim.ts (#1899 #1900) 2026-09-14 20:44:56 +08:00
xiaoxia ce8ad6db4e refactor: delete useClipEffects.ts (#1899 #1900) 2026-09-14 20:44:56 +08:00
xiaoxia c2e2b3aa1b refactor: delete useClipBasicOps.ts (#1899 #1900) 2026-09-14 20:44:55 +08:00
xiaoxia f2a44e5393 refactor: delete index.ts (#1899 #1900) 2026-09-14 20:44:54 +08:00
xiaoxia e0e631bf7b refactor: delete useClipDrag.ts (#1899 #1900) 2026-09-14 20:44:54 +08:00
xiaoxia c0404816c3 refactor: delete useContextMenu.ts (#1899 #1900) 2026-09-14 20:44:53 +08:00
xiaoxia ce4e9b95e8 refactor: delete useAddPicker.ts (#1899 #1900) 2026-09-14 20:44:53 +08:00
xiaoxia 7f7c3bb683 refactor: delete useTemplateSave.ts (#1899 #1900) 2026-09-14 20:44:52 +08:00
xiaoxia 47ffde9c3b refactor: delete useTemplateList.ts (#1899 #1900) 2026-09-14 20:44:52 +08:00
xiaoxia 231293adef refactor: delete useTemplateDetail.ts (#1899 #1900) 2026-09-14 20:44:52 +08:00
xiaoxia 253e21efdf refactor: delete usePlanLoading.ts (#1899 #1900) 2026-09-14 20:44:51 +08:00
xiaoxia 76f8c47324 refactor: delete watermark.ts (#1899 #1900) 2026-09-14 20:44:50 +08:00
xiaoxia 326927b16c refactor: delete tts.ts (#1899 #1900) 2026-09-14 20:44:50 +08:00
xiaoxia 574cb89014 refactor: delete timeline.ts (#1899 #1900) 2026-09-14 20:44:50 +08:00
xiaoxia d60eba950e refactor: delete subtitleStyle.ts (#1899 #1900) 2026-09-14 20:44:49 +08:00
xiaoxia 72bf12253e refactor: delete sticker.ts (#1899 #1900) 2026-09-14 20:44:49 +08:00
xiaoxia 0df1eea3bf refactor: delete pipConfig.ts (#1899 #1900) 2026-09-14 20:44:49 +08:00
xiaoxia ac5c3ed1ef refactor: delete introOutro.ts (#1899 #1900) 2026-09-14 20:44:48 +08:00
xiaoxia 5402134bd3 refactor: delete filter.ts (#1899 #1900) 2026-09-14 20:44:48 +08:00
xiaoxia 339789c135 refactor: delete clipProperties.ts (#1899 #1900) 2026-09-14 20:44:47 +08:00
xiaoxia 3dcea7393a refactor: delete constants.ts (#1899 #1900) 2026-09-14 20:44:47 +08:00
xiaoxia 37dcaf498c refactor: delete WatermarkTypeTabs.tsx (#1899 #1900) 2026-09-14 20:44:46 +08:00
xiaoxia d08b4e82eb refactor: delete WatermarkCommonSection.tsx (#1899 #1900) 2026-09-14 20:44:46 +08:00
xiaoxia dd5aeb4bb3 refactor: delete TextWatermarkSection.tsx (#1899 #1900) 2026-09-14 20:44:45 +08:00
xiaoxia 7d24f7a1fd refactor: delete ScrollWatermarkSection.tsx (#1899 #1900) 2026-09-14 20:44:45 +08:00
xiaoxia c62e7d2261 refactor: delete ImageWatermarkSection.tsx (#1899 #1900) 2026-09-14 20:44:44 +08:00
xiaoxia 0f3d6b5421 refactor: delete usePlayheadDrag.ts (#1899 #1900) 2026-09-14 20:44:44 +08:00
xiaoxia c7b99fadbb refactor: delete TrimPreview.tsx (#1899 #1900) 2026-09-14 20:44:44 +08:00
xiaoxia 53bcac7df6 refactor: delete TimelineHeader.tsx (#1899 #1900) 2026-09-14 20:44:43 +08:00
xiaoxia f8c1610bd9 refactor: delete TimeRuler.tsx (#1899 #1900) 2026-09-14 20:44:43 +08:00
xiaoxia 7fec9afda0 refactor: delete ContextMenu.tsx (#1899 #1900) 2026-09-14 20:44:43 +08:00
xiaoxia c254a791e2 refactor: delete ClipTrack.tsx (#1899 #1900) 2026-09-14 20:44:42 +08:00
xiaoxia c0c6e954dc refactor: delete ClipCard.tsx (#1899 #1900) 2026-09-14 20:44:41 +08:00
xiaoxia 2a8218a89d refactor: delete AddClipPicker.tsx (#1899 #1900) 2026-09-14 20:44:41 +08:00
xiaoxia 28444a7d8a refactor: delete SubtitlePreview.tsx (#1899 #1900) 2026-09-14 20:44:41 +08:00
xiaoxia 8825f2f45f refactor: delete SubtitlePositionSelector.tsx (#1899 #1900) 2026-09-14 20:44:40 +08:00
xiaoxia c5c887e571 refactor: delete SubtitleModeSwitch.tsx (#1899 #1900) 2026-09-14 20:44:39 +08:00
xiaoxia d31631ace2 refactor: delete SubtitleEffectButtons.tsx (#1899 #1900) 2026-09-14 20:44:39 +08:00
xiaoxia 5a8287354d refactor: delete IntroOutroBlock.tsx (#1899 #1900) 2026-09-14 20:44:38 +08:00
xiaoxia e661a07b69 refactor: delete GreenScreenSliders.tsx (#1899 #1900) 2026-09-14 20:44:37 +08:00
xiaoxia 5838b4aa9f refactor: delete GreenScreenPreview.tsx (#1899 #1900) 2026-09-14 20:44:36 +08:00
xiaoxia 5f116474d7 refactor: delete GreenScreenPresets.tsx (#1899 #1900) 2026-09-14 20:44:35 +08:00
xiaoxia 5c3161199e refactor: delete GreenScreenCustomColor.tsx (#1899 #1900) 2026-09-14 20:44:35 +08:00
xiaoxia c6a5b1cf92 refactor: delete FilterPresetGrid.tsx (#1899 #1900) 2026-09-14 20:44:34 +08:00
xiaoxia d64d918f3b refactor: delete FilterManualAdjust.tsx (#1899 #1900) 2026-09-14 20:44:34 +08:00
xiaoxia 0909e39715 refactor: delete types.ts (#1899 #1900) 2026-09-14 20:44:33 +08:00
xiaoxia 03b6fb0bd8 refactor: delete GlobalDrawers.tsx (#1899 #1900) 2026-09-14 20:44:33 +08:00
xiaoxia c20d4152a7 refactor: delete ClipLevelDrawers.tsx (#1899 #1900) 2026-09-14 20:44:32 +08:00
xiaoxia 7125f10ec0 refactor: delete index.tsx (#1899 #1900) 2026-09-14 20:44:31 +08:00
xiaoxia 33a09b1a6c refactor: delete VoiceMaterialSection.tsx (#1899 #1900) 2026-09-14 20:44:30 +08:00
xiaoxia 78741ed588 refactor: delete ClipTypeAndDuration.tsx (#1899 #1900) 2026-09-14 20:44:30 +08:00
xiaoxia 50ad79b1c7 refactor: delete AdvancedEntries.tsx (#1899 #1900) 2026-09-14 20:44:28 +08:00
xiaoxia 32f858ce3e refactor: delete TitleSettingsSection.tsx (#1899 #1900) 2026-09-14 20:44:28 +08:00
xiaoxia c34e8f60cf refactor: delete SubtitleSettingsSection.tsx (#1899 #1900) 2026-09-14 20:44:27 +08:00
xiaoxia 7dc0c833e9 refactor: delete StatsSection.tsx (#1899 #1900) 2026-09-14 20:44:26 +08:00
xiaoxia 7e8eb2cd58 refactor: delete ClipDetailSection.tsx (#1899 #1900) 2026-09-14 20:44:25 +08:00
xiaoxia 7b9840a6a9 refactor: delete BgmSettingsSection.tsx (#1899 #1900) 2026-09-14 20:44:24 +08:00
xiaoxia 69a1b16dd5 refactor: delete useBgmSelector.ts (#1899 #1900) 2026-09-14 20:44:23 +08:00
xiaoxia 45bd7995b7 refactor: delete index.tsx (#1899 #1900) 2026-09-14 20:44:22 +08:00
xiaoxia 43f460d078 refactor: delete BgmMixConfig.tsx (#1899 #1900) 2026-09-14 20:44:21 +08:00
xiaoxia 3966409a37 refactor: delete BgmItem.tsx (#1899 #1900) 2026-09-14 20:44:20 +08:00
xiaoxia 69da356a9a refactor: delete WatermarkPanel.tsx (#1899 #1900) 2026-09-14 20:44:19 +08:00
xiaoxia 6204712fa3 refactor: delete index.tsx (#1899 #1900) 2026-09-14 20:44:18 +08:00
xiaoxia e706d8ef9c refactor: delete useTtsPanel.ts (#1899 #1900) 2026-09-14 20:44:17 +08:00
xiaoxia 7a03b629c6 refactor: delete constants.ts (#1899 #1900) 2026-09-14 20:44:15 +08:00
xiaoxia 831d7a9711 refactor: delete VoiceSelector.tsx (#1899 #1900) 2026-09-14 20:44:15 +08:00
xiaoxia be5a295b7f refactor: delete TtsSlider.tsx (#1899 #1900) 2026-09-14 20:44:07 +08:00
xiaoxia cc1e1ef9c5 refactor: delete TransitionSelector.tsx (#1899 #1900) 2026-09-14 20:44:06 +08:00
xiaoxia ac68a4def3 refactor: delete TopBar.tsx (#1899 #1900) 2026-09-14 20:44:04 +08:00
xiaoxia bee15fd6f9 refactor: delete TimelinePanel.tsx (#1899 #1900) 2026-09-14 20:44:04 +08:00
xiaoxia c8fa0e0f19 refactor: delete SubtitleStylePanel.tsx (#1899 #1900) 2026-09-14 20:44:03 +08:00
xiaoxia 7e1a52925e refactor: delete index.tsx (#1899 #1900) 2026-09-14 20:44:02 +08:00
xiaoxia db455e40ef refactor: delete useStickerPanel.ts (#1899 #1900) 2026-09-14 20:44:01 +08:00
xiaoxia 056fc4b257 refactor: delete constants.ts (#1899 #1900) 2026-09-14 20:43:59 +08:00
xiaoxia dcc100d646 refactor: delete StickerTabs.tsx (#1899 #1900) 2026-09-14 20:43:58 +08:00
xiaoxia 6c12e0d847 refactor: delete StickerPropsEditor.tsx (#1899 #1900) 2026-09-14 20:43:56 +08:00
xiaoxia 56e3ab92a6 refactor: delete StickerList.tsx (#1899 #1900) 2026-09-14 20:43:55 +08:00
xiaoxia 24c86dcabf refactor: delete StatusBar.tsx (#1899 #1900) 2026-09-14 20:43:54 +08:00
xiaoxia f8cce24fa4 refactor: delete SpeedPanel.tsx (#1899 #1900) 2026-09-14 20:43:50 +08:00
xiaoxia 3a2323a2af refactor: delete SaveModal.tsx (#1899 #1900) 2026-09-14 20:43:50 +08:00
xiaoxia fdafad2947 refactor: delete RightPanel.tsx (#1899 #1900) 2026-09-14 20:43:48 +08:00
xiaoxia 6c6b8aed75 refactor: delete PreviewPlayer.tsx (#1899 #1900) 2026-09-14 20:43:46 +08:00
xiaoxia c91514d7d8 refactor: delete PreviewPlayer.css (#1899 #1900) 2026-09-14 20:43:45 +08:00
xiaoxia 6d83087be1 refactor: delete index.tsx (#1899 #1900) 2026-09-14 20:43:44 +08:00
xiaoxia d7abc9ddd7 refactor: delete usePipConfigPanel.ts (#1899 #1900) 2026-09-14 20:43:42 +08:00
xiaoxia 1c9f7642fa refactor: delete constants.ts (#1899 #1900) 2026-09-14 20:43:41 +08:00
xiaoxia 8826629583 refactor: delete PipPreview.tsx (#1899 #1900) 2026-09-14 20:43:37 +08:00
xiaoxia 52d083dbe2 refactor: delete LayerList.tsx (#1899 #1900) 2026-09-14 20:43:37 +08:00
xiaoxia 5c33769930 refactor: delete LayerConfig.tsx (#1899 #1900) 2026-09-14 20:43:36 +08:00
xiaoxia d47803bf35 refactor: delete ModeBar.tsx (#1899 #1900) 2026-09-14 20:43:35 +08:00
xiaoxia 4442e46d1d refactor: delete MediaPanel.tsx (#1899 #1900) 2026-09-14 20:43:34 +08:00
xiaoxia 2c8038e5f4 refactor: delete IntroOutroPanel.tsx (#1899 #1900) 2026-09-14 20:43:33 +08:00
xiaoxia edc68067e9 refactor: delete GreenScreenPanel.tsx (#1899 #1900) 2026-09-14 20:43:33 +08:00
xiaoxia 7fd57e8fd1 refactor: delete FilterPanel.tsx (#1899 #1900) 2026-09-14 20:43:29 +08:00
xiaoxia d325572dd7 refactor: delete EditorDrawers.tsx (#1899 #1900) 2026-09-14 20:43:28 +08:00
xiaoxia 1c1b82ea66 refactor: delete EditorClipList.tsx (#1899 #1900) 2026-09-14 20:43:27 +08:00
xiaoxia 96ccac7fff refactor: delete EditingDrawers.tsx (#1899 #1900) 2026-09-14 20:43:27 +08:00
xiaoxia ac6b001ac3 refactor: delete ClipPropertiesPanel.tsx (#1899 #1900) 2026-09-14 20:43:23 +08:00
xiaoxia 42ef3d27e4 refactor: delete BgmSelector.tsx (#1899 #1900) 2026-09-14 20:43:23 +08:00
xiaoxia eb5aef7890 refactor: delete EditingPlanner.tsx (#1899 #1900) 2026-09-14 20:43:22 +08:00
xiaoxia 09f1189d65 refactor: delete EditingPlanner.css (#1899 #1900) 2026-09-14 20:43:21 +08:00
xiaoxia 937f751f68 Merge pull request 'fix(backend): #1901 统一title_config字段名修复渲染不一致' (#1909) from fix/1901-title-config-unify into develop
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Check push changed paths (push) Successful in 2s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 21s
CI/CD Pipeline / Build Staging API Image (push) Successful in 26s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 27s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m42s
CI/CD Pipeline / Integration Tests (push) Successful in 8m40s
CI/CD Pipeline / Validate - Style (push) Successful in 9m25s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 11m25s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 10m55s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m43s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m48s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m50s
CI/CD Pipeline / Unit Tests (push) Successful in 14m57s
CI/CD Pipeline / Validate - Security (push) Successful in 29m26s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Failing after 42h36m49s
CI/CD Pipeline / Build Production Web Image (push) Failing after 42h36m54s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 43h5m49s
CI/CD Pipeline / PR Build API Image (push) Failing after 43h6m25s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 43h5m56s
CI/CD Pipeline / PR Build Web Image (push) Failing after 43h5m56s
CI/CD Pipeline / Build Production API Image (push) Failing after 42h36m25s
CI/CD Pipeline / CI Gate (push) Failing after 42h36m25s
CI/CD Pipeline / Frontend Lint (push) Failing after 43h5m51s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 43h5m57s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 42h36m25s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 43h5m20s
CI/CD Pipeline / Canary Release to Production (push) Failing after 42h36m20s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 43h5m20s
2026-09-14 20:17:19 +08:00
xiaoxia afcfcf45ee fix(frontend): #1896 删除侵权华康字体修复PreviewPlayer字体映射 (#1908)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 19s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 13s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 14s
CI/CD Pipeline / Build Staging API Image (push) Successful in 46s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m39s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m19s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m50s
CI/CD Pipeline / Validate - Style (push) Has been cancelled
CI/CD Pipeline / Validate - Security (push) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 43h10m14s
CI/CD Pipeline / PR Build Web Image (push) Failing after 43h11m24s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 43h10m56s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 43h9m45s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 43h10m55s
CI/CD Pipeline / PR Build API Image (push) Failing after 43h10m55s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 43h9m45s
CI/CD Pipeline / Frontend Lint (push) Failing after 43h10m50s
fix(frontend): #1896 删除侵权华康字体修复PreviewPlayer字体映射

问题:
1. "华康俪金黑"@font-face字体文件缺失(404)且有版权风险
2. PreviewPlayer直接用中文font_preset/font作为fontFamily,浏览器不识别

修改:
- 删除"华康俪金黑"字体选项(FONT_OPTIONS+FONT_FAMILY_MAP)和@font-face声明
- 统一 editing-planner/constants/clipProperties.ts 和 subtitleStyle.ts 从 generate/constants 重新导出 FONT_OPTIONS,避免多处维护
- PreviewPlayer.tsx 标题/字幕 fontFamily 通过 getFontFamily() 映射为浏览器可识别字体栈
- FrontendPreviewPlayer.tsx 标题 fontFamily 同样通过 getFontFamily() 映射
- TitleStylePanel/PanelTitleConfig/SubtitleStylePanel 等组件因复用统一常量自动生效
- 最终保留5个字体:思源黑体、思源宋体、苹方、微软雅黑、楷体
- PingFang 别名保留在 FONT_FAMILY_MAP 中以兼容旧数据,但不在 UI 选项中显示

Refs #1896
2026-09-14 20:12:21 +08:00
CI Bot ffb40038d3 style: auto-format with black + isort + ruff + prettier [skip ci-format-check]
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 0s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 8s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 8s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m1s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m45s
AI Code Review / AI Code Review (pull_request) Successful in 6m32s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 7m28s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 8m3s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 8m21s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 9m39s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m32s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 25m33s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 13m46s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 14m59s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 43h19m12s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 43h43m59s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 43h44m13s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 43h43m26s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 43h43m30s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 43h43m36s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 43h43m36s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 43h43m36s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 43h44m21s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 43h18m44s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 43h43m2s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 43h18m44s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 43h18m43s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 43h18m44s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 43h43m2s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 43h43m52s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 43h43m2s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 43h43m30s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 43h43m52s
2026-09-14 11:38:53 +00:00
saas-backend-agent 30094bc591 fix(backend): #1901 统一title字段名修复标题渲染不一致
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m20s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m7s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m41s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m32s
AI Code Review / AI Code Review (pull_request) Successful in 6m31s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 6m50s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 8m12s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 8m19s
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 43h50m31s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 43h50m37s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 43h51m57s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 43h50m21s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 43h49m39s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 43h49m59s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 43h50m2s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 43h50m3s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 43h50m8s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 43h50m8s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 43h49m39s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 43h49m39s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 43h51m28s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 43h52m50s
问题:plan.config中标题配置字段名双轨不一致——Worker sync_configs_to_plan 写 plan.config['title'](归一化 size/font/color),API writeback_edit_plan_config 写 plan.config['title_config'](不归一化),三个渲染引擎读取不一致:
- video_compose_service(FFmpeg drawtext):已读 title+fallback title_config 
- unified_render_service(ASS字幕):只 title 无 fallback 
- subtitle_render_engine(字幕渲染):只读 title_config 

修复:
- packages/application/generation_common.py writeback_edit_plan_config:统一写 plan.config['title'](而非 title_config),并做字段名归一化 font_size→size/font_preset→font/font_color→color,与 Worker sync_configs_to_plan 行为一致
- apps/worker/video_processing/subtitle_render_engine.py:_resolve_title_config 先读 plan.config['title'],fallback plan.config['title_config'](兼容存量老数据)
- apps/worker/video_processing/unified_render_service.py:同样先读 'title',text 为空时 fallback 'title_config'
- apps/api/app/api/routes/generation_cover.py:封面E2标题配置读取加 title_config fallback
- 单测适配:test_generation_common.py/test_writeback_edit_plan_config.py 断言改 'title' 并验证归一化
- 所有渲染路径(智能剪辑/AI数字人/封面E2)统一读 'title',老数据通过 fallback 继续渲染
2026-09-14 19:27:29 +08:00
xiaoxia 939a49d1d4 fix(frontend): #1897 声音克隆页增加情绪语速设置 (#1906)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 11s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 16s
CI/CD Pipeline / Build Staging API Image (push) Successful in 16s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 19s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 34s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 2m44s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m32s
CI/CD Pipeline / Integration Tests (push) Successful in 2m59s
CI/CD Pipeline / Validate - Style (push) Successful in 3m31s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 2m31s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 4m45s
CI/CD Pipeline / Validate - Security (push) Successful in 6m12s
CI/CD Pipeline / Unit Tests (push) Successful in 8m16s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 37m7s
CI/CD Pipeline / CI Gate (push) Failing after 44h4m26s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 44h12m10s
CI/CD Pipeline / Frontend Lint (push) Failing after 44h12m40s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 44h12m15s
CI/CD Pipeline / PR Build Web Image (push) Failing after 44h12m16s
CI/CD Pipeline / PR Build API Image (push) Failing after 44h12m16s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 44h3m57s
CI/CD Pipeline / Build Production API Image (push) Failing after 44h3m57s
CI/CD Pipeline / Canary Release to Production (push) Failing after 43h33m50s
CI/CD Pipeline / Build Production Web Image (push) Failing after 44h3m57s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 44h11m41s
CI/CD Pipeline / Deploy Production (push) Failing after 44h3m55s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 44h11m41s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 44h12m17s
fix(frontend): #1897 声音克隆页增加情绪语速设置

- getVoiceClonePreview 增加 speed/emotion 参数透传
- 新增 VoiceClonePreviewPanel:音色选择/试听文本/SpeedControl/EmotionControl
- 复用 TTS SpeedControl 和 EmotionControl 组件
- 声音克隆页在无加载且有就绪音色时展示试听面板

Refs #1897
2026-09-14 19:10:59 +08:00
xiaoxia 8ad44ad045 fix(backend): #1897 声音克隆preview接口支持情绪语速参数 (#1907)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Check push changed paths (push) Successful in 16s
CI/CD Pipeline / Build Staging API Image (push) Successful in 39s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 38s
CI/CD Pipeline / Integration Tests (push) Successful in 2m2s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 1m46s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 2m39s
CI/CD Pipeline / Validate - Style (push) Successful in 3m0s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m16s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m26s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m30s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m53s
CI/CD Pipeline / Validate - Security (push) Successful in 6m55s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m30s
CI/CD Pipeline / Unit Tests (push) Successful in 8m12s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Failing after 44h18m37s
CI/CD Pipeline / Build Production API Image (push) Failing after 44h18m37s
CI/CD Pipeline / Frontend Lint (push) Failing after 44h26m51s
CI/CD Pipeline / PR Build API Image (push) Failing after 44h26m52s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 44h26m23s
CI/CD Pipeline / PR Build Web Image (push) Failing after 44h26m23s
CI/CD Pipeline / Deploy Production (push) Failing after 44h18m4s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 44h18m8s
CI/CD Pipeline / Build Production Web Image (push) Failing after 44h18m8s
CI/CD Pipeline / Canary Release to Production (push) Failing after 44h18m4s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 44h24m15s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 44h24m15s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 44h26m25s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 44h24m15s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 18:56:52 +08:00
xiaoxia 26f3abab72 fix(backend): #1892 画面插入按句号分句 (#1905)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 18s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 12s
CI/CD Pipeline / Build Staging API Image (push) Successful in 1m17s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 1m23s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m22s
CI/CD Pipeline / Validate - Style (push) Successful in 3m41s
CI/CD Pipeline / Integration Tests (push) Successful in 4m1s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 4m16s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m27s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m36s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m42s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m24s
CI/CD Pipeline / Validate - Security (push) Successful in 8m48s
CI/CD Pipeline / Unit Tests (push) Successful in 9m33s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Failing after 44h58m30s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 45h6m21s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 45h8m6s
CI/CD Pipeline / PR Build Web Image (push) Failing after 45h7m38s
CI/CD Pipeline / PR Build API Image (push) Failing after 45h7m39s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 45h5m53s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 45h5m53s
CI/CD Pipeline / CI Gate (push) Failing after 44h58m2s
CI/CD Pipeline / Deploy Production (push) Failing after 44h57m58s
CI/CD Pipeline / Build Production API Image (push) Failing after 44h58m2s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 45h7m40s
CI/CD Pipeline / Canary Release to Production (push) Failing after 44h57m58s
CI/CD Pipeline / Build Production Web Image (push) Failing after 44h58m2s
CI/CD Pipeline / Frontend Lint (push) Failing after 45h7m34s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 18:15:38 +08:00
xiaoxia 4f377d4fd3 fix(frontend): #1898 配音库AI配音增加情绪语言选择 (#1904)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Check push changed paths (push) Successful in 12s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 16s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 27s
CI/CD Pipeline / Build Staging API Image (push) Successful in 1m30s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m6s
CI/CD Pipeline / Integration Tests (push) Successful in 4m1s
CI/CD Pipeline / Validate - Style (push) Successful in 4m56s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 5m3s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m43s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m53s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m9s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m12s
CI/CD Pipeline / Unit Tests (push) Successful in 10m22s
CI/CD Pipeline / Validate - Security (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 45h15m46s
CI/CD Pipeline / PR Build Web Image (push) Failing after 45h17m31s
CI/CD Pipeline / PR Build API Image (push) Failing after 45h17m8s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 45h18m15s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 45h15m18s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 45h15m19s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 45h16m58s
CI/CD Pipeline / Frontend Lint (push) Failing after 45h17m41s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 18:05:01 +08:00
xiaoxia 35b18e5e16 docs: #1895 会员+积分替代订阅制方案设计 (#1903)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 3s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 15s
CI/CD Pipeline / Build Staging API Image (push) Successful in 15s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 10s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 48s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m38s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m45s
CI/CD Pipeline / Integration Tests (push) Successful in 4m20s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m27s
CI/CD Pipeline / Validate - Style (push) Successful in 4m57s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m28s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 6m24s
CI/CD Pipeline / Unit Tests (push) Successful in 10m43s
CI/CD Pipeline / Validate - Security (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 45h29m11s
CI/CD Pipeline / PR Build Web Image (push) Failing after 45h29m47s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 45h29m19s
CI/CD Pipeline / PR Build API Image (push) Failing after 45h29m19s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 45h28m43s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 45h28m43s
CI/CD Pipeline / Frontend Lint (push) Failing after 45h29m14s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 45h29m19s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 17:53:57 +08:00
xiaoxia e9dd33e2f6 Merge pull request 'fix(frontend): #1892 画面插入按句号分句' (#1902) from fix/1892-sentence-split-no-comma into develop
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Check push changed paths (push) Successful in 4s
CI/CD Pipeline / Build Staging API Image (push) Successful in 28s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 54s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m15s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m17s
CI/CD Pipeline / Integration Tests (push) Successful in 4m16s
CI/CD Pipeline / Validate - Style (push) Successful in 5m14s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 58s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m43s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m6s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m11s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 7m38s
CI/CD Pipeline / Validate - Security (push) Successful in 10m41s
CI/CD Pipeline / Unit Tests (push) Successful in 12m47s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Failing after 45h30m18s
CI/CD Pipeline / Build Production Web Image (push) Failing after 45h30m20s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 45h39m1s
CI/CD Pipeline / PR Build Web Image (push) Failing after 45h41m26s
CI/CD Pipeline / PR Build API Image (push) Failing after 45h40m59s
CI/CD Pipeline / Build Production API Image (push) Failing after 45h29m52s
CI/CD Pipeline / CI Gate (push) Failing after 45h29m51s
CI/CD Pipeline / Canary Release to Production (push) Failing after 45h29m50s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 45h29m51s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 45h40m58s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 45h43m15s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 45h38m33s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 45h38m33s
CI/CD Pipeline / Frontend Lint (push) Failing after 45h41m4s
2026-09-14 17:39:59 +08:00
saas-frontend-agent 7f18b1b35a fix(frontend): #1892 画面插入按句号分句,去掉逗号分句
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 44s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m12s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m17s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m29s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m36s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 3m47s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 4m35s
AI Code Review / AI Code Review (pull_request) Successful in 6m20s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 10m31s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 12m34s
CI/CD Pipeline / CI Gate (pull_request) Successful in 3s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 10s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 37s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 45h45m11s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 45h57m44s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 45h57m46s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 45h57m46s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 45h57m15s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 45h57m16s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 45h57m17s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 45h57m18s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 45h57m19s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 45h57m19s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 45h44m45s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 45h57m22s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 45h44m43s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 45h44m45s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 45h44m45s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 45h57m10s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 45h57m11s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 45h57m13s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 45h57m19s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 46h32m35s
2026-09-14 17:25:36 +08:00
xiaoxia 880a7106d4 fix(ci): staging deploy SSH host to internal IP for Docker runner (#1891)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 3s
CI/CD Pipeline / Check push changed paths (push) Successful in 5s
CI/CD Pipeline / Build Staging API Image (push) Successful in 17s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 23s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m36s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m9s
CI/CD Pipeline / Integration Tests (push) Successful in 3m36s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m33s
CI/CD Pipeline / Validate - Style (push) Successful in 4m23s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 5m9s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m36s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m37s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m19s
CI/CD Pipeline / Unit Tests (push) Successful in 9m23s
CI/CD Pipeline / Validate - Security (push) Successful in 12m33s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Failing after 47h52m31s
CI/CD Pipeline / Build Production API Image (push) Failing after 47h52m33s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 48h2m27s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 48h4m44s
CI/CD Pipeline / PR Build Web Image (push) Failing after 48h4m44s
CI/CD Pipeline / PR Build API Image (push) Failing after 48h4m44s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 48h2m0s
CI/CD Pipeline / CI Gate (push) Failing after 47h52m5s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 47h52m5s
CI/CD Pipeline / Canary Release to Production (push) Failing after 47h52m4s
CI/CD Pipeline / Build Production Web Image (push) Failing after 47h52m6s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 48h2m0s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 48h4m46s
CI/CD Pipeline / Frontend Lint (push) Failing after 48h4m40s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 15:18:31 +08:00
xiaoxia d478a9ffbc fix(ci): push :dev tag for watchtower + fix staging deploy host to 116 (#1889)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Check push changed paths (push) Successful in 6s
CI/CD Pipeline / Build Staging API Image (push) Successful in 28s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 27s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 51s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 56s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 4m23s
CI/CD Pipeline / Integration Tests (push) Successful in 4m31s
CI/CD Pipeline / Validate - Style (push) Successful in 4m46s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 5m23s
CI/CD Pipeline / Unit Tests (push) Successful in 9m50s
CI/CD Pipeline / Validate - Security (push) Successful in 13m31s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Failing after 48h27m19s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 48h38m53s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 48h40m31s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 48h39m25s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 48h39m25s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 48h40m30s
CI/CD Pipeline / PR Build Web Image (push) Failing after 48h40m30s
CI/CD Pipeline / Build Production Web Image (push) Failing after 48h26m55s
CI/CD Pipeline / Build Production API Image (push) Failing after 48h26m55s
CI/CD Pipeline / CI Gate (push) Failing after 48h26m54s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 48h38m25s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 48h26m54s
CI/CD Pipeline / Canary Release to Production (push) Failing after 48h26m52s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 48h38m25s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 48h39m24s
CI/CD Pipeline / PR Build API Image (push) Failing after 48h40m30s
CI/CD Pipeline / Frontend Lint (push) Failing after 48h40m25s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 14:42:46 +08:00
xiaoxia 628fc6c4e6 fix(ci): push :dev tag to registry for watchtower auto-update (#1888)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 9s
CI/CD Pipeline / Build Staging API Image (push) Successful in 19s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 23s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 50s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 56s
CI/CD Pipeline / Integration Tests (push) Successful in 3m30s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 3m44s
CI/CD Pipeline / Validate - Style (push) Successful in 4m7s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m4s
CI/CD Pipeline / Validate - Security (push) Successful in 10m34s
CI/CD Pipeline / Unit Tests (push) Successful in 12m4s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Failing after 48h45m22s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 48h55m35s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 48h57m39s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 48h56m9s
CI/CD Pipeline / PR Build Web Image (push) Failing after 48h57m12s
CI/CD Pipeline / PR Build API Image (push) Failing after 48h57m13s
CI/CD Pipeline / Deploy Production (push) Failing after 48h44m51s
CI/CD Pipeline / Build Production Web Image (push) Failing after 48h44m55s
CI/CD Pipeline / CI Gate (push) Failing after 48h44m55s
CI/CD Pipeline / Canary Release to Production (push) Failing after 48h44m51s
CI/CD Pipeline / Build Production API Image (push) Failing after 48h44m55s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 48h55m8s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 48h55m8s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 48h56m9s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 48h56m9s
CI/CD Pipeline / Frontend Lint (push) Failing after 48h57m7s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 48h57m14s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 14:26:05 +08:00
xiaoxia f651422582 fix(deploy): 修复生产部署脚本502——worker/web容器启动失败 (#1887)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 11s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 39s
CI/CD Pipeline / Integration Tests (push) Successful in 3m18s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 3m21s
CI/CD Pipeline / Validate - Style (push) Successful in 3m54s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m16s
CI/CD Pipeline / Validate - Security (push) Successful in 8m17s
CI/CD Pipeline / Unit Tests (push) Successful in 8m42s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 14m38s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 18m14s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 5m19s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m26s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m58s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m1s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 51h35m15s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 51h44m59s
CI/CD Pipeline / PR Build Web Image (push) Failing after 51h53m46s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 51h53m21s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 51h53m20s
CI/CD Pipeline / PR Build API Image (push) Failing after 51h53m20s
CI/CD Pipeline / Deploy Production (push) Failing after 51h44m31s
CI/CD Pipeline / Build Production Web Image (push) Failing after 51h44m33s
CI/CD Pipeline / Build Production API Image (push) Failing after 51h44m33s
CI/CD Pipeline / CI Gate (push) Failing after 51h44m33s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 51h34m50s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 51h34m50s
CI/CD Pipeline / Canary Release to Production (push) Failing after 51h26m28s
CI/CD Pipeline / Frontend Lint (push) Failing after 51h53m16s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 11:29:58 +08:00
xiaoxia 14580f536a refactor(backend): 智能剪辑后端公共逻辑抽取+解耦 (#1884)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 5s
CI/CD Pipeline / Check push changed paths (push) Successful in 13s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 19s
CI/CD Pipeline / Build Staging API Image (push) Successful in 24s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 26s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 59s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m27s
CI/CD Pipeline / Integration Tests (push) Successful in 3m38s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 3m45s
CI/CD Pipeline / Validate - Style (push) Successful in 4m14s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m42s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m14s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 4m58s
CI/CD Pipeline / Validate - Security (push) Successful in 8m1s
CI/CD Pipeline / Unit Tests (push) Successful in 9m0s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 1m32s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m6s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m6s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m27s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m50s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 2m52s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m36s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 5m31s
AI Code Review / AI Code Review (pull_request) Successful in 6m21s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 7m48s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 3s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 51s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m4s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 53h34m26s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 53h42m14s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 53h42m15s
CI/CD Pipeline / Deploy Production (push) Failing after 59h59m30s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 60h8m2s
CI/CD Pipeline / PR Build Web Image (push) Failing after 60h8m44s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 53h41m47s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 53h41m49s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 53h41m50s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 53h41m50s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 53h41m50s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 53h41m55s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 53h34m0s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 53h34m0s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 53h33m56s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 53h33m56s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 53h41m43s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 53h41m43s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 53h41m43s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 53h41m49s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 53h41m47s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 60h8m17s
CI/CD Pipeline / PR Build API Image (push) Failing after 60h8m18s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 60h7m36s
CI/CD Pipeline / CI Gate (push) Failing after 59h59m9s
CI/CD Pipeline / Build Production Web Image (push) Failing after 59h59m9s
CI/CD Pipeline / Canary Release to Production (push) Failing after 59h59m4s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 59h59m9s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 60h7m36s
CI/CD Pipeline / Frontend Lint (push) Failing after 60h8m10s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 60h8m20s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 54h17m5s
CI/CD Pipeline / Build Production API Image (push) Failing after 60h34m30s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 03:15:00 +08:00
xiaoxia 6ff8d48c15 refactor(web): 智能剪辑 & AI数字人 前端代码审查与重构 (#1883)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 4s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m23s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m39s
CI/CD Pipeline / Build Staging API Image (push) Successful in 2m45s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m9s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m10s
CI/CD Pipeline / Validate - Style (push) Successful in 4m33s
CI/CD Pipeline / Integration Tests (push) Successful in 5m1s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m42s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m32s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 6m47s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m10s
CI/CD Pipeline / Validate - Security (push) Successful in 10m36s
CI/CD Pipeline / Unit Tests (push) Successful in 10m43s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Failing after 60h28m16s
CI/CD Pipeline / Frontend Lint (push) Failing after 60h39m0s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 60h35m47s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 60h38m38s
CI/CD Pipeline / PR Build API Image (push) Failing after 60h38m38s
CI/CD Pipeline / Build Production Web Image (push) Failing after 60h27m50s
CI/CD Pipeline / CI Gate (push) Failing after 60h27m50s
CI/CD Pipeline / Canary Release to Production (push) Failing after 60h27m46s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 60h35m46s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 60h35m46s
CI/CD Pipeline / PR Build Web Image (push) Failing after 60h38m38s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 60h38m40s
CI/CD Pipeline / Deploy Production (push) Failing after 60h27m46s
CI/CD Pipeline / Build Production API Image (push) Failing after 60h27m50s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 02:44:41 +08:00
LingYing Agent da1d55ef4e chore: 触发staging部署(批量变体P0修复)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 0s
CI/CD Pipeline / Check push changed paths (push) Successful in 3s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 21s
CI/CD Pipeline / Build Staging API Image (push) Successful in 21s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 24s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 57s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m25s
CI/CD Pipeline / Integration Tests (push) Successful in 3m30s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 3m41s
CI/CD Pipeline / Validate - Style (push) Successful in 4m0s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 4m7s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m48s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m34s
CI/CD Pipeline / Validate - Security (push) Successful in 8m42s
CI/CD Pipeline / Unit Tests (push) Successful in 10m0s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Failing after 60h57m49s
CI/CD Pipeline / Build Production API Image (push) Failing after 60h57m54s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 61h7m30s
CI/CD Pipeline / Frontend Lint (push) Failing after 61h7m56s
CI/CD Pipeline / Build Production Web Image (push) Failing after 60h57m28s
CI/CD Pipeline / CI Gate (push) Failing after 60h57m28s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 61h7m4s
CI/CD Pipeline / Canary Release to Production (push) Failing after 60h57m23s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 60h57m28s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 61h7m4s
CI/CD Pipeline / PR Build Web Image (push) Failing after 61h7m30s
CI/CD Pipeline / PR Build API Image (push) Failing after 61h7m30s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 61h7m30s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 61h7m35s
2026-09-14 02:15:43 +08:00
xiaoxia 00f8c0b566 fix(web): 修复批量变体预览3个P0 bug (#1881)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 5s
CI/CD Pipeline / Build Staging API Image (push) Successful in 21s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 22s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 45s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m4s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m37s
CI/CD Pipeline / Integration Tests (push) Successful in 3m57s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m3s
CI/CD Pipeline / Validate - Style (push) Successful in 4m41s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m21s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m53s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 7m7s
CI/CD Pipeline / Validate - Security (push) Successful in 9m9s
CI/CD Pipeline / Unit Tests (push) Successful in 11m6s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Failing after 61h22m13s
CI/CD Pipeline / Build Production Web Image (push) Failing after 61h22m18s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 61h32m37s
CI/CD Pipeline / PR Build API Image (push) Failing after 61h33m0s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 61h32m11s
CI/CD Pipeline / CI Gate (push) Failing after 61h21m52s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 61h32m11s
CI/CD Pipeline / PR Build Web Image (push) Failing after 61h33m0s
CI/CD Pipeline / Canary Release to Production (push) Failing after 61h21m47s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 61h21m52s
CI/CD Pipeline / Build Production API Image (push) Failing after 61h21m52s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 61h32m59s
CI/CD Pipeline / Frontend Lint (push) Failing after 61h33m0s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 61h33m5s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 01:50:15 +08:00
xiaoxia 1f01d6df25 fix(worker): 修复批量变体3个P0 bug (#1882)
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 3s
CI/CD Pipeline / Check push changed paths (push) Successful in 8s
CI/CD Pipeline / Build Staging API Image (push) Successful in 2m13s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m29s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m14s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m16s
CI/CD Pipeline / Integration Tests (push) Successful in 3m43s
CI/CD Pipeline / Validate - Style (push) Successful in 4m15s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m16s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m21s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m34s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m31s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m37s
CI/CD Pipeline / Validate - Security (push) Successful in 10m15s
CI/CD Pipeline / Unit Tests (push) Successful in 11m28s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 61h50m19s
CI/CD Pipeline / PR Build Web Image (push) Failing after 61h53m42s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 61h53m17s
CI/CD Pipeline / PR Build API Image (push) Failing after 61h53m17s
CI/CD Pipeline / Build Production Web Image (push) Failing after 61h41m43s
CI/CD Pipeline / Build Production API Image (push) Failing after 61h41m43s
CI/CD Pipeline / Deploy Production (push) Failing after 61h41m37s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 61h41m42s
CI/CD Pipeline / Canary Release to Production (push) Failing after 61h41m36s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 61h49m53s
CI/CD Pipeline / Frontend Lint (push) Failing after 61h53m12s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 61h53m23s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 61h49m53s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-14 01:29:57 +08:00
xiaoxia 5b931438e0 Merge pull request 'fix(worker): 修复像素扰动color_balance滤镜名错误导致模板编辑器FFmpeg exit=8' (#1880) from fix/colorbalance-filter-name-0913 into develop
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 3s
CI/CD Pipeline / Build Staging API Image (push) Successful in 22s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 23s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m11s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m6s
CI/CD Pipeline / Integration Tests (push) Successful in 3m2s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 3m26s
CI/CD Pipeline / Validate - Style (push) Successful in 4m0s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m44s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m47s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 4m19s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m27s
CI/CD Pipeline / Validate - Security (push) Successful in 7m57s
CI/CD Pipeline / Unit Tests (push) Successful in 8m35s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 0s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
AI Code Review / AI Code Review (pull_request) Successful in 24s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Failing after 63h7m26s
CI/CD Pipeline / Build Production Web Image (push) Failing after 63h7m30s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 63h14m49s
CI/CD Pipeline / Frontend Lint (push) Failing after 63h16m6s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 63h15m45s
CI/CD Pipeline / PR Build Web Image (push) Failing after 63h15m39s
CI/CD Pipeline / PR Build API Image (push) Failing after 63h15m39s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 63h14m24s
CI/CD Pipeline / CI Gate (push) Failing after 63h7m4s
CI/CD Pipeline / Canary Release to Production (push) Failing after 63h7m1s
CI/CD Pipeline / Build Production API Image (push) Failing after 63h7m5s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 63h7m4s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 63h14m24s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 63h15m39s
2026-09-14 00:07:35 +08:00
灵应 b9acf8c86f fix(worker): 修复像素扰动color_balance滤镜名错误导致ffmpeg exit=8
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 5s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 21s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 32s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Failing after 13m45s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 6m21s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 6m49s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 7m5s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 8m39s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 15m14s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 6s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m4s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 63h18m24s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 63h47m15s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 63h47m17s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 63h47m19s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 63h18m0s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 63h46m55s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 63h18m0s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 63h46m51s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 63h46m51s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 63h46m53s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 63h46m54s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 63h46m54s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 63h47m4s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 63h46m49s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 63h46m49s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 63h17m59s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 63h18m0s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 63h33m14s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 63h33m15s
根因:像素扰动功能(bb98620)在ffmpeg滤镜链中使用了color_balance滤镜名,
但ffmpeg的实际滤镜名是colorbalance(无下划线),导致滤镜解析失败,
报错'No such filter: color_balance',ffmpeg退出码8。

影响:任何启用了像素扰动的变体渲染(默认批量变体会随机启用)都会失败,
表现为进度卡在56%,error_message只显示ffmpeg版本banner。

修复:将ffmpeg滤镜名从color_balance=改为colorbalance=。
内部config key保持color_balance不变(避免兼容问题)。
2026-09-13 23:36:02 +08:00
xiaoxia 6f4a95e00f Merge pull request 'fix(worker): 改进FFmpeg错误信息可见性便于exit=8排查' (#1879) from fix/ffmpeg-error-visibility-0913 into develop
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 4s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m31s
CI/CD Pipeline / Build Staging API Image (push) Successful in 1m50s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m12s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m17s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 4m39s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m21s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m57s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m59s
CI/CD Pipeline / Validate - Style (push) Successful in 7m13s
CI/CD Pipeline / Integration Tests (push) Successful in 7m45s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 8m31s
CI/CD Pipeline / Unit Tests (push) Successful in 12m18s
CI/CD Pipeline / Validate - Security (push) Successful in 21m39s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Failing after 63h31m51s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 63h51m15s
CI/CD Pipeline / PR Build API Image (push) Failing after 63h53m29s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 63h31m26s
CI/CD Pipeline / Build Production API Image (push) Failing after 63h31m27s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 63h50m50s
CI/CD Pipeline / Deploy Production (push) Failing after 63h31m22s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 63h50m50s
CI/CD Pipeline / PR Build Web Image (push) Failing after 63h53m4s
CI/CD Pipeline / Canary Release to Production (push) Failing after 63h31m22s
CI/CD Pipeline / Build Production Web Image (push) Failing after 63h31m27s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 63h53m4s
CI/CD Pipeline / Frontend Lint (push) Failing after 63h53m5s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 63h53m10s
2026-09-13 23:30:09 +08:00
278 changed files with 3826 additions and 24951 deletions
+17 -5
View File
@@ -1022,12 +1022,22 @@ jobs:
BRANCH_TAG="${IMAGE_FULL}:${GITHUB_REF_NAME}"
CACHE_REF="${REGISTRY}/${{ matrix.cache_name }}:${GITHUB_REF_NAME}"
# develop 分支额外推送 :dev tagWatchtower 监听的滚动更新 tag
if [ "${GITHUB_REF_NAME}" = "develop" ]; then
DEV_TAG="${IMAGE_FULL}:dev"
EXTRA_TAGS="$BRANCH_TAG $DEV_TAG"
PUSHED_TAGS_SUMMARY="${BRANCH_TAG} + ${DEV_TAG}"
else
EXTRA_TAGS="$BRANCH_TAG"
PUSHED_TAGS_SUMMARY="${BRANCH_TAG}"
fi
EXTRA_BUILD_ARGS="APP_VERSION=\"${GITHUB_SHA}\""
NO_CACHE_FLAG=""
for i in 1 2 3; do
echo "=== Docker build 尝试 $i/3 (${{ matrix.service_display }}) ==="
if EXTRA_TAGS="$BRANCH_TAG" bash scripts/ci/docker_build_push.sh $NO_CACHE_FLAG ${{ matrix.dockerfile }} "${IMAGE_TAG}" "${CACHE_REF}" $EXTRA_BUILD_ARGS; then
if EXTRA_TAGS="$EXTRA_TAGS" bash scripts/ci/docker_build_push.sh $NO_CACHE_FLAG ${{ matrix.dockerfile }} "${IMAGE_TAG}" "${CACHE_REF}" $EXTRA_BUILD_ARGS; then
echo "✅ Docker build 成功"
break
fi
@@ -1040,7 +1050,7 @@ jobs:
fi
done
echo "${{ matrix.service_display }} image pushed: ${IMAGE_TAG} (+ ${BRANCH_TAG})"
echo "${{ matrix.service_display }} image pushed: ${IMAGE_TAG} (+ ${PUSHED_TAGS_SUMMARY})"
- name: Job duration summary
if: always()
@@ -1244,9 +1254,11 @@ jobs:
ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}
run: |
set -eux
staging_host="${STAGING_SSH_HOST:-47.98.113.167}"
# CI runner (act_runner) 部署在 116 staging 本机(116.62.226.203 公网 22 未开放),
# 默认走 127.0.0.1:22 本机 SSH,避免跨机网络依赖;可通过 secrets 覆盖。
staging_host="${STAGING_SSH_HOST:-127.0.0.1}"
staging_user="${STAGING_SSH_USER:-root}"
staging_port="${STAGING_SSH_PORT:-22222}"
staging_port="${STAGING_SSH_PORT:-22}"
echo "Host: $staging_host"
echo "Port: $staging_port"
@@ -1470,7 +1482,6 @@ jobs:
- validate-security
- validate-python
- unit-tests
- frontend-lint
- frontend-unit-test
if: github.event_name == 'push' && github.ref_name == 'main' && !failure() && !cancelled()
strategy:
@@ -2124,3 +2135,4 @@ jobs:
START_TIME=""
[ -f /tmp/ci_job_start_time ] && START_TIME=$(cat /tmp/ci_job_start_time)
curl -sfH "Authorization: token ${GITHUB_TOKEN:-$GITEA_TOKEN}" -o /tmp/_ci_trace.py "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/ci_trace_report.py?ref=${GITHUB_SHA}" 2>/dev/null && python3 /tmp/_ci_trace.py --service xiaoxia-saas-ci --status $STATUS --start-time "$START_TIME" || true
# CI retry trigger
+1
View File
@@ -0,0 +1 @@
retrigger3
+1
View File
@@ -263,3 +263,4 @@ pytest --cov=packages --cov-report=html
---
**License**: MIT
<!-- CI trigger: 1788229339 -->
@@ -766,7 +766,12 @@ def generate_cover(
storage_svc = get_shared_storage_service()
mk_client = get_mediakit_client()
# 从 plan.config 读取完整标题样式,E2 从源素材抽帧时叠加(源素材本身无标题)
# #1901 统一读 "title",兼容老数据 "title_config"
_e2_title_cfg = (plan.config or {}).get("title", {}) or {}
if not isinstance(_e2_title_cfg, dict) or not (_e2_title_cfg.get("text") or "").strip():
_alt = (plan.config or {}).get("title_config", {}) or {}
if isinstance(_alt, dict):
_e2_title_cfg = _alt
if not isinstance(_e2_title_cfg, dict):
_e2_title_cfg = {}
_e2_title_text = (_e2_title_cfg.get("text", "") or "").strip() if _e2_title_cfg.get("enabled", True) else ""
+36 -111
View File
@@ -58,27 +58,10 @@ def _variant_value(values: list[str], index: int, fallback: str = "") -> str:
def _query_voice_durations(db: Session, voice_ids: list[str]) -> list[float]:
"""批量查询配音素材时长(秒),#1749 配音时长分配用。
"""[已下沉] 路由层兼容别名 → app.services.generation_common.query_voice_durations。"""
from app.services.generation_common import query_voice_durations
逐项 try/float 硬化:MagicMock/异常/缺失 → 0.0(无配音不分配,不阻断)。
"""
ids = [v for v in dict.fromkeys(voice_ids or []) if v]
if not ids:
return []
try:
from packages.adapters.sqlalchemy_impl.models import AssetModel
rows = db.query(AssetModel.id, AssetModel.duration).filter(AssetModel.id.in_(ids)).all()
dur_map: dict[str, float] = {}
for row in rows:
try:
dur_map[row[0]] = float(row[1] or 0.0)
except (TypeError, ValueError):
dur_map[row[0]] = 0.0
return [dur_map.get(v, 0.0) for v in ids]
except Exception:
logger.warning("[生成任务] 配音时长查询失败(按无配音处理,不阻断)", exc_info=True)
return [0.0 for _ in ids]
return query_voice_durations(db, voice_ids)
def _to_generation_task_response(task) -> GenerationTaskResponse:
@@ -184,61 +167,10 @@ def _writeback_edit_plan_config(
title_config: dict | None,
db: Session,
) -> None:
"""任务入队成功后,回写 EditPlan.configgeneration_task_id + title_config。
"""[已下沉] 路由层兼容别名 → app.services.generation_common.writeback_edit_plan_config。"""
from app.services.generation_common import writeback_edit_plan_config
用 merge 方式更新,不整体覆盖 config,避免丢失其他字段。
失败只记日志,不影响任务创建。
"""
if not plan_id:
return
try:
from packages.adapters.sqlalchemy_impl.models import EditPlanModel
plan_model = db.query(EditPlanModel).filter(EditPlanModel.id == plan_id).first()
if plan_model is None:
logger.warning("[生成任务] 回写plan.config失败: plan不存在 plan_id=%s", plan_id)
return
current_config = plan_model.config if isinstance(plan_model.config, dict) else {}
merged = dict(current_config)
merged["generation_task_id"] = task_id
# 检查标题是否发生变化,如果变化则清除 cover 字段强制重新生成封面
if title_config:
old_title_config = merged.get("title_config", {}) or {}
old_title_text = (old_title_config.get("text") or "").strip()
new_title_text = (title_config.get("text") or "").strip()
if old_title_text != new_title_text:
# 标题变化,清除旧封面
if "cover" in merged:
del merged["cover"]
logger.info(
"[生成任务] 标题变化,清除旧封面: plan_id=%s old_title=%s new_title=%s",
plan_id,
old_title_text,
new_title_text,
)
merged["title_config"] = title_config
plan_model.config = merged
db.commit()
logger.info(
"[生成任务] 回写plan.config成功: plan_id=%s task_id=%s keys=%s",
plan_id,
task_id,
list(merged.keys()),
)
except Exception as e:
logger.warning(
"[生成任务] 回写plan.config异常(不影响任务创建): plan_id=%s error=%s",
plan_id,
e,
exc_info=True,
)
try:
db.rollback()
except Exception:
pass
return writeback_edit_plan_config(plan_id, task_id, title_config, db)
def _resolve_project_and_library(
@@ -489,25 +421,14 @@ def create_generation_task(
# 各变体配音时长(查询硬化:异常 → 0.0 不阻断)
voice_durations = _query_voice_durations(db, variant_voices)
# 解析批量源 plan:优先前端传入;否则按 template_id + user 查最新(与单任务兜底同源
batch_source_plan_id = request.source_edit_plan_id
if not batch_source_plan_id and request.template_id:
try:
from packages.adapters.sqlalchemy_impl.models import EditPlanModel
# 解析批量源 plan:优先前端传入;否则按 template_id + user 查最新(公共函数
from app.services.generation_common import resolve_latest_plan_by_template
_latest = (
db.query(EditPlanModel)
.filter(
EditPlanModel.template_id == request.template_id,
EditPlanModel.created_by_user_id == user_id,
)
.order_by(EditPlanModel.created_at.desc())
.first()
)
if _latest:
batch_source_plan_id = _latest.id
except Exception:
logger.warning("[生成任务] 批量源 plan 解析失败", exc_info=True)
batch_source_plan_id = (
request.source_edit_plan_id
or resolve_latest_plan_by_template(db, template_id=request.template_id, user_id=user_id)
or ""
)
if not batch_source_plan_id and not request.variant_plan_ids:
# 无任何可用源 plan:批量变体无从选片,明确报错,严禁静默共用/同源
@@ -552,7 +473,12 @@ def create_generation_task(
) from clone_err
variant_plan_ids.append(_plan0.id)
# 变体 1..N-1 独立选片
# #1855 P0:批次区间避让表,从变体0实际clips构建初始值(公共函数)
from app.services.generation_common import collect_plan_segments as _collect_segments
_batch_segments = _collect_segments(_plan0.id, _plan_svc._clip_repo)
# 变体 1..N-1 独立选片(传入累积batch_segments做素材区间避让)
for task_index in range(1, count):
variant = None
last_err: Exception | None = None
@@ -564,6 +490,7 @@ def create_generation_task(
created_by_user_id=user_id,
name_suffix=f"批量{task_index + 1}",
voice_duration=voice_durations[task_index] if task_index < len(voice_durations) else 0.0,
batch_segments=_batch_segments,
)
break
except ValueError as ve:
@@ -595,7 +522,16 @@ def create_generation_task(
) from last_err
variant_plan_ids.append(variant.id)
# ③ 配音时长分配(回传 plan / clone 变体0 均需幂等分配;reselect 已在选片时分配)
# #1855 P0:把新变体的clips区间追加到batch_segments,供下一变体避让
try:
_new_segs = _collect_segments(variant.id, _plan_svc._clip_repo)
for _aid, _ivs in _new_segs.items():
_batch_segments.setdefault(_aid, []).extend(_ivs)
except Exception:
logger.exception("[生成任务] 变体%d 区间收集失败(不阻断)", task_index)
# ③ 配音时长分配(回传 plan / clone 变体0 均需幂等分配;reselect 已在选片时分配,
# #1855apply_voice_duration_to_plan 已内置幂等判断,重复调用安全)
for _vi, _pid in enumerate(variant_plan_ids):
_vd = voice_durations[_vi] if _vi < len(voice_durations) else 0.0
if _vd > 0:
@@ -616,24 +552,13 @@ def create_generation_task(
)
_single_vd: list[float] = _query_voice_durations(db, _voices)
_single_dur = _single_vd[0] if _single_vd else 0.0
_single_plan = request.source_edit_plan_id
if not _single_plan and request.template_id:
try:
from packages.adapters.sqlalchemy_impl.models import EditPlanModel
from app.services.generation_common import resolve_latest_plan_by_template
_latest = (
db.query(EditPlanModel)
.filter(
EditPlanModel.template_id == request.template_id,
EditPlanModel.created_by_user_id == user_id,
)
.order_by(EditPlanModel.created_at.desc())
.first()
)
if _latest:
_single_plan = _latest.id
except Exception:
logger.warning("[生成任务] 单任务源 plan 解析失败", exc_info=True)
_single_plan = (
request.source_edit_plan_id
or resolve_latest_plan_by_template(db, template_id=request.template_id, user_id=user_id)
or ""
)
if _single_dur > 0 and _single_plan:
from app.services.edit_plan_service import EditPlanService
@@ -90,25 +90,12 @@ def create_variant_plans(
except VariantVoiceError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
# 解析源 plan:显式传入优先;否则按 template_id + user 查最新
# 解析源 plan:显式传入优先;否则按 template_id + user 查最新(公共函数)
from app.services.generation_common import resolve_latest_plan_by_template
source_plan_id = request.source_edit_plan_id.strip()
if not source_plan_id and request.template_id.strip():
try:
from packages.adapters.sqlalchemy_impl.models import EditPlanModel
_latest = (
db.query(EditPlanModel)
.filter(
EditPlanModel.template_id == request.template_id.strip(),
EditPlanModel.created_by_user_id == user_id,
)
.order_by(EditPlanModel.created_at.desc())
.first()
)
if _latest:
source_plan_id = _latest.id
except Exception:
logger.exception("[variant-plans] 源 plan 解析失败")
source_plan_id = resolve_latest_plan_by_template(db, template_id=request.template_id, user_id=user_id) or ""
if not source_plan_id:
raise HTTPException(
+20 -6
View File
@@ -287,10 +287,15 @@ def retry_voice_clone(
return _to_response(profile)
_ALLOWED_PREVIEW_EMOTIONS = {"", "natural", "excited", "calm", "friendly"}
@router.get("/{clone_id}/preview", response_model=VoiceClonePreviewResponse)
def get_voice_clone_preview(
clone_id: str,
text: str = Query("", description="自定义试听文本,为空则使用默认示例"),
speed: float = Query(1.0, ge=0.5, le=2.0, description="语速,0.5-2.0,默认 1.0"),
emotion: str = Query("", description="情绪:natural/excited/calm/friendly,空字符串为默认自然"),
authenticated_user: AuthenticatedUser = Depends(get_current_user),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
cosyvoice: CosyVoiceService = Depends(get_cosyvoice_service),
@@ -298,11 +303,17 @@ def get_voice_clone_preview(
"""获取克隆音色试听音频(实时 TTS 合成)。
- 克隆音色必须处于 ready 状态
- 使用默认试听文本时,结果缓存 7 天
- 可传入自定义 text 参数试听不同文本
- 使用默认试听文本时,结果缓存 7 天(仅默认 text+speed=1.0+emotion=空 组合缓存)
- 可传入自定义 text/speed/emotion 试听不同效果
"""
import time
if emotion not in _ALLOWED_PREVIEW_EMOTIONS:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"不支持的 emotion 值: {emotion},可选: natural/excited/calm/friendly 或留空",
)
use_case = GetVoiceCloneUseCase(repository)
try:
profile = use_case.execute(clone_id, authenticated_user.user.id)
@@ -315,8 +326,8 @@ def get_voice_clone_preview(
detail=f"Voice clone is not ready (current status: {profile.status})",
)
# 有自定义文本时不缓存
use_cache = not text.strip()
# 仅默认试听文本 + 默认 speed + 默认 emotion 时使用缓存
use_cache = (not text.strip()) and abs(speed - 1.0) < 1e-6 and (not emotion)
if use_cache and clone_id in _clone_preview_cache:
audio_url, duration, file_size, cached_text, cached_at = _clone_preview_cache[clone_id]
@@ -337,12 +348,15 @@ def get_voice_clone_preview(
text=preview_text,
voice_id=profile.voice_id,
format="mp3",
speed=1.0,
speed=speed,
emotion=emotion,
)
except CosyVoiceError as e:
raise HTTPException(status_code=502, detail=f"TTS 合成失败: {e}") from e
except ValueError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) from e
# 缓存(仅默认试听文本
# 缓存(仅默认参数组合
if use_cache:
_clone_preview_cache[clone_id] = (
result.audio_url,
+142 -70
View File
@@ -473,6 +473,7 @@ class EditPlanService:
name_suffix: str = "变体",
voice_duration: float = 0.0,
rng=None,
batch_segments: dict[str, list[tuple[float, float]]] | None = None,
) -> EditPlan:
"""为批量变体生成独立 plan:完整重跑单视频选片流程(#1743)。
@@ -489,6 +490,8 @@ class EditPlanService:
created_by_user_id: 新 plan 归属用户。
name_suffix: plan 名后缀。
rng: 可选随机数(测试注入种子)。
batch_segments: 可选,外部传入的批次内已使用素材区间(前序变体避让用)。
传入时作为初始避让对象;未传则保持原逻辑从源 plan clips 自建(向后兼容)。
Raises:
ValueError: 源 plan 不存在/无片段、素材池为空或时长全未知。
@@ -537,16 +540,26 @@ class EditPlanService:
voice = float(voice_duration or 0.0)
except (TypeError, ValueError):
voice = 0.0
rhythm_template_for_reselect = None
if source.config:
rhythm_template_for_reselect = source.config.get("rhythm_template")
if voice > 0 and source_clips_data:
from packages.domain.voice_duration_planner import plan_clip_durations
_effects: list[str | None] = [c.get("transition_effect") for c in source_clips_data]
_tdurs: list[float] = [float(c.get("transition_duration") or 0.0) for c in source_clips_data]
# #1855 P0:先占位durations为空dict,真正查durations在后面pool_ids确定后执行;
# plan_clip_durations 的 asset_durations 参数在该函数中仅作最大段长钳制,
# 这里先不依赖它(durations 还没查),传 None 让planner用默认策略;
# 真正的asset_durations会在后面 clips_data 生成时传入 reselect_clips_for_variant
target_durations = plan_clip_durations(
len(source_clips_data),
voice,
transition_effects=_effects,
transition_durations=_tdurs,
rhythm_template=rhythm_template_for_reselect,
asset_durations=None,
)
if target_durations:
for _c, _d in zip(source_clips_data, target_durations, strict=False):
@@ -582,12 +595,18 @@ class EditPlanService:
created_by_user_id=created_by_user_id or (source.created_by_user_id or ""),
)
# 批次内区间:以源 plan(变体 0)片段为初始避让对象
batch_segments: dict[str, list[tuple[float, float]]] = {}
for c in clips:
if c.asset_id and float(c.duration or 0) > 0:
st = float(c.start_time or 0.0)
batch_segments.setdefault(c.asset_id, []).append((st, st + float(c.duration)))
# 批次内区间:外部传入时使用外部传入(含前序变体已用区间);
# 否则保持原逻辑从源 plan clips 自建(向后兼容)
if batch_segments is not None:
batch_segments_resolved: dict[str, list[tuple[float, float]]] = {
k: list(v) for k, v in batch_segments.items()
}
else:
batch_segments_resolved = {}
for c in clips:
if c.asset_id and float(c.duration or 0) > 0:
st = float(c.start_time or 0.0)
batch_segments_resolved.setdefault(c.asset_id, []).append((st, st + float(c.duration)))
clips_data = reselect_clips_for_variant(
source_clips_data,
@@ -595,7 +614,7 @@ class EditPlanService:
asset_durations=durations,
asset_scene_points=scene_points,
historical_used_segments=historical,
batch_segments=batch_segments,
batch_segments=batch_segments_resolved,
target_durations=target_durations,
rng=rng,
)
@@ -767,6 +786,17 @@ class EditPlanService:
if plan is None:
return None
# #1855 P0:幂等判断——如果已成功分配过且当前 total_duration 已接近 voice_duration,直接返回
try:
existing_mark = None
if plan.config:
existing_mark = plan.config.get("voice_duration_applied")
cur_total = float(plan.total_duration or 0.0)
if existing_mark is not None and abs(existing_mark - voice) < 1e-6 and abs(cur_total - voice) < 0.5:
return plan
except Exception:
pass
clips: List[EditPlanClip] = []
skip, page = 0, 500
while True:
@@ -838,6 +868,10 @@ class EditPlanService:
)
try:
plan.total_duration = net
# #1855 P0:写入幂等标记,避免二次调用时只重分配 duration 不重算 start_time
new_cfg = dict(plan.config or {})
new_cfg["voice_duration_applied"] = voice
plan.config = new_cfg
db = self._clip_repo.session
db.commit()
except Exception:
@@ -878,6 +912,69 @@ class EditPlanService:
rng = rng or _random.Random()
plan_ids: list[str] = []
# #1855 P0:先确定片段数 clip_count(用于节奏模板生成长度匹配)
from packages.domain.bgm_pool import allocate_bgm_pool_for_variants
from packages.domain.variant_plan_selector import (
generate_pixel_perturbation,
generate_visual_perturbation,
)
from packages.domain.voice_duration_planner import RHYTHM_TEMPLATES, adapt_template_length
clip_count = 0
# 从源 plan 获取片段数(分页读,避免关系加载问题)
_sclips: list = []
_sk, _pg = 0, 500
while True:
_b = self._clip_repo.list_by_plan(source_plan_id, skip=_sk, limit=_pg)
if not _b:
break
_sclips.extend(_b)
if len(_b) < _pg:
break
_sk += _pg
clip_count = len(_sclips)
# 预先生成所有 N 个变体的节奏模板/BGM/扰动参数(时机提前到选片前写入config)
rhythm_templates_for_variants: list = []
for _idx in range(count):
if clip_count > 0:
variant_seed = rng.randint(0, 999999)
_tpl = adapt_template_length(RHYTHM_TEMPLATES[variant_seed % len(RHYTHM_TEMPLATES)], clip_count)
rhythm_templates_for_variants.append(_tpl)
else:
rhythm_templates_for_variants.append(None)
source_bgm_config: dict = {}
source_plan = self.get_plan(source_plan_id)
if source_plan and source_plan.config:
source_bgm_config = source_plan.config.get("bgm", {}) or {}
variant_seeds_for_bgm = [rng.randint(0, 999999) for _ in range(count)]
bgm_pool_assignments = allocate_bgm_pool_for_variants(source_bgm_config, variant_seeds_for_bgm)
def _build_variant_config_update(idx: int) -> dict:
"""构建单个变体的 config 更新(节奏模板/BGM/视觉/像素扰动)。"""
upd: dict = {}
try:
perturbation = generate_visual_perturbation(rng)
if idx == 0:
perturbation["hflip"] = False
upd["visual_perturbation"] = perturbation
except Exception:
logger.exception("变体 %d 视觉扰动生成失败(不阻断)", idx)
try:
pixel_pert = generate_pixel_perturbation(rng)
upd["pixel_perturbation"] = pixel_pert
except Exception:
logger.exception("变体 %d 像素扰动生成失败(不阻断)", idx)
rt = rhythm_templates_for_variants[idx] if idx < len(rhythm_templates_for_variants) else None
if rt is not None:
upd["rhythm_template"] = rt
if idx < len(bgm_pool_assignments):
existing_bgm = dict((source_plan.config or {}).get("bgm", {}) or {})
existing_bgm.update(bgm_pool_assignments[idx])
upd["bgm"] = existing_bgm
return upd
# 变体 0:clone(片段结构同源 plan,起点重算),不污染源 plan
plan0 = self.clone_plan_for_variant(
source_plan_id,
@@ -890,6 +987,15 @@ class EditPlanService:
v0_voice = float(voice_durations[0] or 0.0)
except (TypeError, ValueError):
v0_voice = 0.0
# #1855 P0:在配音分配前先写入变体0的节奏模板/扰动/BGM,确保 apply_voice_duration_to_plan 能读到 rhythm_template
try:
_cfg0 = _build_variant_config_update(0)
if _cfg0:
self.update_plan_config(plan0.id, _cfg0)
except Exception:
logger.exception("变体0 配置写入失败(不阻断): plan=%s", plan0.id)
if v0_voice > 0:
try:
self.apply_voice_duration_to_plan(plan0.id, v0_voice)
@@ -897,7 +1003,12 @@ class EditPlanService:
logger.exception("变体0 配音分配失败(不阻断): plan=%s", plan0.id)
plan_ids.append(plan0.id)
# 变体 1..N-1:独立选片
# #1855 P0:批次内素材区间避让表——从变体0实际落库的clips构建初始值(公共函数)
from app.services.generation_common import collect_plan_segments as _collect_plan_segments
batch_segments_acc: dict[str, list[tuple[float, float]]] = _collect_plan_segments(plan0.id, self._clip_repo)
# 变体 1..N-1:独立选片(传入累积的 batch_segments 做区间避让)
for i in range(1, count):
voice = 0.0
if voice_durations and i < len(voice_durations):
@@ -905,6 +1016,14 @@ class EditPlanService:
voice = float(voice_durations[i] or 0.0)
except (TypeError, ValueError):
voice = 0.0
# #1855 P0:在reselect前先为"变体i"准备配置更新——但reselect内部复制的是source.config
# 所以每个变体独立的节奏模板需要在reselect后单独写入config
# 但 plan_clip_durations 用的是 source.config.rhythm_template(即源plan的节奏模板),
# 为了让每个变体在选片阶段就使用自己的节奏模板分配段长,这里采用:
# - reselect 仍使用源 plan 的 rhythm_template(保持片段骨架一致)
# - 选片完成后立即写入该变体自己的 rhythm_template/扰动/BGM 到config
# 后续不再二次 apply_voice_duration_to_plan(由幂等标记跳过)
variant = self.reselect_plan_for_variant(
source_plan_id,
candidate_asset_ids,
@@ -912,73 +1031,26 @@ class EditPlanService:
name_suffix=f"变体{i + 1}",
voice_duration=voice,
rng=rng,
batch_segments=batch_segments_acc,
)
# 选片完成后写入该变体的独立配置(节奏模板/扰动/BGM)
try:
_cfgi = _build_variant_config_update(i)
if _cfgi:
self.update_plan_config(variant.id, _cfgi)
except Exception:
logger.exception("变体 %d 配置写入失败(不阻断): plan=%s", i, variant.id)
plan_ids.append(variant.id)
# #1764:为每个变体生成独立节奏模板(让批量视频片段时长分布不同)
from packages.domain.voice_duration_planner import RHYTHM_TEMPLATES, adapt_template_length
clip_count = 0
if voice_durations and len(voice_durations) > 0:
# 从源 plan 获取片段数
source_plan = self.get_plan(source_plan_id)
if source_plan and hasattr(source_plan, "clips"):
clip_count = len(list(source_plan.clips)) if source_plan.clips else 0
rhythm_templates_for_variants = []
if clip_count > 0:
for idx in range(len(plan_ids)):
# 每个变体用不同的 seed 选择节奏模板
variant_seed = rng.randint(0, 999999)
template = adapt_template_length(RHYTHM_TEMPLATES[variant_seed % len(RHYTHM_TEMPLATES)], clip_count)
rhythm_templates_for_variants.append(template)
logger.info("变体 %d 节奏模板: plan=%s template=%s", idx, plan_ids[idx], template)
# #1767:BGM 池差异化分配(让批量变体使用不同 BGM / 段落 / 音量)
from packages.domain.bgm_pool import allocate_bgm_pool_for_variants
source_bgm_config = {}
source_plan = self.get_plan(source_plan_id)
if source_plan and source_plan.config:
source_bgm_config = source_plan.config.get("bgm", {}) or {}
variant_seeds_for_bgm = [rng.randint(0, 999999) for _ in plan_ids]
bgm_pool_assignments = allocate_bgm_pool_for_variants(source_bgm_config, variant_seeds_for_bgm)
# 为每个变体生成独立视觉扰动参数(让批量视频画面本身更不同)
from packages.domain.variant_plan_selector import generate_visual_perturbation
for idx, pid in enumerate(plan_ids):
# #1855 P0:把当前新变体的 clips 区间追加到 batch_segments,供下一变体避让
try:
perturbation = generate_visual_perturbation(rng)
# 变体 0 不做 hflip(保持预览 plan 原始画面方向)
if idx == 0:
perturbation["hflip"] = False
config_update = {"visual_perturbation": perturbation}
# #1764:写入节奏模板
if idx < len(rhythm_templates_for_variants):
config_update["rhythm_template"] = rhythm_templates_for_variants[idx]
# #1765:写入像素级扰动滤镜
from packages.domain.variant_plan_selector import generate_pixel_perturbation
pixel_pert = generate_pixel_perturbation(rng)
config_update["pixel_perturbation"] = pixel_pert
# #1767:写入 BGM 池分配(覆盖 bgm 配置中的 preset_id / audio_offset / volume_adjust_db
if idx < len(bgm_pool_assignments):
existing_bgm = dict((source_plan.config or {}).get("bgm", {}) or {})
existing_bgm.update(bgm_pool_assignments[idx])
config_update["bgm"] = existing_bgm
self.update_plan_config(pid, config_update)
logger.info(
"变体 %d 视觉扰动+像素扰动+BGM池: plan=%s vis=%s pix=%s bgm=%s",
idx,
pid,
perturbation,
pixel_pert,
bgm_pool_assignments[idx] if idx < len(bgm_pool_assignments) else None,
)
_new_segs = _collect_plan_segments(variant.id, self._clip_repo)
for _aid, _ivs in _new_segs.items():
batch_segments_acc.setdefault(_aid, []).extend(_ivs)
except Exception:
logger.exception("变体 %d 视觉扰动生成失败(不阻断): plan=%s", idx, pid)
logger.exception("变体 %d 区间收集失败(不阻断): plan=%s", i, variant.id)
# 标记所有变体 plan 的 clips 为 ready(已分配素材+起点,语义上就是 ready)
for pid in plan_ids:
+189
View File
@@ -0,0 +1,189 @@
"""智能剪辑公共服务辅助函数(从 route 层下沉)。
集中管理:
- query_voice_durations:批量查询配音素材时长
- writeback_edit_plan_config:任务入队后回写 EditPlan.config
- collect_plan_segments:分页读取 plan clips 构建素材区间表(变体避让用)
- resolve_latest_plan_by_template:按 template_id + user_id 查最新 EditPlan
设计原则:
- 无副作用的纯查询 / 幂等写回;失败一律不阻断主流程(记日志 + 返回安全默认值)
- 不依赖 FastAPI / HTTPException,便于 service 层和 worker 复用
"""
from __future__ import annotations
import logging
from typing import Any, Optional
from sqlalchemy.orm import Session
logger = logging.getLogger(__name__)
def query_voice_durations(db: Session, voice_ids: list[str]) -> list[float]:
"""批量查询配音素材时长(秒),#1749 配音时长分配用。
逐项 try/float 硬化:MagicMock/异常/缺失 → 0.0(无配音不分配,不阻断)。
#1855 P0修复:不再对 voice_ids 去重,保持与调用方传入顺序/长度一致,
允许同配音id多次出现时返回相同时长(支持"同配音N变体"的时长对齐)。
"""
raw_ids = list(voice_ids or [])
if not raw_ids:
return []
unique_ids: list[str] = []
_seen: set[str] = set()
for v in raw_ids:
if v and v not in _seen:
_seen.add(v)
unique_ids.append(v)
if not unique_ids:
return [0.0 for _ in raw_ids]
try:
from packages.adapters.sqlalchemy_impl.models import AssetModel
rows = db.query(AssetModel.id, AssetModel.duration).filter(AssetModel.id.in_(unique_ids)).all()
dur_map: dict[str, float] = {}
for row in rows:
try:
dur_map[row[0]] = float(row[1] or 0.0)
except (TypeError, ValueError):
dur_map[row[0]] = 0.0
return [dur_map.get(v, 0.0) if v else 0.0 for v in raw_ids]
except Exception:
logger.warning("[generation_common] 配音时长查询失败(按无配音处理,不阻断)", exc_info=True)
return [0.0 for _ in raw_ids]
def writeback_edit_plan_config(
plan_id: str,
task_id: str,
title_config: dict | None,
db: Session,
) -> None:
"""任务入队成功后,回写 EditPlan.configgeneration_task_id + title_config。
用 merge 方式更新,不整体覆盖 config,避免丢失其他字段。
失败只记日志,不影响任务创建。
"""
if not plan_id:
return
try:
from packages.adapters.sqlalchemy_impl.models import EditPlanModel
plan_model = db.query(EditPlanModel).filter(EditPlanModel.id == plan_id).first()
if plan_model is None:
logger.warning("[generation_common] 回写plan.config失败: plan不存在 plan_id=%s", plan_id)
return
current_config = plan_model.config if isinstance(plan_model.config, dict) else {}
merged = dict(current_config)
merged["generation_task_id"] = task_id
if title_config:
# #1901 统一字段名为 "title"worker sync_configs_to_plan 写的是 "title"
# 先读取新旧两个 key,判断标题文字是否变化
old_title_cfg = merged.get("title", {}) or {}
if not isinstance(old_title_cfg, dict) or not (old_title_cfg.get("text") or "").strip():
old_title_cfg = merged.get("title_config", {}) or {}
old_title_text = (old_title_cfg.get("text") or "").strip() if isinstance(old_title_cfg, dict) else ""
new_title_text = (title_config.get("text") or "").strip()
if old_title_text != new_title_text:
if "cover" in merged:
del merged["cover"]
logger.info(
"[generation_common] 标题变化,清除旧封面: plan_id=%s old_title=%s new_title=%s",
plan_id,
old_title_text,
new_title_text,
)
# 字段名归一化(font_size→size, font_preset→font, font_color→color),与 worker sync_configs_to_plan 保持一致
normalized = dict(title_config)
if "font_size" in normalized and "size" not in normalized:
normalized["size"] = normalized["font_size"]
if "font_preset" in normalized and "font" not in normalized:
normalized["font"] = normalized["font_preset"]
if "font_color" in normalized and "color" not in normalized:
normalized["color"] = normalized["font_color"]
merged["title"] = normalized
# 清掉旧 key,避免双字段并存
merged.pop("title_config", None)
plan_model.config = merged
db.commit()
logger.info(
"[generation_common] 回写plan.config成功: plan_id=%s task_id=%s keys=%s",
plan_id,
task_id,
list(merged.keys()),
)
except Exception as e:
logger.warning(
"[generation_common] 回写plan.config异常(不影响任务创建): plan_id=%s error=%s",
plan_id,
e,
exc_info=True,
)
try:
db.rollback()
except Exception:
pass
def collect_plan_segments(
plan_id: str,
clip_repo: Any,
*,
page_size: int = 500,
) -> dict[str, list[tuple[float, float]]]:
"""分页读取 plan 所有 clips,构建 {asset_id: [(start, end), ...]} 素材区间表。
用于 #1855 P0 批次内素材区间避让(变体间素材片段重叠控制)。
"""
segs: dict[str, list[tuple[float, float]]] = {}
sk, pg = 0, page_size
while True:
batch = clip_repo.list_by_plan(plan_id, skip=sk, limit=pg)
if not batch:
break
for c in batch:
if c.asset_id and float(c.duration or 0) > 0:
st = float(c.start_time or 0.0)
segs.setdefault(c.asset_id, []).append((st, st + float(c.duration)))
if len(batch) < pg:
break
sk += pg
return segs
def resolve_latest_plan_by_template(
db: Session,
*,
template_id: str,
user_id: str,
) -> Optional[str]:
"""按 template_id + user_id 查找最新的 EditPlan.id(模板兜底用)。找不到返回 None。"""
if not (template_id or "").strip():
return None
try:
from packages.adapters.sqlalchemy_impl.models import EditPlanModel
latest = (
db.query(EditPlanModel)
.filter(
EditPlanModel.template_id == template_id.strip(),
EditPlanModel.created_by_user_id == user_id,
)
.order_by(EditPlanModel.created_at.desc())
.first()
)
return latest.id if latest else None
except Exception:
logger.warning(
"[generation_common] 按template查找最新plan失败: template=%s user=%s",
template_id,
user_id,
exc_info=True,
)
return None
@@ -26,6 +26,10 @@ export interface BatchVariantPlansRequest {
count: number
/** 源剪辑计划 ID:优先取预览/草稿关联的 plan;不传由后端按 template_id+user 兜底最新 plan */
source_edit_plan_id?: string
/** 统一配音 ID(共用配音模式);独立配音模式不传,改传 voice_library_ids */
voice_library_id?: string
/** 独立配音 ID 列表(长度=count,按变体序号一一对应);共用配音模式不传 */
voice_library_ids?: string[]
}
/** 单个变体的计划片段 */
@@ -36,6 +40,8 @@ export interface VariantPlan {
plan_id: string
/** 该变体的真实片段(顺序/素材/起点与正式成片一致) */
clips: EditPlanClip[]
/** 该变体实际配音时长(秒),用于前端预览按配音时长对齐音画;后端暂未返回时缺省 */
voice_duration?: number
}
/** 批量变体计划响应 */
+2
View File
@@ -16,6 +16,7 @@ export interface TTSSynthesizeRequest {
voice_id?: string
output_name?: string
language?: string
emotion?: string
speed?: number
voice_model?: string
voice_clone_profile_id?: string
@@ -103,6 +104,7 @@ export interface TTSPreviewRequest {
voice_id: string
speed?: number
pitch?: number
language?: string
emotion?: string // 情绪参数:natural/excited/calm/friendly
}
+3
View File
@@ -96,9 +96,12 @@ export const retryVoiceClone = async (id: string): Promise<VoiceCloneProfile> =>
export const getVoiceClonePreview = async (
cloneId: string,
text?: string,
options?: { speed?: number; emotion?: string },
): Promise<VoiceClonePreviewResponse> => {
const searchParams = new URLSearchParams()
if (text) searchParams.set("text", text)
if (options?.speed !== undefined) searchParams.set("speed", String(options.speed))
if (options?.emotion) searchParams.set("emotion", options.emotion)
const qs = searchParams.toString()
const response = await apiClient.get<VoiceClonePreviewResponse>(
`/voice-clones/${cloneId}/preview${qs ? `?${qs}` : ""}`,
-38
View File
@@ -8,9 +8,7 @@ import {
FileOutlined,
FileTextOutlined,
AudioOutlined,
AppstoreOutlined,
EditOutlined,
FolderOutlined,
VideoCameraOutlined,
HistoryOutlined,
TrophyOutlined,
@@ -70,24 +68,6 @@ export const NAV_ITEMS: NavItem[] = [
path: "/app/voices",
icon: React.createElement(AudioOutlined),
},
{
key: "templates",
label: "模板库",
path: "/app/templates",
icon: React.createElement(AppstoreOutlined),
},
{
key: "editing-planner",
label: "剪辑模板",
path: "/app/editing-planner",
icon: React.createElement(EditOutlined),
},
{
key: "my-templates",
label: "我的模板",
path: "/app/my-templates",
icon: React.createElement(FolderOutlined),
},
{
key: "generate",
@@ -150,12 +130,6 @@ export const NAV_GROUPS: NavGroup[] = [
path: "/app/ai-avatar",
icon: React.createElement(UserOutlined),
},
{
key: "editing-planner",
label: "剪辑模板",
path: "/app/editing-planner",
icon: React.createElement(EditOutlined),
},
],
},
{
@@ -191,18 +165,6 @@ export const NAV_GROUPS: NavGroup[] = [
path: "/app/products",
icon: React.createElement(TrophyOutlined),
},
{
key: "templates",
label: "模板库",
path: "/app/templates",
icon: React.createElement(AppstoreOutlined),
},
{
key: "my-templates",
label: "我的模板",
path: "/app/my-templates",
icon: React.createElement(FolderOutlined),
},
],
},
{
-9
View File
@@ -25,15 +25,6 @@ body {
background-color: var(--bg-secondary);
}
/* ── 自定义字体 ── */
@font-face {
font-family: "华康俪金黑";
src: url("/fonts/DFLiJinHei-W8.ttf") format("truetype");
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* 滚动条 - V21 样式 */
::-webkit-scrollbar {
width: 8px;
@@ -227,11 +227,6 @@ const AiAvatarPage: React.FC = () => {
setLipsyncStatus("generating")
setLipsyncErrorMessage("")
console.log("[对口型] 开始生成:", {
videoId: video.id,
mode: isPreSynth ? "pre-synth" : "tts-direct",
textLen: state.scriptText.length,
})
const asset = await getAssetById(video.id)
const videoUrl = asset?.file_url
if (!videoUrl) {
@@ -262,9 +257,7 @@ const AiAvatarPage: React.FC = () => {
emotion: normalizeEmotion(state.emotion),
}
}
console.log("[对口型] createLipsyncJob 请求:", payload)
const job = await createLipsyncJob(payload)
console.log("[对口型] createLipsyncJob 响应:", { id: job.id, status: job.status })
state.setLipsyncJob(job)
// 如果是预合成模式,后端会同步把状态置为 submitted(甚至可能已返回 running),
@@ -274,11 +267,6 @@ const AiAvatarPage: React.FC = () => {
try {
const updated = await getLipsyncJob(job.id)
state.setLipsyncJob(updated)
console.log("[对口型] 轮询状态:", {
id: updated.id,
status: updated.status,
error: updated.error_message,
})
if (updated.status === "completed") {
if (lipsyncTimerRef.current) clearInterval(lipsyncTimerRef.current)
setLipsyncStatus("completed")
@@ -139,13 +139,6 @@ export function PanelVoiceSelector({
}
const targetId = voice.voice_clone_profile_id || voice.id
// DEBUG: 打印请求参数,帮助定位 /tts/preview 失败原因
console.log("[AI数字人-克隆试听] previewTts 请求:", {
voice_id: targetId,
voice_name: voice.name,
voice_type: voice.type,
voice_clone_profile_id: voice.voice_clone_profile_id,
voice_id_field: voice.voice_id,
})
setPreviewingId(voice.id)
try {
const res = await previewTts({
@@ -154,10 +147,6 @@ export function PanelVoiceSelector({
speed: speed, // 透传用户选择的语速(#1822)
emotion: normalizeEmotion(emotion), // 情绪中文→英文枚举
})
console.log("[AI数字人-克隆试听] previewTts 响应:", {
audio_url: res.audio_url?.substring(0, 80),
duration: res.duration,
})
if (!res.audio_url) {
setPreviewingId(null)
message.error("合成试听失败:未返回音频")
@@ -22,8 +22,8 @@ export interface ScriptSentence {
endTime: number
}
/** 句子分隔符:中英文句号/问号/感叹号/分号/逗号/换行(覆盖中文短视频常用断句 */
const SENTENCE_SPLIT_RE = /[。!?!??!;,\n\r]+/
/** 句子分隔符:中英文句号/问号/感叹号/分号/换行(按句断,不在逗号处切;保持与后端一致 */
const SENTENCE_SPLIT_RE = /[。!?!??!;\n\r]+/
/**
* 分句并计算每句的起止时间。
File diff suppressed because it is too large Load Diff
@@ -1,326 +0,0 @@
/**
* 模板编辑器 — 制作/编辑剪辑模板
* 四行布局:顶栏(42px) → 模式栏(48px) → 三栏主体 → 底栏(40px)
*
* 主组件仅保留 Hook 组装与整体布局
* 全局配置 → hooks/useGlobalSettings
* 配音素材 → hooks/useVoiceMaterials
* 撤销重做 → hooks/useUndoRedo
* 抽屉管理 → hooks/useEditorDrawers
* 播放控制 → hooks/usePlaybackControl
* 片段操作 → hooks/useClipOperations
* 模板管理 → hooks/useTemplateManagement
*/
import React, { useState } from "react"
import { useSearchParams } from "react-router-dom"
import { MODE_LABELS } from "@/api/editing-planner"
import { MODE_LIST } from "./constants"
import MediaPanel from "./components/MediaPanel"
import PreviewPlayer from "./components/PreviewPlayer"
import TimelinePanel from "./components/TimelinePanel"
import TopBar from "./components/TopBar"
import ModeBar from "./components/ModeBar"
import RightPanel from "./components/RightPanel"
import StatusBar from "./components/StatusBar"
import EditorDrawers from "./components/EditorDrawers"
import SaveModal from "./components/SaveModal"
import { useUndoRedo } from "./hooks/useUndoRedo"
import { useEditorDrawers } from "./hooks/useEditorDrawers"
import { usePlaybackControl } from "./hooks/usePlaybackControl"
import { useClipOperations } from "./hooks/useClipOperations"
import { useTemplateManagement, FILTER_CATEGORIES } from "./hooks/useTemplateManagement"
import { useGlobalSettings } from "./hooks/useGlobalSettings"
import { useVoiceMaterials } from "./hooks/useVoiceMaterials"
import type { ClipData } from "./types"
import "./EditingPlanner.css"
const EditingPlanner: React.FC = () => {
const [searchParams] = useSearchParams()
const urlTemplateId = searchParams.get("templateId") || ""
const urlPlanId = searchParams.get("planId") || ""
/* ── 片段(撤销/重做) ── */
const {
state: clips,
set: setClips,
undo,
redo,
canUndo,
canRedo,
reset: resetClips,
} = useUndoRedo<ClipData[]>([])
/* ── 全局配置 ── */
const {
titleConfig,
setTitleConfig,
subtitleSettings,
setSubtitleSettings,
bgmSettings,
setBgmSettings,
watermarkSettings,
setWatermarkSettings,
introOutroSettings,
setIntroOutroSettings,
pipSettings,
setPipSettings,
filterSettings,
setFilterSettings,
chromaKeySettings,
setChromaKeySettings,
stickerSettings,
setStickerSettings,
} = useGlobalSettings()
/* ── 右侧栏 Tab ── */
const [rightTab, setRightTab] = useState<"properties" | "clips">("properties")
/* ── 配音素材 ── */
const {
voiceMaterials,
loading: voiceMaterialsLoading,
refetch: refetchVoiceMaterials,
} = useVoiceMaterials()
/* ── 派生计算 ── */
const totalDuration = clips.reduce((sum, c) => sum + c.duration, 0)
/* ── Hook: 抽屉管理 ── */
const drawers = useEditorDrawers()
/* ── Hook: 播放控制 ── */
const playback = usePlaybackControl(totalDuration)
/* ── Hook: 片段操作 ── */
const clipOps = useClipOperations({ clips, setClips })
/* ── Hook: 模板管理 ── */
const tpl = useTemplateManagement({
urlTemplateId,
urlPlanId,
resetClips,
setClips,
setSelectedClipId: clipOps.setSelectedClipId,
setTitleConfig,
setSubtitleSettings,
setBgmSettings,
clips,
totalDuration,
titleConfig,
subtitleSettings,
bgmSettings,
watermarkSettings,
introOutroSettings,
pipSettings,
filterSettings,
chromaKeySettings,
stickerSettings,
})
/* ──────────── 渲染 ──────────── */
return (
<div className="ep-v8-root">
{/* ═══ 第1行:顶栏 42px ═══ */}
<TopBar
currentTemplate={tpl.currentTemplate}
canUndo={canUndo}
canRedo={canRedo}
onUndo={undo}
onRedo={redo}
onOpenSaveModal={tpl.handleOpenSaveModal}
/>
{/* ═══ 第2行:模式栏 56px ═══ */}
<ModeBar
modeList={MODE_LIST}
currentMode={tpl.currentMode}
onModeChange={tpl.handleModeChange}
/>
{/* ═══ 第3行:三栏主体 ═══ */}
<div className="ep-main-body">
{/* 左栏 220px:模板列表 */}
<MediaPanel
templates={tpl.filteredTemplates}
loading={tpl.loadingTemplates}
searchQuery={tpl.searchQuery}
currentFilter={tpl.currentFilter}
filterCategories={FILTER_CATEGORIES}
loadedTemplateId={tpl.loadedTemplateId}
onLoadTemplate={tpl.handleLoadTemplate}
onSearchChange={tpl.setSearchQuery}
onFilterChange={tpl.setCurrentFilter}
/>
{/* 中栏 flex-1 */}
<div className="ep-center-col">
{/* 上半部:视频预览 + 封面预览 */}
<PreviewPlayer
clips={clips}
selectedClipId={clipOps.selectedClipId}
isPlaying={playback.isPlaying}
titleConfig={titleConfig}
subtitleSettings={{
enabled: subtitleSettings.enabled,
position: subtitleSettings.position,
font: subtitleSettings.font,
size: subtitleSettings.fontSize,
animation: subtitleSettings.animation,
}}
onClipSelect={clipOps.handleClipSelect}
onPlayPause={() => playback.setIsPlaying(!playback.isPlaying)}
/>
{/* 下半部:水平时间线 */}
<TimelinePanel
clips={clips}
selectedClipId={clipOps.selectedClipId}
currentMode={tpl.currentMode}
onClipSelect={clipOps.handleClipSelect}
onClipReorder={clipOps.handleClipReorder}
onClipRemove={clipOps.handleClipRemove}
onAddClip={clipOps.handleAddClip}
onClipTrim={clipOps.handleClipTrim}
onClipSplit={clipOps.handleClipSplit}
onClipResetTrim={clipOps.handleClipResetTrim}
currentTime={playback.currentTime}
pixelsPerSecond={playback.pixelsPerSecond}
onZoomChange={playback.handleZoomChange}
onSeek={playback.handleSeek}
totalDuration={totalDuration}
/>
</div>
{/* 右栏 260px:设置面板 */}
<RightPanel
titleConfig={titleConfig}
onTitleConfigChange={setTitleConfig}
rightTab={rightTab}
onTabChange={setRightTab}
selectedClip={clipOps.selectedClip}
subtitleSettings={subtitleSettings}
bgmSettings={bgmSettings}
clipsCount={clips.length}
totalDuration={totalDuration}
currentMode={tpl.currentMode}
onSubtitleSettingsChange={(partial) =>
setSubtitleSettings((prev) => ({ ...prev, ...partial }))
}
onBgmSettingsChange={(partial) => setBgmSettings((prev) => ({ ...prev, ...partial }))}
onClipUpdate={clipOps.handleClipUpdate}
onOpenBgmDrawer={() => drawers.setBgmDrawerOpen(true)}
onOpenSubtitleDrawer={() => drawers.setSubtitleDrawerOpen(true)}
voiceMaterials={voiceMaterials}
voiceMaterialsLoading={voiceMaterialsLoading}
onRefreshVoiceMaterials={refetchVoiceMaterials}
onClipVoiceSelect={clipOps.handleClipVoiceSelect}
onOpenTransitionDrawer={drawers.openTransitionDrawer}
onOpenSpeedDrawer={drawers.openSpeedDrawer}
onOpenTtsDrawer={drawers.openTtsDrawer}
onOpenWatermarkDrawer={() => drawers.setWatermarkDrawerOpen(true)}
onOpenIntroOutroDrawer={() => drawers.setIntroOutroDrawerOpen(true)}
onOpenPipDrawer={() => drawers.setPipDrawerOpen(true)}
onOpenFilterDrawer={() => drawers.setFilterDrawerOpen(true)}
onOpenGreenScreenDrawer={() => drawers.setChromaKeyDrawerOpen(true)}
onOpenStickerDrawer={() => drawers.setStickerDrawerOpen(true)}
clips={clips}
selectedClipId={clipOps.selectedClipId}
onClipSelect={clipOps.handleClipSelect}
onClipMoveUp={(clipId) => {
const idx = clips.findIndex((c) => c.id === clipId)
if (idx > 0) clipOps.handleClipReorder(idx, idx - 1)
}}
onClipMoveDown={(clipId) => {
const idx = clips.findIndex((c) => c.id === clipId)
if (idx < clips.length - 1) clipOps.handleClipReorder(idx, idx + 1)
}}
onClipRemove={clipOps.handleClipRemove}
onClipAdd={() => clipOps.handleAddClip("pip", 3)}
/>
</div>
{/* ═══ 第4行:底栏 40px ═══ */}
<StatusBar
clipsCount={clips.length}
totalDuration={totalDuration}
currentModeLabel={MODE_LABELS[tpl.currentMode]}
templateSegments={tpl.currentTemplate?.segments.length || 0}
/>
{/* ═══ 弹窗 ═══ */}
<SaveModal
open={tpl.saveModalOpen}
loading={tpl.saveLoading}
isUpdate={!!tpl.loadedTemplateId}
draftName={tpl.draftName}
draftCategory={tpl.draftCategory}
draftTags={tpl.draftTags}
categories={tpl.categories}
estimatedDuration={totalDuration}
onNameChange={tpl.setDraftName}
onCategoryChange={tpl.setDraftCategory}
onTagsChange={tpl.setDraftTags}
onSave={tpl.handleSave}
onCancel={() => tpl.setSaveModalOpen(false)}
/>
{/* ═══ Drawer 集合 ═══ */}
<EditorDrawers
bgmDrawerOpen={drawers.bgmDrawerOpen}
bgmSettings={bgmSettings}
onBgmSettingsChange={setBgmSettings}
onCloseBgmDrawer={() => drawers.setBgmDrawerOpen(false)}
subtitleDrawerOpen={drawers.subtitleDrawerOpen}
subtitleSettings={subtitleSettings}
onSubtitleSettingsChange={setSubtitleSettings}
onCloseSubtitleDrawer={() => drawers.setSubtitleDrawerOpen(false)}
transitionDrawerOpen={drawers.transitionDrawerOpen}
transitionTargetClipId={drawers.transitionTargetClipId}
clips={clips}
onTransitionChange={(config) =>
clipOps.handleTransitionChange(drawers.transitionTargetClipId, config)
}
onCloseTransitionDrawer={() => drawers.setTransitionDrawerOpen(false)}
speedDrawerOpen={drawers.speedDrawerOpen}
speedTargetClipId={drawers.speedTargetClipId}
onSpeedChange={(config) => clipOps.handleSpeedChange(drawers.speedTargetClipId, config)}
onApplySpeedAll={clipOps.handleApplySpeedAll}
onCloseSpeedDrawer={() => drawers.setSpeedDrawerOpen(false)}
ttsDrawerOpen={drawers.ttsDrawerOpen}
ttsTargetClipId={drawers.ttsTargetClipId}
onTtsChange={(config) => clipOps.handleTtsChange(drawers.ttsTargetClipId, config)}
onCloseTtsDrawer={() => drawers.setTtsDrawerOpen(false)}
watermarkDrawerOpen={drawers.watermarkDrawerOpen}
watermarkSettings={watermarkSettings}
onWatermarkChange={setWatermarkSettings}
onCloseWatermarkDrawer={() => drawers.setWatermarkDrawerOpen(false)}
introOutroDrawerOpen={drawers.introOutroDrawerOpen}
introOutroSettings={introOutroSettings}
onIntroOutroChange={setIntroOutroSettings}
onCloseIntroOutroDrawer={() => drawers.setIntroOutroDrawerOpen(false)}
pipDrawerOpen={drawers.pipDrawerOpen}
pipSettings={pipSettings}
totalDuration={totalDuration}
onPipChange={setPipSettings}
onClosePipDrawer={() => drawers.setPipDrawerOpen(false)}
filterDrawerOpen={drawers.filterDrawerOpen}
filterSettings={filterSettings}
onFilterChange={setFilterSettings}
onCloseFilterDrawer={() => drawers.setFilterDrawerOpen(false)}
chromaKeyDrawerOpen={drawers.chromaKeyDrawerOpen}
chromaKeySettings={chromaKeySettings}
onChromaKeyChange={setChromaKeySettings}
onCloseChromaKeyDrawer={() => drawers.setChromaKeyDrawerOpen(false)}
stickerDrawerOpen={drawers.stickerDrawerOpen}
stickerSettings={stickerSettings}
onStickerChange={setStickerSettings}
onCloseStickerDrawer={() => drawers.setStickerDrawerOpen(false)}
/>
</div>
)
}
export default EditingPlanner
@@ -1,5 +0,0 @@
/**
* BGM 选择器入口(向后兼容)
* 实际实现位于 ./bgm-selector/ 目录
*/
export { default } from "./bgm-selector"
@@ -1,182 +0,0 @@
/**
* 右栏设置面板 — V8 原型 1:1 还原
* 字幕设置 + BGM设置 + 片段详情
*/
import React from "react"
import type { ClipPropertiesPanelProps } from "@/pages/editing-planner/types/clipProperties"
import SubtitleSettingsSection from "./clip-properties/SubtitleSettingsSection"
import TitleSettingsSection from "./clip-properties/TitleSettingsSection"
import BgmSettingsSection from "./clip-properties/BgmSettingsSection"
import ClipDetailSection from "./clip-properties/ClipDetailSection"
import StatsSection from "./clip-properties/StatsSection"
import { useVoicePreview } from "@/pages/editing-planner/hooks/useVoicePreview"
const ClipPropertiesPanel: React.FC<ClipPropertiesPanelProps> = ({
titleConfig,
onTitleConfigChange,
selectedClip,
subtitleSettings,
bgmSettings,
clipsCount,
totalDuration,
currentMode,
onSubtitleSettingsChange,
onBgmSettingsChange: _onBgmSettingsChange,
onClipUpdate,
onOpenBgmDrawer,
onOpenSubtitleDrawer,
voiceMaterials = [],
voiceMaterialsLoading = false,
onRefreshVoiceMaterials,
onClipVoiceSelect,
onOpenTransitionDrawer,
onOpenSpeedDrawer,
onOpenTtsDrawer,
onOpenWatermarkDrawer,
onOpenIntroOutroDrawer,
onOpenPipDrawer,
onOpenFilterDrawer,
onOpenGreenScreenDrawer,
onOpenStickerDrawer,
}) => {
const { previewingId, handlePreviewVoice, stopPreview } = useVoicePreview()
return (
<div className="ep-right-panel">
{/* ═══ 标题设置 — #1789 ═══ */}
{titleConfig && onTitleConfigChange && (
<TitleSettingsSection config={titleConfig} onChange={onTitleConfigChange} />
)}
{/* ═══ 字幕设置 ═══ */}
<SubtitleSettingsSection
settings={subtitleSettings}
onChange={onSubtitleSettingsChange}
onOpenSubtitleDrawer={onOpenSubtitleDrawer}
/>
{/* ═══ BGM 设置 ═══ */}
<BgmSettingsSection settings={bgmSettings} onOpenBgmDrawer={onOpenBgmDrawer} />
{/* ═══ 水印设置 ═══ */}
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">🔖</span>
</div>
{onOpenWatermarkDrawer && (
<button className="ep-advanced-btn" onClick={onOpenWatermarkDrawer}>
<span className="ep-advanced-btn-icon">🔖</span>
<span className="ep-advanced-btn-label"></span>
<span className="ep-advanced-btn-arrow"></span>
</button>
)}
</div>
{/* ═══ 片头片尾设置 ═══ */}
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">🎬</span>
</div>
{onOpenIntroOutroDrawer && (
<button className="ep-advanced-btn" onClick={onOpenIntroOutroDrawer}>
<span className="ep-advanced-btn-icon">🎬</span>
<span className="ep-advanced-btn-label"></span>
<span className="ep-advanced-btn-arrow"></span>
</button>
)}
</div>
{/* ═══ 混剪 ═══ */}
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">🖼</span>
</div>
{onOpenPipDrawer && (
<button className="ep-advanced-btn" onClick={onOpenPipDrawer}>
<span className="ep-advanced-btn-icon">🖼</span>
<span className="ep-advanced-btn-label"></span>
<span className="ep-advanced-btn-arrow"></span>
</button>
)}
</div>
{/* ═══ 滤镜调色 ═══ */}
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">🎨</span>
</div>
{onOpenFilterDrawer && (
<button className="ep-advanced-btn" onClick={onOpenFilterDrawer}>
<span className="ep-advanced-btn-icon">🎨</span>
<span className="ep-advanced-btn-label"></span>
<span className="ep-advanced-btn-arrow"></span>
</button>
)}
</div>
{/* ═══ 绿幕抠像 ═══ */}
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">🟩</span>
绿
</div>
{onOpenGreenScreenDrawer && (
<button className="ep-advanced-btn" onClick={onOpenGreenScreenDrawer}>
<span className="ep-advanced-btn-icon">🟩</span>
<span className="ep-advanced-btn-label">绿</span>
<span className="ep-advanced-btn-arrow"></span>
</button>
)}
</div>
{/* ═══ 贴纸 ═══ */}
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">🏷</span>
</div>
{onOpenStickerDrawer && (
<button className="ep-advanced-btn" onClick={onOpenStickerDrawer}>
<span className="ep-advanced-btn-icon">🏷</span>
<span className="ep-advanced-btn-label"></span>
<span className="ep-advanced-btn-arrow"></span>
</button>
)}
</div>
{/* ═══ 片段详情(选中时显示) ═══ */}
{selectedClip && (
<ClipDetailSection
clip={selectedClip}
currentMode={currentMode}
voiceMaterials={voiceMaterials}
voiceMaterialsLoading={voiceMaterialsLoading}
onClipUpdate={onClipUpdate}
onRefreshVoiceMaterials={onRefreshVoiceMaterials}
onClipVoiceSelect={onClipVoiceSelect}
onOpenTransitionDrawer={onOpenTransitionDrawer}
onOpenSpeedDrawer={onOpenSpeedDrawer}
onOpenTtsDrawer={onOpenTtsDrawer}
previewingId={previewingId}
onPreviewVoice={handlePreviewVoice}
onStopPreview={stopPreview}
/>
)}
{/* ═══ 统计信息(未选中时显示) ═══ */}
{!selectedClip && (
<StatsSection
clipsCount={clipsCount}
totalDuration={totalDuration}
currentMode={currentMode}
/>
)}
</div>
)
}
export default ClipPropertiesPanel
@@ -1,106 +0,0 @@
/**
* 所有弹窗和 Drawer 组件的集合
* 纯渲染层,业务逻辑和 state 留在父组件
*/
import React from "react"
import SaveModal from "./SaveModal"
import { ClipLevelDrawers } from "./editing-drawers/ClipLevelDrawers"
import { GlobalDrawers } from "./editing-drawers/GlobalDrawers"
import type { EditingDrawersProps } from "./editing-drawers/types"
const EditingDrawers: React.FC<EditingDrawersProps> = (props) => {
const {
saveModalOpen,
saveLoading,
isUpdate,
draftName,
draftCategory,
draftTags,
categories,
estimatedDuration,
onNameChange,
onCategoryChange,
onTagsChange,
onSave,
onCancelSave,
clips,
} = props
return (
<>
{/* 保存弹窗 */}
<SaveModal
open={saveModalOpen}
loading={saveLoading}
isUpdate={isUpdate}
draftName={draftName}
draftCategory={draftCategory}
draftTags={draftTags}
categories={categories}
estimatedDuration={estimatedDuration}
onNameChange={onNameChange}
onCategoryChange={onCategoryChange}
onTagsChange={onTagsChange}
onSave={onSave}
onCancel={onCancelSave}
/>
{/* 片段级抽屉(转场/调速/TTS) */}
<ClipLevelDrawers
clips={clips}
transitionDrawerOpen={props.transitionDrawerOpen}
transitionTargetClipId={props.transitionTargetClipId}
onCloseTransitionDrawer={props.onCloseTransitionDrawer}
onTransitionChange={props.onTransitionChange}
speedDrawerOpen={props.speedDrawerOpen}
speedTargetClipId={props.speedTargetClipId}
onCloseSpeedDrawer={props.onCloseSpeedDrawer}
onSpeedChange={props.onSpeedChange}
onApplySpeedAll={props.onApplySpeedAll}
ttsDrawerOpen={props.ttsDrawerOpen}
ttsTargetClipId={props.ttsTargetClipId}
onCloseTtsDrawer={props.onCloseTtsDrawer}
onTtsChange={props.onTtsChange}
/>
{/* 全局设置抽屉(BGM/字幕/水印/片头片尾/混剪/滤镜/绿幕/贴纸) */}
<GlobalDrawers
bgmDrawerOpen={props.bgmDrawerOpen}
bgmSettings={props.bgmSettings}
onCloseBgmDrawer={props.onCloseBgmDrawer}
onChangeBgmSettings={props.onChangeBgmSettings}
subtitleDrawerOpen={props.subtitleDrawerOpen}
subtitleSettings={props.subtitleSettings}
onCloseSubtitleDrawer={props.onCloseSubtitleDrawer}
onChangeSubtitleSettings={props.onChangeSubtitleSettings}
totalDuration={props.totalDuration}
watermarkDrawerOpen={props.watermarkDrawerOpen}
watermarkSettings={props.watermarkSettings}
onCloseWatermarkDrawer={props.onCloseWatermarkDrawer}
onWatermarkChange={props.onWatermarkChange}
introOutroDrawerOpen={props.introOutroDrawerOpen}
introOutroSettings={props.introOutroSettings}
onCloseIntroOutroDrawer={props.onCloseIntroOutroDrawer}
onIntroOutroChange={props.onIntroOutroChange}
pipDrawerOpen={props.pipDrawerOpen}
pipSettings={props.pipSettings}
onClosePipDrawer={props.onClosePipDrawer}
onPipChange={props.onPipChange}
filterDrawerOpen={props.filterDrawerOpen}
filterSettings={props.filterSettings}
onCloseFilterDrawer={props.onCloseFilterDrawer}
onFilterChange={props.onFilterChange}
chromaKeyDrawerOpen={props.chromaKeyDrawerOpen}
chromaKeySettings={props.chromaKeySettings}
onCloseChromaKeyDrawer={props.onCloseChromaKeyDrawer}
onChromaKeyChange={props.onChromaKeyChange}
stickerDrawerOpen={props.stickerDrawerOpen}
stickerSettings={props.stickerSettings}
onCloseStickerDrawer={props.onCloseStickerDrawer}
onStickerChange={props.onStickerChange}
/>
</>
)
}
export default EditingDrawers
@@ -1,148 +0,0 @@
/**
* 编辑器右侧栏 — 片段列表 Tab
* 紧凑版片段管理:选中、上下移动、删除、添加
*/
import React from "react"
import { Button, Tooltip, Empty } from "antd"
import {
UpOutlined,
DownOutlined,
DeleteOutlined,
PlusOutlined,
ScissorOutlined,
SoundOutlined,
PictureOutlined,
VideoCameraOutlined,
} from "@ant-design/icons"
import type { ClipData, ClipType } from "../types"
interface EditorClipListProps {
clips: ClipData[]
selectedClipId: string | null
onSelect: (clipId: string) => void
onMoveUp: (clipId: string) => void
onMoveDown: (clipId: string) => void
onRemove: (clipId: string) => void
onAdd: () => void
}
const clipTypeIcon: Record<ClipType | string, React.ReactNode> = {
video: <VideoCameraOutlined />,
image: <PictureOutlined />,
voice: <SoundOutlined />,
pip: <ScissorOutlined />,
}
const clipTypeLabel: Record<ClipType | string, string> = {
video: "视频",
image: "图片",
voice: "配音",
pip: "混剪",
}
const EditorClipList: React.FC<EditorClipListProps> = ({
clips,
selectedClipId,
onSelect,
onMoveUp,
onMoveDown,
onRemove,
onAdd,
}) => {
if (clips.length === 0) {
return (
<div className="ep-clip-list-empty">
<Empty
description="暂无片段"
image={Empty.PRESENTED_IMAGE_SIMPLE}
style={{ margin: "40px 0" }}
/>
<Button type="primary" icon={<PlusOutlined />} block onClick={onAdd}>
</Button>
</div>
)
}
return (
<div className="ep-clip-list">
{/* 顶部工具栏 */}
<div className="ep-clip-list-toolbar">
<span className="ep-clip-list-count">
<b>{clips.length}</b>
</span>
<Tooltip title="添加片段">
<Button type="primary" size="small" icon={<PlusOutlined />} onClick={onAdd}>
</Button>
</Tooltip>
</div>
{/* 片段列表 */}
<div className="ep-clip-list-scroll">
{clips.map((clip, index) => (
<div
key={clip.id}
className={`ep-clip-list-item${selectedClipId === clip.id ? " selected" : ""}`}
onClick={() => onSelect(clip.id)}
>
{/* 序号 + 类型图标 */}
<div className="ep-clip-item-head">
<span className="ep-clip-item-index">{index + 1}</span>
<span className="ep-clip-item-type">
{clipTypeIcon[clip.type] || <ScissorOutlined />}
<span className="ep-clip-item-type-label">
{clipTypeLabel[clip.type] || "片段"}
</span>
</span>
</div>
{/* 文案预览 */}
{clip.script_text && (
<div className="ep-clip-item-text">
{clip.script_text.slice(0, 40)}
{clip.script_text.length > 40 ? "..." : ""}
</div>
)}
{/* 操作按钮 */}
<div className="ep-clip-item-actions" onClick={(e) => e.stopPropagation()}>
<Tooltip title="上移">
<Button
type="text"
size="small"
icon={<UpOutlined />}
disabled={index === 0}
onClick={() => onMoveUp(clip.id)}
className="ep-clip-item-btn"
/>
</Tooltip>
<Tooltip title="下移">
<Button
type="text"
size="small"
icon={<DownOutlined />}
disabled={index === clips.length - 1}
onClick={() => onMoveDown(clip.id)}
className="ep-clip-item-btn"
/>
</Tooltip>
<Tooltip title="删除">
<Button
type="text"
size="small"
danger
icon={<DeleteOutlined />}
onClick={() => onRemove(clip.id)}
className="ep-clip-item-btn"
/>
</Tooltip>
</div>
</div>
))}
</div>
</div>
)
}
export default EditorClipList
@@ -1,246 +0,0 @@
import React from "react"
import BgmSelector from "./BgmSelector"
import SubtitleStylePanel from "./SubtitleStylePanel"
import TransitionSelector from "./TransitionSelector"
import SpeedPanel from "./SpeedPanel"
import TtsPanel from "./TtsPanel"
import WatermarkPanel from "./WatermarkPanel"
import IntroOutroPanel from "./IntroOutroPanel"
import PipConfigPanel from "./PipConfigPanel"
import FilterPanel from "./FilterPanel"
import GreenScreenPanel from "./GreenScreenPanel"
import StickerPanel from "./StickerPanel"
import type {
ClipData,
TransitionConfig,
SpeedConfig,
TtsConfig,
WatermarkConfig,
IntroOutroConfig,
PipConfig,
FilterConfig,
ChromaKeyConfig,
StickerConfig,
} from "../types"
import type { SubtitleStyleConfig } from "../types/subtitle"
import type { BgmMixConfig } from "@/api/bgm"
import { DEFAULT_TRANSITION, DEFAULT_SPEED, DEFAULT_TTS_CONFIG } from "../types"
interface EditorDrawersProps {
// BGM
bgmDrawerOpen: boolean
bgmSettings: BgmMixConfig
onBgmSettingsChange: (config: BgmMixConfig) => void
onCloseBgmDrawer: () => void
// 字幕
subtitleDrawerOpen: boolean
subtitleSettings: SubtitleStyleConfig
onSubtitleSettingsChange: (config: SubtitleStyleConfig) => void
onCloseSubtitleDrawer: () => void
// 转场
transitionDrawerOpen: boolean
transitionTargetClipId: string | null
clips: ClipData[]
onTransitionChange: (config: TransitionConfig) => void
onCloseTransitionDrawer: () => void
// 调速
speedDrawerOpen: boolean
speedTargetClipId: string | null
onSpeedChange: (config: SpeedConfig) => void
onApplySpeedAll: (config: SpeedConfig) => void
onCloseSpeedDrawer: () => void
// TTS
ttsDrawerOpen: boolean
ttsTargetClipId: string | null
onTtsChange: (config: TtsConfig) => void
onCloseTtsDrawer: () => void
// 水印
watermarkDrawerOpen: boolean
watermarkSettings: WatermarkConfig
onWatermarkChange: (config: WatermarkConfig) => void
onCloseWatermarkDrawer: () => void
// 片头片尾
introOutroDrawerOpen: boolean
introOutroSettings: IntroOutroConfig
onIntroOutroChange: (config: IntroOutroConfig) => void
onCloseIntroOutroDrawer: () => void
// 混剪
pipDrawerOpen: boolean
pipSettings: PipConfig
totalDuration: number
onPipChange: (config: PipConfig) => void
onClosePipDrawer: () => void
// 滤镜
filterDrawerOpen: boolean
filterSettings: FilterConfig
onFilterChange: (config: FilterConfig) => void
onCloseFilterDrawer: () => void
// 绿幕
chromaKeyDrawerOpen: boolean
chromaKeySettings: ChromaKeyConfig
onChromaKeyChange: (config: ChromaKeyConfig) => void
onCloseChromaKeyDrawer: () => void
// 贴纸
stickerDrawerOpen: boolean
stickerSettings: StickerConfig
onStickerChange: (config: StickerConfig) => void
onCloseStickerDrawer: () => void
}
const EditorDrawers: React.FC<EditorDrawersProps> = ({
bgmDrawerOpen,
bgmSettings,
onBgmSettingsChange,
onCloseBgmDrawer,
subtitleDrawerOpen,
subtitleSettings,
onSubtitleSettingsChange,
onCloseSubtitleDrawer,
transitionDrawerOpen,
transitionTargetClipId,
clips,
onTransitionChange,
onCloseTransitionDrawer,
speedDrawerOpen,
speedTargetClipId,
onSpeedChange,
onApplySpeedAll,
onCloseSpeedDrawer,
ttsDrawerOpen,
ttsTargetClipId,
onTtsChange,
onCloseTtsDrawer,
watermarkDrawerOpen,
watermarkSettings,
onWatermarkChange,
onCloseWatermarkDrawer,
introOutroDrawerOpen,
introOutroSettings,
onIntroOutroChange,
onCloseIntroOutroDrawer,
pipDrawerOpen,
pipSettings,
totalDuration,
onPipChange,
onClosePipDrawer,
filterDrawerOpen,
filterSettings,
onFilterChange,
onCloseFilterDrawer,
chromaKeyDrawerOpen,
chromaKeySettings,
onChromaKeyChange,
onCloseChromaKeyDrawer,
stickerDrawerOpen,
stickerSettings,
onStickerChange,
onCloseStickerDrawer,
}) => {
const transitionConfig = transitionTargetClipId
? (clips.find((c) => c.id === transitionTargetClipId)?.transition ?? DEFAULT_TRANSITION)
: DEFAULT_TRANSITION
const transitionTitle = transitionTargetClipId ? "片段转场设置" : "全局默认转场"
return (
<>
{/* BGM 选择器 Drawer */}
<BgmSelector
open={bgmDrawerOpen}
onClose={onCloseBgmDrawer}
config={bgmSettings}
onChange={onBgmSettingsChange}
/>
{/* 字幕样式配置 Drawer */}
<SubtitleStylePanel
open={subtitleDrawerOpen}
onClose={onCloseSubtitleDrawer}
config={subtitleSettings}
onChange={onSubtitleSettingsChange}
/>
{/* 转场特效选择器 Drawer */}
<TransitionSelector
open={transitionDrawerOpen}
onClose={onCloseTransitionDrawer}
config={transitionConfig}
onChange={onTransitionChange}
title={transitionTitle}
/>
{/* 片段调速面板 Drawer */}
{speedTargetClipId && (
<SpeedPanel
open={speedDrawerOpen}
onClose={onCloseSpeedDrawer}
config={clips.find((c) => c.id === speedTargetClipId)?.speed ?? DEFAULT_SPEED}
onChange={onSpeedChange}
onApplyAll={onApplySpeedAll}
/>
)}
{/* TTS 配音面板 Drawer */}
{ttsTargetClipId && (
<TtsPanel
open={ttsDrawerOpen}
onClose={onCloseTtsDrawer}
config={clips.find((c) => c.id === ttsTargetClipId)?.tts_config ?? DEFAULT_TTS_CONFIG}
onChange={onTtsChange}
/>
)}
{/* 水印配置面板 */}
<WatermarkPanel
open={watermarkDrawerOpen}
onClose={onCloseWatermarkDrawer}
config={watermarkSettings}
onChange={onWatermarkChange}
/>
{/* 片头片尾配置面板 */}
<IntroOutroPanel
open={introOutroDrawerOpen}
onClose={onCloseIntroOutroDrawer}
config={introOutroSettings}
onChange={onIntroOutroChange}
/>
{/* 混剪配置面板 */}
<PipConfigPanel
open={pipDrawerOpen}
onClose={onClosePipDrawer}
config={pipSettings}
onChange={onPipChange}
totalDuration={totalDuration}
/>
{/* 滤镜调色面板 */}
<FilterPanel
open={filterDrawerOpen}
onClose={onCloseFilterDrawer}
config={filterSettings}
onChange={onFilterChange}
/>
{/* 绿幕抠像面板 */}
<GreenScreenPanel
open={chromaKeyDrawerOpen}
onClose={onCloseChromaKeyDrawer}
config={chromaKeySettings}
onChange={onChromaKeyChange}
/>
{/* 贴纸面板 */}
<StickerPanel
open={stickerDrawerOpen}
onClose={onCloseStickerDrawer}
config={stickerSettings}
onChange={onStickerChange}
totalDuration={totalDuration}
/>
</>
)
}
export default EditorDrawers
@@ -1,106 +0,0 @@
/**
* 滤镜调色配置面板
* 预设滤镜 + 手动调节(亮度/对比度/饱和度/色温/色调/锐度)
*/
import React, { useCallback } from "react"
import { Drawer, Switch } from "antd"
import type { FilterConfig, FilterPreset } from "@/pages/editing-planner/types"
import { DEFAULT_FILTER_CONFIG } from "@/pages/editing-planner/types"
import { PRESET_GRADIENTS } from "@/pages/editing-planner/constants/filter"
import FilterPresetGrid from "./filter/FilterPresetGrid"
import FilterManualAdjust from "./filter/FilterManualAdjust"
interface FilterPanelProps {
open: boolean
onClose: () => void
config: FilterConfig
onChange: (config: FilterConfig) => void
}
const FilterPanel: React.FC<FilterPanelProps> = ({ open, onClose, config, onChange }) => {
const update = useCallback(
(partial: Partial<FilterConfig>) => {
onChange({ ...config, ...partial })
},
[config, onChange],
)
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_FILTER_CONFIG, enabled: config.enabled })
}, [config.enabled, onChange])
const handlePresetSelect = useCallback(
(preset: FilterPreset) => {
if (preset === "none") {
onChange({ ...DEFAULT_FILTER_CONFIG, enabled: config.enabled })
} else {
onChange({
...DEFAULT_FILTER_CONFIG,
enabled: config.enabled,
preset,
})
}
},
[config.enabled, onChange],
)
const handleManualChange = useCallback(
(key: keyof FilterConfig, value: number) => {
onChange({ ...config, [key]: value })
},
[config, onChange],
)
return (
<Drawer
title="滤镜调色"
placement="right"
width={420}
styles={{ wrapper: { maxWidth: "100vw" } }}
open={open}
onClose={onClose}
className="filter-panel-drawer"
>
{/* 顶部开关 */}
<div className="filter-header">
<span className="filter-header-label"></span>
<Switch
size="small"
checked={config.enabled}
onChange={(checked) => update({ enabled: checked })}
/>
</div>
{/* 预设滤镜选择 */}
<FilterPresetGrid selectedPreset={config.preset} onPresetSelect={handlePresetSelect} />
{/* 手动调节 */}
<FilterManualAdjust config={config} onChange={handleManualChange} />
{/* 预览色块 */}
<div className="filter-section">
<div className="filter-section-title"></div>
<div
className="filter-preview-block"
style={{
background: PRESET_GRADIENTS[config.preset],
filter: [
`brightness(${100 + config.brightness}%)`,
`contrast(${100 + config.contrast}%)`,
`saturate(${100 + config.saturation}%)`,
].join(" "),
}}
/>
</div>
{/* 底部重置 */}
<div className="filter-footer">
<button className="filter-reset-btn" onClick={handleReset}>
</button>
</div>
</Drawer>
)
}
export default FilterPanel
@@ -1,92 +0,0 @@
import React, { useCallback } from "react"
import { Drawer, Switch } from "antd"
import type { ChromaKeyConfig, ChromaKeyColorPreset } from "../types"
import { DEFAULT_CHROMA_KEY_CONFIG, CHROMA_KEY_PRESET_COLORS } from "../types"
import { GreenScreenPresets } from "./green-screen/GreenScreenPresets"
import { GreenScreenCustomColor } from "./green-screen/GreenScreenCustomColor"
import { GreenScreenSliders } from "./green-screen/GreenScreenSliders"
import { GreenScreenPreview } from "./green-screen/GreenScreenPreview"
interface GreenScreenPanelProps {
open: boolean
onClose: () => void
config: ChromaKeyConfig
onChange: (config: ChromaKeyConfig) => void
}
const GreenScreenPanel: React.FC<GreenScreenPanelProps> = ({ open, onClose, config, onChange }) => {
const update = useCallback(
(partial: Partial<ChromaKeyConfig>) => {
onChange({ ...config, ...partial })
},
[config, onChange],
)
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_CHROMA_KEY_CONFIG, enabled: config.enabled })
}, [config.enabled, onChange])
const handlePresetSelect = useCallback(
(preset: ChromaKeyColorPreset) => {
update({
color_preset: preset,
color: CHROMA_KEY_PRESET_COLORS[preset],
})
},
[update],
)
const handleColorChange = useCallback(
(color: string) => {
update({ color })
},
[update],
)
return (
<Drawer
title="绿幕抠像"
placement="right"
width={420}
styles={{ wrapper: { maxWidth: "100vw" } }}
open={open}
onClose={onClose}
className="green-screen-panel-drawer"
>
<div className="green-header">
<span className="green-header-label">绿</span>
<Switch
size="small"
checked={config.enabled}
onChange={(checked) => update({ enabled: checked })}
/>
</div>
<GreenScreenPresets
selectedPreset={config.color_preset}
onPresetSelect={handlePresetSelect}
/>
<GreenScreenCustomColor color={config.color} onColorChange={handleColorChange} />
<GreenScreenSliders
similarity={config.similarity}
blend={config.blend}
spill={config.spill}
onSimilarityChange={(v) => update({ similarity: v })}
onBlendChange={(v) => update({ blend: v })}
onSpillChange={(v) => update({ spill: v })}
/>
<GreenScreenPreview color={config.color} blend={config.blend} />
<div className="green-footer">
<button className="green-reset-btn" onClick={handleReset}>
</button>
</div>
</Drawer>
)
}
export default GreenScreenPanel
@@ -1,101 +0,0 @@
/**
* 片头片尾配置面板 — Drawer 形式
* 两个区块:片头(Intro)/ 片尾(Outro
* 每个区块支持:类型选择(无/视频/图片)、素材 URL、时长、过渡动画
*/
import React, { useCallback } from "react"
import { Drawer } from "antd"
import type {
IntroOutroConfig,
IntroOutroItem,
IntroOutroKind,
} from "@/pages/editing-planner/types"
import { DEFAULT_INTRO_OUTRO } from "@/pages/editing-planner/types"
import IntroOutroBlock from "./intro-outro/IntroOutroBlock"
interface IntroOutroPanelProps {
open: boolean
onClose: () => void
config: IntroOutroConfig
onChange: (config: IntroOutroConfig) => void
}
const IntroOutroPanel: React.FC<IntroOutroPanelProps> = ({ open, onClose, config, onChange }) => {
/* ── 更新片头 ── */
const handleIntroChange = useCallback(
(partial: Partial<IntroOutroItem>) => {
onChange({ ...config, intro: { ...config.intro, ...partial } })
},
[config, onChange],
)
/* ── 更新片尾 ── */
const handleOutroChange = useCallback(
(partial: Partial<IntroOutroItem>) => {
onChange({ ...config, outro: { ...config.outro, ...partial } })
},
[config, onChange],
)
/* ── 切换片头类型 ── */
const handleIntroKindChange = useCallback(
(kind: IntroOutroKind) => {
handleIntroChange({ kind, url: kind === "none" ? undefined : "" })
},
[handleIntroChange],
)
/* ── 切换片尾类型 ── */
const handleOutroKindChange = useCallback(
(kind: IntroOutroKind) => {
handleOutroChange({ kind, url: kind === "none" ? undefined : "" })
},
[handleOutroChange],
)
/* ── 重置 ── */
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_INTRO_OUTRO })
}, [onChange])
return (
<Drawer
title="🎬 片头片尾设置"
placement="right"
width={420}
styles={{ wrapper: { maxWidth: "100vw" } }}
open={open}
onClose={onClose}
className="intro-outro-panel-drawer"
>
{/* ═══ 片头区块 ═══ */}
<IntroOutroBlock
title="片头"
icon="🎞️"
item={config.intro}
transitionLabel="进入过渡动画"
onKindChange={handleIntroKindChange}
onChange={handleIntroChange}
/>
{/* ═══ 片尾区块 ═══ */}
<IntroOutroBlock
title="片尾"
icon="🏁"
item={config.outro}
transitionLabel="退出过渡动画"
onKindChange={handleOutroKindChange}
onChange={handleOutroChange}
/>
{/* ── 底部操作 ── */}
<div className="iop-footer">
<button className="iop-reset-btn" onClick={handleReset}>
</button>
</div>
</Drawer>
)
}
export default IntroOutroPanel
@@ -1,102 +0,0 @@
/**
* 左侧面板 — 模板列表
* 模板编辑器只负责定义模板规则(片段数量、时长范围),不承载素材管理。
*/
import React from "react"
import type { EditingTemplate } from "@/api/editing-planner"
import { MODE_LABELS } from "@/api/editing-planner"
interface MediaPanelProps {
templates: EditingTemplate[]
loading: boolean
searchQuery: string
currentFilter: string
filterCategories: string[]
loadedTemplateId: string | null
onLoadTemplate: (id: string) => void
onSearchChange: (q: string) => void
onFilterChange: (f: string) => void
}
const MediaPanel: React.FC<MediaPanelProps> = ({
templates,
loading,
searchQuery,
currentFilter,
filterCategories,
loadedTemplateId,
onLoadTemplate,
onSearchChange,
onFilterChange,
}) => {
return (
<div className="ep-left-panel">
{/* 搜索 */}
<div className="ep-search-wrap ep-media-panel-inner">
<span className="ep-search-icon">🔍</span>
<input
className="ep-search-input"
placeholder="搜索模板..."
value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)}
/>
</div>
{/* Chip 分类筛选 */}
<div className="ep-filter-chips">
{filterCategories.map((cat) => (
<button
key={cat}
className={`ep-filter-chip ${currentFilter === cat ? "active" : ""}`}
onClick={() => onFilterChange(cat)}
>
{cat}
</button>
))}
</div>
{/* 模板列表 */}
<div className="ep-template-list">
{loading ? (
<div className="ep-loading">
<span></span>
<span>...</span>
</div>
) : templates.length === 0 ? (
<div className="ep-empty">
<span>📭</span>
<span></span>
</div>
) : (
templates.map((tpl) => (
<div
key={tpl.id}
className={`ep-template-card ${loadedTemplateId === tpl.id ? "active" : ""}`}
onClick={() => onLoadTemplate(tpl.id)}
>
<div className="ep-template-card-header">
<span className="ep-template-card-name">{tpl.name}</span>
<span className="ep-template-card-mode">{MODE_LABELS[tpl.mode]}</span>
</div>
<div className="ep-template-card-meta">
<span> {tpl.estimated_duration}s</span>
<span>📐 {tpl.segments.length}</span>
</div>
{tpl.tags.length > 0 && (
<div className="ep-template-card-tags">
{tpl.tags.map((tag) => (
<span key={tag} className="ep-template-tag">
{tag}
</span>
))}
</div>
)}
</div>
))
)}
</div>
</div>
)
}
export default MediaPanel
@@ -1,28 +0,0 @@
import React from "react"
import type { TemplateMode } from "@/api/editing-planner"
interface ModeBarProps {
modeList: { key: TemplateMode; label: string; icon: string }[]
currentMode: TemplateMode
onModeChange: (mode: TemplateMode) => void
}
const ModeBar: React.FC<ModeBarProps> = ({ modeList, currentMode, onModeChange }) => {
return (
<div className="ep-mode-bar">
<span className="ep-mode-bar-label"></span>
{modeList.map((m) => (
<button
key={m.key}
className={`ep-mode-btn ${currentMode === m.key ? "active" : ""}`}
onClick={() => onModeChange(m.key)}
>
<span className="ep-mode-icon">{m.icon}</span>
<span className="ep-mode-label">{m.label}</span>
</button>
))}
</div>
)
}
export default ModeBar
@@ -1,268 +0,0 @@
import React from "react"
import type { PipLayer, PipGridPosition, PipAnimType, PipSlideDirection } from "../../../types"
import { GRID_POSITIONS, ANIM_OPTIONS, SLIDE_DIR_OPTIONS } from "../constants"
export interface LayerConfigProps {
layer: PipLayer
totalDuration: number
onUpdate: (id: string, partial: Partial<PipLayer>) => void
onGridClick: (pos: PipGridPosition) => void
onWidthChange: (val: number) => void
onHeightChange: (val: number) => void
}
export const LayerConfig: React.FC<LayerConfigProps> = ({
layer,
totalDuration,
onUpdate,
onGridClick,
onWidthChange,
onHeightChange,
}) => {
return (
<div className="pip-config-area">
{/* ── 素材类型 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div className="pip-type-btns">
<button
className={`pip-type-btn${layer.material_type === "image" ? " active" : ""}`}
onClick={() => onUpdate(layer.id, { material_type: "image" })}
>
🖼
</button>
<button
className={`pip-type-btn${layer.material_type === "video" ? " active" : ""}`}
onClick={() => onUpdate(layer.id, { material_type: "video" })}
>
🎬
</button>
</div>
</div>
{/* ── 素材 URL ── */}
<div className="pip-field">
<label className="pip-field-label">
{layer.material_type === "image" ? "图片" : "视频"} URL
</label>
<input
className="pip-input"
type="text"
placeholder={
layer.material_type === "image"
? "https://example.com/image.png"
: "https://example.com/video.mp4"
}
value={layer.material_url}
onChange={(e) => onUpdate(layer.id, { material_url: e.target.value })}
/>
</div>
{/* ── 位置:九宫格 + 坐标 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div style={{ display: "flex", gap: 16, alignItems: "flex-start" }}>
<div className="pip-grid">
{GRID_POSITIONS.map((pos) => (
<button
key={pos}
className={`pip-grid-btn${layer.grid_position === pos ? " active" : ""}`}
onClick={() => onGridClick(pos)}
>
<span className="pip-grid-dot" />
</button>
))}
</div>
<div className="pip-field-row" style={{ flex: 1 }}>
<div>
<label className="pip-field-label">X (%)</label>
<input
className="pip-number"
type="number"
min={0}
max={100}
value={layer.x}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val >= 0 && val <= 100) onUpdate(layer.id, { x: val })
}}
/>
</div>
<div>
<label className="pip-field-label">Y (%)</label>
<input
className="pip-number"
type="number"
min={0}
max={100}
value={layer.y}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val >= 0 && val <= 100) onUpdate(layer.id, { y: val })
}}
/>
</div>
</div>
</div>
</div>
{/* ── 尺寸 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div className="pip-slider-row">
<span style={{ fontSize: 12, color: "#999", width: 20 }}></span>
<input
className="pip-slider"
type="range"
min={10}
max={80}
value={layer.width}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val)) onWidthChange(val)
}}
/>
<span className="pip-slider-value">{layer.width}%</span>
</div>
<div className="pip-slider-row" style={{ marginTop: 6 }}>
<span style={{ fontSize: 12, color: "#999", width: 20 }}></span>
<input
className="pip-slider"
type="range"
min={10}
max={80}
value={layer.height}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val)) onHeightChange(val)
}}
/>
<span className="pip-slider-value">{layer.height}%</span>
</div>
<div
className="pip-lock-row"
style={{ marginTop: 6 }}
onClick={() => onUpdate(layer.id, { aspect_lock: !layer.aspect_lock })}
>
<span className="pip-lock-icon">{layer.aspect_lock ? "🔒" : "🔓"}</span>
<span>{layer.aspect_lock ? "已锁定比例" : "锁定宽高比"}</span>
</div>
</div>
{/* ── 圆角 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div className="pip-slider-row">
<input
className="pip-slider"
type="range"
min={0}
max={50}
value={layer.border_radius}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val)) onUpdate(layer.id, { border_radius: val })
}}
/>
<span className="pip-slider-value">{layer.border_radius}%</span>
</div>
</div>
{/* ── 透明度 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div className="pip-slider-row">
<input
className="pip-slider"
type="range"
min={0}
max={100}
value={layer.opacity}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val)) onUpdate(layer.id, { opacity: val })
}}
/>
<span className="pip-slider-value">{layer.opacity}%</span>
</div>
</div>
{/* ── 时间 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div className="pip-field-row">
<div>
<label className="pip-field-label"> (s)</label>
<input
className="pip-number"
type="number"
min={0}
max={totalDuration || 999}
step={0.1}
value={layer.start_time}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val >= 0 && val <= totalDuration)
onUpdate(layer.id, { start_time: val })
}}
/>
</div>
<div>
<label className="pip-field-label"> (s)</label>
<input
className="pip-number"
type="number"
min={0.1}
max={totalDuration || 999}
step={0.1}
value={layer.duration}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val > 0 && val <= totalDuration)
onUpdate(layer.id, { duration: val })
}}
/>
</div>
</div>
</div>
{/* ── 入场动画 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<select
className="pip-select"
value={layer.animation}
onChange={(e) => onUpdate(layer.id, { animation: e.target.value as PipAnimType })}
>
{ANIM_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
{/* 滑入方向(仅 slide_in 时显示) */}
{layer.animation === "slide_in" && (
<div className="pip-field">
<label className="pip-field-label"></label>
<select
className="pip-select"
value={layer.slide_direction}
onChange={(e) =>
onUpdate(layer.id, {
slide_direction: e.target.value as PipSlideDirection,
})
}
>
{SLIDE_DIR_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
)}
</div>
)
}
@@ -1,58 +0,0 @@
import React from "react"
import type { PipLayer } from "../../../types"
import { LAYER_COLORS } from "../constants"
export interface LayerListProps {
layers: PipLayer[]
selectedId: string
onSelect: (id: string) => void
onDelete: (id: string) => void
}
export const LayerList: React.FC<LayerListProps> = ({ layers, selectedId, onSelect, onDelete }) => {
if (layers.length === 0) {
return (
<div className="pip-layer-list">
<div className="pip-layer-empty"></div>
</div>
)
}
return (
<div className="pip-layer-list">
{layers.map((layer, idx) => (
<div
key={layer.id}
className={`pip-layer-item${selectedId === layer.id ? " active" : ""}`}
onClick={() => onSelect(layer.id)}
>
{layer.thumbnail_url || layer.material_url ? (
<img
className="pip-layer-thumb"
src={layer.thumbnail_url || layer.material_url || undefined}
alt={layer.name}
/>
) : (
<div
className="pip-layer-thumb"
style={{
background: LAYER_COLORS[idx % LAYER_COLORS.length],
}}
/>
)}
<span className="pip-layer-name">{layer.name}</span>
<button
className="pip-layer-delete"
onClick={(e) => {
e.stopPropagation()
onDelete(layer.id)
}}
title="删除图层"
>
</button>
</div>
))}
</div>
)
}
@@ -1,33 +0,0 @@
import React from "react"
import type { PipLayer } from "../../../types"
import { LAYER_COLORS } from "../constants"
export interface PipPreviewProps {
layers: PipLayer[]
selectedId: string
}
export const PipPreview: React.FC<PipPreviewProps> = ({ layers, selectedId }) => {
return (
<div className="pip-preview-box">
{layers.map((layer, idx) => (
<div
key={layer.id}
className={`pip-preview-layer${selectedId === layer.id ? " selected" : ""}`}
style={{
left: `${layer.x}%`,
top: `${layer.y}%`,
width: `${layer.width}%`,
height: `${layer.height}%`,
background: LAYER_COLORS[idx % LAYER_COLORS.length],
opacity: layer.opacity / 100,
borderRadius: `${layer.border_radius}%`,
zIndex: layer.z_index,
}}
>
<span className="pip-preview-label">{layer.name}</span>
</div>
))}
</div>
)
}
@@ -1,55 +0,0 @@
import type { PipGridPosition, PipAnimType, PipSlideDirection } from "../../types"
/** 九宫格位置 → 百分比坐标映射 */
export const GRID_POSITION_MAP: Record<PipGridPosition, { x: number; y: number }> = {
top_left: { x: 5, y: 5 },
top_center: { x: 37.5, y: 5 },
top_right: { x: 70, y: 5 },
center_left: { x: 5, y: 37.5 },
center: { x: 37.5, y: 37.5 },
center_right: { x: 70, y: 37.5 },
bottom_left: { x: 5, y: 70 },
bottom_center: { x: 37.5, y: 70 },
bottom_right: { x: 70, y: 70 },
}
/** 九宫格位置选项 */
export const GRID_POSITIONS: PipGridPosition[] = [
"top_left",
"top_center",
"top_right",
"center_left",
"center",
"center_right",
"bottom_left",
"bottom_center",
"bottom_right",
]
/** 入场动画选项 */
export const ANIM_OPTIONS: { value: PipAnimType; label: string }[] = [
{ value: "none", label: "无" },
{ value: "fade_in", label: "淡入" },
{ value: "slide_in", label: "滑入" },
]
/** 滑入方向选项 */
export const SLIDE_DIR_OPTIONS: { value: PipSlideDirection; label: string }[] = [
{ value: "left", label: "← 左" },
{ value: "right", label: "→ 右" },
{ value: "up", label: "↑ 上" },
{ value: "down", label: "↓ 下" },
]
/** 预览图层颜色池 */
export const LAYER_COLORS = [
"rgba(22,119,255,0.5)",
"rgba(82,196,26,0.5)",
"rgba(250,173,20,0.5)",
"rgba(255,77,79,0.5)",
"rgba(114,46,209,0.5)",
"rgba(19,194,194,0.5)",
]
let layerIdCounter = 0
export const genLayerId = () => `pip_layer_${Date.now()}_${++layerIdCounter}`
@@ -1,130 +0,0 @@
import { useState, useCallback, useMemo } from "react"
import type { PipConfig, PipLayer, PipGridPosition } from "../../../types"
import { DEFAULT_PIP_LAYER, DEFAULT_PIP_CONFIG } from "../../../types"
import { GRID_POSITION_MAP, genLayerId } from "../constants"
const MIN_DIMENSION = 10
const MAX_DIMENSION = 80
interface UsePipConfigPanelOptions {
config: PipConfig
onChange: (config: PipConfig) => void
}
export const usePipConfigPanel = ({ config, onChange }: UsePipConfigPanelOptions) => {
const [selectedId, setSelectedId] = useState<string>("")
const selectedLayer = useMemo(
() => config.layers.find((l) => l.id === selectedId) ?? null,
[config.layers, selectedId],
)
/* ── 添加图层 ── */
const handleAddLayer = useCallback(() => {
const newLayer: PipLayer = {
...DEFAULT_PIP_LAYER,
id: genLayerId(),
name: `图层 ${config.layers.length + 1}`,
z_index: config.layers.length + 1,
}
onChange({
...config,
layers: [...config.layers, newLayer],
})
setSelectedId(newLayer.id)
}, [config, onChange])
/* ── 删除图层 ── */
const handleDeleteLayer = useCallback(
(id: string) => {
const newLayers = config.layers.filter((l) => l.id !== id)
onChange({ ...config, layers: newLayers })
if (selectedId === id) {
setSelectedId(newLayers.length > 0 ? newLayers[0].id : "")
}
},
[config, onChange, selectedId],
)
/* ── 更新图层 ── */
const updateLayer = useCallback(
(id: string, partial: Partial<PipLayer>) => {
onChange({
...config,
layers: config.layers.map((l) => (l.id === id ? { ...l, ...partial } : l)),
})
},
[config, onChange],
)
/* ── 切换启用 ── */
const handleEnableToggle = useCallback(
(checked: boolean) => {
onChange({ ...config, enabled: checked })
},
[config, onChange],
)
/* ── 重置 ── */
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_PIP_CONFIG })
setSelectedId("")
}, [onChange])
/* ── 九宫格点击 ── */
const handleGridClick = useCallback(
(pos: PipGridPosition) => {
if (!selectedLayer) return
const coords = GRID_POSITION_MAP[pos]
updateLayer(selectedLayer.id, {
grid_position: pos,
x: coords.x,
y: coords.y,
})
},
[selectedLayer, updateLayer],
)
/* ── 宽高变化 ── */
const handleWidthChange = useCallback(
(val: number) => {
if (!selectedLayer) return
const partial: Partial<PipLayer> = { width: val }
if (selectedLayer.aspect_lock && selectedLayer.height > 0) {
const ratio = selectedLayer.width / selectedLayer.height
const newHeight = Math.round(val / ratio)
partial.height = Math.max(MIN_DIMENSION, Math.min(MAX_DIMENSION, newHeight))
}
updateLayer(selectedLayer.id, partial)
},
[selectedLayer, updateLayer],
)
const handleHeightChange = useCallback(
(val: number) => {
if (!selectedLayer) return
const partial: Partial<PipLayer> = { height: val }
if (selectedLayer.aspect_lock && selectedLayer.width > 0 && selectedLayer.height > 0) {
const ratio = selectedLayer.width / selectedLayer.height
const newWidth = Math.round(val * ratio)
partial.width = Math.max(MIN_DIMENSION, Math.min(MAX_DIMENSION, newWidth))
}
updateLayer(selectedLayer.id, partial)
},
[selectedLayer, updateLayer],
)
return {
selectedId,
setSelectedId,
selectedLayer,
handleAddLayer,
handleDeleteLayer,
updateLayer,
handleEnableToggle,
handleReset,
handleGridClick,
handleWidthChange,
handleHeightChange,
}
}
@@ -1,105 +0,0 @@
/**
* 混剪配置面板 — Drawer 形式
* 左侧图层列表 + 右侧单图层配置 + 迷你预览区
*/
import React from "react"
import { Drawer, Switch } from "antd"
import type { PipConfig } from "../../types"
import { usePipConfigPanel } from "./hooks/usePipConfigPanel"
import { LayerList } from "./components/LayerList"
import { PipPreview } from "./components/PipPreview"
import { LayerConfig } from "./components/LayerConfig"
export interface PipConfigPanelProps {
open: boolean
onClose: () => void
config: PipConfig
onChange: (config: PipConfig) => void
totalDuration: number
}
const PipConfigPanel: React.FC<PipConfigPanelProps> = ({
open,
onClose,
config,
onChange,
totalDuration,
}) => {
const {
selectedId,
setSelectedId,
selectedLayer,
handleAddLayer,
handleDeleteLayer,
updateLayer,
handleEnableToggle,
handleReset,
handleGridClick,
handleWidthChange,
handleHeightChange,
} = usePipConfigPanel({ config, onChange })
return (
<Drawer
title="🖼️ 混剪设置"
placement="right"
width={520}
open={open}
onClose={onClose}
className="pip-config-panel-drawer"
>
{/* ═══ 顶部工具栏 ═══ */}
<div className="pip-toolbar">
<div className="pip-toolbar-left">
<button className="pip-add-btn" onClick={handleAddLayer}>
+
</button>
</div>
<div className="pip-enable-switch">
<span></span>
<Switch size="small" checked={config.enabled} onChange={handleEnableToggle} />
</div>
</div>
{/* ═══ 主体:图层列表 + 配置区 ═══ */}
<div className="pip-body">
{/* 左侧图层列表 */}
<LayerList
layers={config.layers}
selectedId={selectedId}
onSelect={setSelectedId}
onDelete={handleDeleteLayer}
/>
{/* 右侧配置区 */}
{!selectedLayer ? (
<div className="pip-config-area">
<div className="pip-config-empty"></div>
</div>
) : (
<>
<PipPreview layers={config.layers} selectedId={selectedId} />
<LayerConfig
layer={selectedLayer}
totalDuration={totalDuration}
onUpdate={updateLayer}
onGridClick={handleGridClick}
onWidthChange={handleWidthChange}
onHeightChange={handleHeightChange}
/>
</>
)}
</div>
{/* ── 底部操作 ── */}
<div className="pip-footer">
<button className="pip-reset-btn" onClick={handleReset}>
</button>
</div>
</Drawer>
)
}
export default PipConfigPanel
@@ -1,431 +0,0 @@
/**
* 预览播放器样式 — V21 设计系统
* 任务 2.16
*/
/* ============================================================
预览播放器容器
============================================================ */
.ep-preview {
flex-shrink: 0;
display: flex;
flex-direction: column;
border-bottom: 1px solid var(--border-color);
background: var(--bg-primary);
}
/* ── 预览画面 ── */
.ep-preview-screen {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
max-height: 280px;
overflow: hidden;
background: #0f0f14;
border-radius: 0;
}
.ep-preview-visual {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--space-md);
transition: background 0.4s ease;
}
/* 片段类型大图标 */
.ep-preview-type-icon {
font-size: 56px;
opacity: 0.7;
filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.3));
animation: ep-preview-float 3s ease-in-out infinite;
}
@keyframes ep-preview-float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-6px);
}
}
/* 文案字幕 */
.ep-preview-subtitle {
max-width: 80%;
padding: 8px 20px;
background: rgba(0, 0, 0, 0.55);
backdrop-filter: blur(8px);
border-radius: var(--radius-md);
color: #fff;
font-size: 14px;
line-height: 1.6;
text-align: center;
letter-spacing: 0.02em;
}
/* 片段序号角标 */
.ep-preview-clip-badge {
position: absolute;
top: 12px;
left: 12px;
padding: 3px 10px;
border-radius: var(--radius-sm);
color: #fff;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.04em;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
}
/* 素材类型标签 */
.ep-preview-material-tag {
position: absolute;
top: 12px;
right: 12px;
padding: 3px 10px;
border-radius: var(--radius-sm);
background: rgba(0, 0, 0, 0.45);
backdrop-filter: blur(6px);
color: rgba(255, 255, 255, 0.85);
font-size: 11px;
text-transform: capitalize;
}
/* 空状态 */
.ep-preview-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: rgba(255, 255, 255, 0.4);
}
.ep-preview-empty-icon {
font-size: 48px;
margin-bottom: var(--space-sm);
opacity: 0.5;
}
.ep-preview-empty p {
margin: 0;
font-size: var(--font-size-sm);
}
/* ============================================================
控制栏
============================================================ */
.ep-preview-controls {
display: flex;
align-items: center;
gap: var(--space-md);
padding: 8px var(--space-lg);
border-top: 1px solid rgba(255, 255, 255, 0.06);
background: #16161e;
}
/* 时间显示 */
.ep-preview-time {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
font-variant-numeric: tabular-nums;
min-width: 90px;
}
.ep-preview-time-current {
color: #fff;
font-weight: 600;
}
.ep-preview-time-sep {
color: rgba(255, 255, 255, 0.3);
}
.ep-preview-time-total {
color: rgba(255, 255, 255, 0.5);
}
/* 播放按钮组 */
.ep-preview-buttons {
display: flex;
align-items: center;
gap: 4px;
flex: 1;
justify-content: center;
}
.ep-preview-btn {
width: 32px;
height: 32px;
border: none;
background: transparent;
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
cursor: pointer;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
transition: var(--transition-all);
}
.ep-preview-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
}
.ep-preview-btn-play {
width: 40px;
height: 40px;
background: var(--primary-color);
color: #fff;
font-size: 16px;
border-radius: 50%;
box-shadow: 0 2px 8px rgba(79, 70, 229, 0.4);
}
.ep-preview-btn-play:hover {
background: var(--primary-hover);
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.5);
}
/* 片段信息 */
.ep-preview-clip-info {
display: flex;
align-items: center;
gap: var(--space-sm);
font-size: 12px;
min-width: 90px;
justify-content: flex-end;
}
.ep-preview-clip-idx {
color: rgba(255, 255, 255, 0.6);
}
.ep-preview-clip-dur {
color: var(--primary-color);
font-weight: 600;
background: rgba(79, 70, 229, 0.15);
padding: 1px 8px;
border-radius: 999px;
}
/* ============================================================
进度条(可拖拽)
============================================================ */
.ep-preview-progress {
position: relative;
height: 20px;
padding: 7px var(--space-lg);
cursor: pointer;
user-select: none;
background: #16161e;
}
.ep-preview-progress-track {
position: relative;
height: 6px;
display: flex;
background: rgba(255, 255, 255, 0.08);
border-radius: 3px;
overflow: hidden;
}
.ep-preview-progress-segment {
height: 100%;
transition: opacity 0.2s ease;
}
.ep-preview-progress-fill {
position: absolute;
top: 0;
left: 0;
height: 100%;
background: rgba(255, 255, 255, 0.25);
border-radius: 3px;
pointer-events: none;
}
.ep-preview-progress-handle {
position: absolute;
top: 50%;
width: 14px;
height: 14px;
background: #fff;
border-radius: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
transition: transform 0.1s ease;
pointer-events: none;
z-index: 2;
}
.ep-preview-progress:hover .ep-preview-progress-handle {
transform: translate(-50%, -50%) scale(1.2);
}
/* ============================================================
迷你时间线
============================================================ */
.ep-preview-timeline {
position: relative;
display: flex;
height: 28px;
gap: 2px;
padding: 0 var(--space-lg);
background: #12121a;
overflow: hidden;
}
.ep-preview-timeline-seg {
height: 100%;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: var(--transition-all);
min-width: 8px;
position: relative;
}
.ep-preview-timeline-seg:hover {
opacity: 0.85;
transform: scaleY(1.08);
}
.ep-preview-timeline-seg.active {
opacity: 1;
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.3);
}
.ep-preview-timeline-seg.selected {
box-shadow: 0 0 0 2px var(--primary-color);
}
.ep-preview-timeline-seg-label {
font-size: 10px;
opacity: 0.8;
pointer-events: none;
}
/* 播放头 */
.ep-preview-playhead {
position: absolute;
top: 0;
width: 2px;
height: 100%;
background: #fff;
transform: translateX(-50%);
pointer-events: none;
z-index: 3;
box-shadow: 0 0 4px rgba(255, 255, 255, 0.5);
}
/* ============================================================
片段内进度条
============================================================ */
.ep-preview-clip-progress {
height: 3px;
background: rgba(255, 255, 255, 0.06);
}
.ep-preview-clip-progress-fill {
height: 100%;
transition: width 0.1s linear;
border-radius: 0 2px 2px 0;
}
/* ============================================================
响应式
============================================================ */
@media (max-width: 1200px) {
.ep-preview-screen {
max-height: 240px;
}
}
@media (max-width: 900px) {
.ep-preview-screen {
max-height: 200px;
}
.ep-preview-type-icon {
font-size: 40px;
}
.ep-preview-subtitle {
font-size: 13px;
padding: 6px 14px;
}
.ep-preview-time {
min-width: 70px;
font-size: 11px;
}
.ep-preview-clip-info {
min-width: 70px;
font-size: 11px;
}
}
@media (max-width: 768px) {
.ep-preview-screen {
max-height: 180px;
}
.ep-preview-controls {
padding: 6px var(--space-md);
gap: var(--space-sm);
}
.ep-preview-btn-play {
width: 36px;
height: 36px;
font-size: 14px;
}
.ep-preview-timeline {
height: 22px;
padding: 0 var(--space-md);
}
}
@media (max-width: 480px) {
.ep-preview-screen {
max-height: 140px;
}
.ep-preview-type-icon {
font-size: 32px;
}
.ep-preview-subtitle {
font-size: 12px;
max-width: 90%;
}
.ep-preview-clip-badge,
.ep-preview-material-tag {
font-size: 10px;
padding: 2px 6px;
}
.ep-preview-clip-info {
display: none;
}
.ep-preview-time {
min-width: auto;
}
}
@@ -1,129 +0,0 @@
/**
* 预览区 — V8 原型 1:1 还原
* 手机模型预览
*/
import React from "react"
import type { ClipData, ClipType } from "../types"
import type { TitleConfig } from "@/api/template-editor"
interface SubtitleSettings {
enabled: boolean
position: string
font: string
size: number
animation: string
}
interface PreviewPlayerProps {
clips: ClipData[]
selectedClipId: string | null
isPlaying: boolean
titleConfig?: TitleConfig
subtitleSettings?: SubtitleSettings
onClipSelect: (clipId: string) => void
onPlayPause: () => void
}
const CLIP_TYPE_ICONS: Record<ClipType, string> = {
voice: "🎙️",
pip: "🖼️",
}
const CLIP_TYPE_LABELS: Record<ClipType, string> = {
voice: "口播",
pip: "混剪",
}
const PreviewPlayer: React.FC<PreviewPlayerProps> = ({
clips,
selectedClipId,
isPlaying,
titleConfig,
subtitleSettings,
onPlayPause,
}) => {
const selectedClip = clips.find((c) => c.id === selectedClipId)
const displayClip = selectedClip || clips[0]
return (
<div className="ep-preview-area">
{/* 手机模型预览 */}
<div className="ep-phone-preview">
<div className="ep-phone-status-bar">
<span>9:41</span>
<span>📶 🔋</span>
</div>
<div className="ep-phone-content">
{displayClip ? (
<>
<button className="ep-phone-play-btn" onClick={onPlayPause}>
{isPlaying ? "⏸" : "▶"}
</button>
<div className="ep-phone-progress">
<div
className="ep-phone-progress-fill"
style={{ width: isPlaying ? "45%" : "0%" }}
/>
</div>
<div className="ep-phone-clip-label">
{CLIP_TYPE_ICONS[displayClip.type] || "🎬"}{" "}
{CLIP_TYPE_LABELS[displayClip.type] || "片段"}
</div>
</>
) : (
<span className="ep-phone-empty-hint"></span>
)}
{/* 标题实时预览 */}
{titleConfig && !titleConfig.ai_auto_select && titleConfig.content && (
<div
className="ep-preview-title"
style={{
fontSize: `${Math.min(titleConfig.font_size, 20)}px`,
fontFamily: titleConfig.font_preset,
fontWeight: "bold",
fontStyle: "normal",
textShadow: "2px 2px 4px rgba(0,0,0,0.5)",
WebkitTextStroke: "1px rgba(0,0,0,0.6)",
top:
titleConfig.position === "top"
? "6.25%"
: titleConfig.position === "center"
? "50%"
: "auto",
bottom: titleConfig.position === "bottom" ? "30px" : "auto",
transform: titleConfig.position === "center" ? "translateY(-50%)" : "none",
color: titleConfig.font_color,
}}
>
{titleConfig.content}
</div>
)}
{/* 字幕实时预览 */}
{subtitleSettings?.enabled && (
<div
className="ep-preview-subtitle"
style={{
fontSize: `${Math.min(subtitleSettings.size, 14)}px`,
fontFamily: subtitleSettings.font,
top:
subtitleSettings.position === "top"
? "8px"
: subtitleSettings.position === "center"
? "50%"
: "auto",
bottom: subtitleSettings.position === "bottom" ? "8px" : "auto",
transform: subtitleSettings.position === "center" ? "translateY(-50%)" : "none",
}}
>
</div>
)}
</div>
</div>
</div>
)
}
export default PreviewPlayer
@@ -1,176 +0,0 @@
import React from "react"
import ClipPropertiesPanel from "./ClipPropertiesPanel"
import EditorClipList from "./EditorClipList"
import type { ClipData } from "../types"
import type { SubtitleStyleConfig } from "../types/subtitle"
import type { BgmMixConfig } from "@/api/bgm"
import type { TemplateMode } from "@/api/editing-planner"
import type { TitleConfig } from "@/api/template-editor"
import type { AssetItem } from "@/api/assets"
interface RightPanelProps {
titleConfig?: TitleConfig
onTitleConfigChange?: (config: TitleConfig | ((prev: TitleConfig) => TitleConfig)) => void
rightTab: "properties" | "clips"
onTabChange: (tab: "properties" | "clips") => void
// 属性 tab
selectedClip: ClipData | null
subtitleSettings: SubtitleStyleConfig
bgmSettings: BgmMixConfig
clipsCount: number
totalDuration: number
currentMode: TemplateMode
onSubtitleSettingsChange: (partial: Partial<SubtitleStyleConfig>) => void
onBgmSettingsChange: (partial: Partial<BgmMixConfig>) => void
onClipUpdate: (clipId: string, updates: Partial<ClipData>) => void
onOpenBgmDrawer: () => void
onOpenSubtitleDrawer: () => void
voiceMaterials: AssetItem[]
voiceMaterialsLoading: boolean
onRefreshVoiceMaterials: () => void
onClipVoiceSelect: (clipId: string, asset: AssetItem | null) => void
onOpenTransitionDrawer: (clipId?: string) => void
onOpenSpeedDrawer: (clipId: string) => void
onOpenTtsDrawer: (clipId: string) => void
onOpenWatermarkDrawer: () => void
onOpenIntroOutroDrawer: () => void
onOpenPipDrawer: () => void
onOpenFilterDrawer: () => void
onOpenGreenScreenDrawer: () => void
onOpenStickerDrawer: () => void
// 片段 tab
clips: ClipData[]
selectedClipId: string | null
onClipSelect: (clipId: string) => void
onClipMoveUp: (clipId: string) => void
onClipMoveDown: (clipId: string) => void
onClipRemove: (clipId: string) => void
onClipAdd: () => void
}
const RightPanel: React.FC<RightPanelProps> = ({
titleConfig,
onTitleConfigChange,
rightTab,
onTabChange,
selectedClip,
subtitleSettings,
bgmSettings,
clipsCount,
totalDuration,
currentMode,
onSubtitleSettingsChange,
onBgmSettingsChange,
onClipUpdate,
onOpenBgmDrawer,
onOpenSubtitleDrawer,
voiceMaterials,
voiceMaterialsLoading,
onRefreshVoiceMaterials,
onClipVoiceSelect,
onOpenTransitionDrawer,
onOpenSpeedDrawer,
onOpenTtsDrawer,
onOpenWatermarkDrawer,
onOpenIntroOutroDrawer,
onOpenPipDrawer,
onOpenFilterDrawer,
onOpenGreenScreenDrawer,
onOpenStickerDrawer,
clips,
selectedClipId,
onClipSelect,
onClipMoveUp,
onClipMoveDown,
onClipRemove,
onClipAdd,
}) => {
return (
<div className="ep-right-panel">
{/* Tab 切换 */}
<div className="ep-right-tabs">
<button
className={`ep-right-tab${rightTab === "properties" ? " active" : ""}`}
onClick={() => onTabChange("properties")}
>
</button>
<button
className={`ep-right-tab${rightTab === "clips" ? " active" : ""}`}
onClick={() => onTabChange("clips")}
>
</button>
</div>
{/* 属性 Tab */}
{rightTab === "properties" && (
<div className="ep-right-tab-content">
{(() => {
// ClipPropertiesPanel 内部类型与主文件类型结构一致但字段细节不同
// 使用 unknown 作为中间类型避免 any 警告
const sub = subtitleSettings as unknown as React.ComponentProps<
typeof ClipPropertiesPanel
>["subtitleSettings"]
const bgm = bgmSettings as unknown as React.ComponentProps<
typeof ClipPropertiesPanel
>["bgmSettings"]
const onSubChange = onSubtitleSettingsChange as unknown as React.ComponentProps<
typeof ClipPropertiesPanel
>["onSubtitleSettingsChange"]
const onBgmChange = onBgmSettingsChange as unknown as React.ComponentProps<
typeof ClipPropertiesPanel
>["onBgmSettingsChange"]
return (
<ClipPropertiesPanel
titleConfig={titleConfig}
onTitleConfigChange={onTitleConfigChange}
selectedClip={selectedClip}
subtitleSettings={sub}
bgmSettings={bgm}
clipsCount={clipsCount}
totalDuration={totalDuration}
currentMode={currentMode}
onSubtitleSettingsChange={onSubChange}
onBgmSettingsChange={onBgmChange}
onClipUpdate={onClipUpdate}
onOpenBgmDrawer={onOpenBgmDrawer}
onOpenSubtitleDrawer={onOpenSubtitleDrawer}
voiceMaterials={voiceMaterials}
voiceMaterialsLoading={voiceMaterialsLoading}
onRefreshVoiceMaterials={onRefreshVoiceMaterials}
onClipVoiceSelect={onClipVoiceSelect}
onOpenTransitionDrawer={onOpenTransitionDrawer}
onOpenSpeedDrawer={onOpenSpeedDrawer}
onOpenTtsDrawer={onOpenTtsDrawer}
onOpenWatermarkDrawer={onOpenWatermarkDrawer}
onOpenIntroOutroDrawer={onOpenIntroOutroDrawer}
onOpenPipDrawer={onOpenPipDrawer}
onOpenFilterDrawer={onOpenFilterDrawer}
onOpenGreenScreenDrawer={onOpenGreenScreenDrawer}
onOpenStickerDrawer={onOpenStickerDrawer}
/>
)
})()}
</div>
)}
{/* 片段 Tab */}
{rightTab === "clips" && (
<div className="ep-right-tab-content">
<EditorClipList
clips={clips}
selectedClipId={selectedClipId}
onSelect={onClipSelect}
onMoveUp={onClipMoveUp}
onMoveDown={onClipMoveDown}
onRemove={onClipRemove}
onAdd={onClipAdd}
/>
</div>
)}
</div>
)
}
export default RightPanel
@@ -1,86 +0,0 @@
/**
* 保存/更新模板弹窗 — V21 设计系统
* 分类使用 Select 关联后端分类 APIP1-5
*/
import React from "react"
import { Modal, Input, Select } from "@/components/ui"
import type { TemplateCategory } from "@/api/editing-planner"
interface SaveModalProps {
open: boolean
loading: boolean
isUpdate: boolean
draftName: string
draftCategory: string
draftTags: string
categories: TemplateCategory[]
estimatedDuration: number
onNameChange: (v: string) => void
onCategoryChange: (v: string) => void
onTagsChange: (v: string) => void
onSave: () => void
onCancel: () => void
}
const SaveModal: React.FC<SaveModalProps> = ({
open,
loading,
isUpdate,
draftName,
draftCategory,
draftTags,
categories,
estimatedDuration,
onNameChange,
onCategoryChange,
onTagsChange,
onSave,
onCancel,
}) => {
return (
<Modal
title={isUpdate ? "更新模板" : "保存模板"}
open={open}
onCancel={onCancel}
onOk={onSave}
confirmLoading={loading}
okText="保存"
>
<div className="ep-modal-field">
<label> *</label>
<Input
placeholder="输入模板名称"
value={draftName}
onChange={(e) => onNameChange(e.target.value)}
/>
</div>
<div className="ep-modal-field">
<label></label>
<Select
placeholder="选择分类"
value={draftCategory || undefined}
onChange={(v: string) => onCategoryChange(v || "")}
allowClear
showSearch
options={categories.map((c) => ({ value: c.name, label: c.name }))}
/>
</div>
<div className="ep-modal-field">
<label></label>
<Input
placeholder="例如:vlog, 日常"
value={draftTags}
onChange={(e) => onTagsChange(e.target.value)}
/>
</div>
<div className="ep-modal-info">
<strong>~{estimatedDuration}s</strong>
</div>
</Modal>
)
}
export default SaveModal
@@ -1,152 +0,0 @@
/**
* 片段调速面板 — Drawer 形式
* 速度滑块(0.25x ~ 4x)+ 预设快捷按钮 + 音调修正开关
* 支持应用到当前片段 / 所有片段
*/
import React, { useCallback } from "react"
import { Drawer, Slider } from "antd"
import type { SpeedConfig } from "../types"
import { DEFAULT_SPEED } from "../types"
/* ──────────── 预设速度 ──────────── */
const SPEED_PRESETS: { rate: number; label: string }[] = [
{ rate: 0.5, label: "0.5x" },
{ rate: 1.0, label: "1x" },
{ rate: 1.5, label: "1.5x" },
{ rate: 2.0, label: "2x" },
]
/* ──────────── Props ──────────── */
interface SpeedPanelProps {
open: boolean
onClose: () => void
/** 当前片段调速配置 */
config: SpeedConfig
onChange: (config: SpeedConfig) => void
/** 应用到所有片段 */
onApplyAll?: (config: SpeedConfig) => void
}
const SpeedPanel: React.FC<SpeedPanelProps> = ({ open, onClose, config, onChange, onApplyAll }) => {
/* ── 修改速度 ── */
const handleChangeRate = useCallback(
(rate: number) => {
onChange({ ...config, rate })
},
[config, onChange],
)
/* ── 切换音调修正 ── */
const handleTogglePitch = useCallback(() => {
onChange({ ...config, pitchCorrection: !config.pitchCorrection })
}, [config, onChange])
/* ── 选择预设 ── */
const handlePreset = useCallback(
(rate: number) => {
onChange({ ...config, rate })
},
[config, onChange],
)
/* ── 应用到所有片段 ── */
const handleApplyAll = useCallback(() => {
onApplyAll?.(config)
}, [config, onApplyAll])
/* ── 重置 ── */
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_SPEED })
}, [onChange])
/* ── 速度描述文字 ── */
const speedLabel =
config.rate < 1
? "慢速(慢动作)"
: config.rate === 1
? "原速"
: config.rate < 2
? "快速"
: "极速"
return (
<Drawer
title="⚡ 片段调速"
placement="right"
width={380}
styles={{ wrapper: { maxWidth: "100vw" } }}
open={open}
onClose={onClose}
className="speed-panel-drawer"
>
{/* ── 速度滑块 ── */}
<div className="sp-speed-section">
<div className="sp-speed-header">
<span className="sp-speed-label"></span>
<span className="sp-speed-value">{config.rate.toFixed(2)}x</span>
</div>
<Slider
min={0.25}
max={4.0}
step={0.05}
value={config.rate}
onChange={handleChangeRate}
tooltip={{ formatter: (v) => `${(v as number).toFixed(2)}x` }}
/>
<div className="sp-speed-marks">
<span>0.25x</span>
<span>1x</span>
<span>2x</span>
<span>4x</span>
</div>
<div className="sp-speed-desc">{speedLabel}</div>
</div>
{/* ── 预设快捷按钮 ── */}
<div className="sp-presets">
<div className="sp-presets-label"></div>
<div className="sp-presets-row">
{SPEED_PRESETS.map((p) => (
<button
key={p.rate}
className={`sp-preset-btn${Math.abs(config.rate - p.rate) < 0.01 ? " active" : ""}`}
onClick={() => handlePreset(p.rate)}
>
{p.label}
</button>
))}
</div>
</div>
{/* ── 音调修正开关 ── */}
<div className="sp-pitch-section">
<div className="sp-pitch-info">
<span className="sp-pitch-label"></span>
<span className="sp-pitch-desc">
{config.pitchCorrection ? "变速不变调(推荐)" : "变速同时变调"}
</span>
</div>
<div
className={`ep-toggle${config.pitchCorrection ? " active" : ""}`}
onClick={handleTogglePitch}
>
<div className="ep-toggle-knob" />
</div>
</div>
{/* ── 底部操作 ── */}
<div className="sp-footer">
<button className="sp-reset-btn" onClick={handleReset}>
</button>
{onApplyAll && (
<button className="sp-apply-all-btn" onClick={handleApplyAll}>
</button>
)}
</div>
</Drawer>
)
}
export default SpeedPanel
@@ -1,32 +0,0 @@
import React from "react"
interface StatusBarProps {
clipsCount: number
totalDuration: number
currentModeLabel: string
templateSegments: number
}
const StatusBar: React.FC<StatusBarProps> = ({
clipsCount,
totalDuration,
currentModeLabel,
templateSegments,
}) => {
return (
<div className="ep-status-bar">
<div className="ep-status-left">
<span>📋 : {clipsCount}</span>
<span className="ep-status-sep">|</span>
<span> : {totalDuration.toFixed(1)}s</span>
</div>
<div className="ep-status-right">
<span>🎬 {currentModeLabel}</span>
<span className="ep-status-sep">|</span>
<span>📐 : {templateSegments}</span>
</div>
</div>
)
}
export default StatusBar
@@ -1,57 +0,0 @@
import React from "react"
import type { StickerItem } from "../../../types"
export interface StickerListProps {
items: StickerItem[]
selectedId: string | null
onSelect: (id: string) => void
onRemove: (id: string) => void
}
export const StickerList: React.FC<StickerListProps> = ({
items,
selectedId,
onSelect,
onRemove,
}) => {
if (items.length === 0) return null
const getStickerDisplay = (item: StickerItem) => {
if (item.type === "emoji") return item.content
if (item.type === "text") return item.content.slice(0, 10)
return "🖼"
}
const getStickerName = (item: StickerItem) => {
if (item.type === "text") return item.content.slice(0, 10)
if (item.type === "emoji") return "表情贴纸"
return "图片贴纸"
}
return (
<div className="sticker-list-section">
<div className="sticker-section-title"> ({items.length})</div>
<div className="sticker-list">
{items.map((item) => (
<div
key={item.id}
className={`sticker-list-item${selectedId === item.id ? " active" : ""}`}
onClick={() => onSelect(item.id)}
>
<span className="sticker-list-icon">{getStickerDisplay(item)}</span>
<span className="sticker-list-name">{getStickerName(item)}</span>
<button
className="sticker-list-delete"
onClick={(e) => {
e.stopPropagation()
onRemove(item.id)
}}
>
</button>
</div>
))}
</div>
</div>
)
}
@@ -1,203 +0,0 @@
import React from "react"
import type { StickerItem, TextStickerPreset } from "../../../types"
import { TEXT_STICKER_PRESET_LABELS } from "../../../types"
import { TEXT_PRESET_STYLES } from "../constants"
export interface StickerPropsEditorProps {
sticker: StickerItem
totalDuration: number
onChange: (id: string, partial: Partial<StickerItem>) => void
}
export const StickerPropsEditor: React.FC<StickerPropsEditorProps> = ({
sticker,
totalDuration,
onChange,
}) => {
const update = (partial: Partial<StickerItem>) => {
onChange(sticker.id, partial)
}
return (
<div className="sticker-props-section">
<div className="sticker-section-title"></div>
{/* 位置 X */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"> X</span>
<input
type="range"
className="sticker-prop-slider"
min={0}
max={100}
value={sticker.x}
onChange={(e) => update({ x: Number(e.target.value) })}
/>
<span className="sticker-prop-value">{sticker.x}%</span>
</div>
{/* 位置 Y */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"> Y</span>
<input
type="range"
className="sticker-prop-slider"
min={0}
max={100}
value={sticker.y}
onChange={(e) => update({ y: Number(e.target.value) })}
/>
<span className="sticker-prop-value">{sticker.y}%</span>
</div>
{/* 尺寸 */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="range"
className="sticker-prop-slider"
min={5}
max={50}
value={sticker.width}
onChange={(e) =>
update({
width: Number(e.target.value),
height: Number(e.target.value),
})
}
/>
<span className="sticker-prop-value">{sticker.width}%</span>
</div>
{/* 旋转 */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="range"
className="sticker-prop-slider"
min={-180}
max={180}
value={sticker.rotation}
onChange={(e) => update({ rotation: Number(e.target.value) })}
/>
<span className="sticker-prop-value">{sticker.rotation}°</span>
</div>
{/* 透明度 */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="range"
className="sticker-prop-slider"
min={0}
max={100}
value={sticker.opacity}
onChange={(e) => update({ opacity: Number(e.target.value) })}
/>
<span className="sticker-prop-value">{sticker.opacity}%</span>
</div>
{/* 时间 */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="number"
className="sticker-prop-number"
min={0}
max={totalDuration}
step={0.1}
value={sticker.start_time}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val >= 0 && val <= totalDuration) update({ start_time: val })
}}
/>
<span className="sticker-prop-label"></span>
<input
type="number"
className="sticker-prop-number"
min={0}
max={totalDuration}
step={0.1}
value={sticker.duration}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val > 0 && val <= totalDuration) update({ duration: val })
}}
/>
</div>
{/* 文字贴纸特有属性 */}
{sticker.type === "text" && (
<>
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<select
className="sticker-prop-select"
value={sticker.text_preset}
onChange={(e) => update({ text_preset: e.target.value as TextStickerPreset })}
>
{(Object.keys(TEXT_STICKER_PRESET_LABELS) as TextStickerPreset[]).map((p) => (
<option key={p} value={p}>
{TEXT_STICKER_PRESET_LABELS[p]}
</option>
))}
</select>
</div>
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="range"
className="sticker-prop-slider"
min={12}
max={72}
value={sticker.font_size}
onChange={(e) => update({ font_size: Number(e.target.value) })}
/>
<span className="sticker-prop-value">{sticker.font_size}px</span>
</div>
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="color"
className="sticker-prop-color"
value={sticker.text_color}
onChange={(e) => update({ text_color: e.target.value })}
/>
</div>
</>
)}
{/* 预览 */}
<div className="sticker-preview-box">
<div
className="sticker-preview-item"
style={{
left: `${sticker.x}%`,
top: `${sticker.y}%`,
width: `${sticker.width}%`,
height: `${sticker.width}%`,
transform: `translate(-50%, -50%) rotate(${sticker.rotation}deg)`,
opacity: sticker.opacity / 100,
fontSize: sticker.type === "text" ? `${sticker.font_size}px` : undefined,
...TEXT_PRESET_STYLES[sticker.text_preset],
}}
>
{sticker.type === "emoji" && sticker.content}
{sticker.type === "text" && sticker.content}
{sticker.type === "image" && (
<img
src={sticker.content}
alt="sticker"
style={{
width: "100%",
height: "100%",
objectFit: "contain",
}}
/>
)}
</div>
</div>
</div>
)
}
@@ -1,136 +0,0 @@
import React, { useState } from "react"
import type { StickerType, TextStickerPreset } from "../../../types"
import { TEXT_STICKER_PRESET_LABELS } from "../../../types"
import { EMOJI_LIST, TEXT_PRESET_STYLES } from "../constants"
export interface StickerTabsProps {
activeTab: StickerType
onTabChange: (tab: StickerType) => void
textInput: string
onTextInputChange: (val: string) => void
onAddEmoji: (emoji: string) => void
onAddImage: (url: string) => void
onAddText: (text: string) => void
}
export const StickerTabs: React.FC<StickerTabsProps> = ({
activeTab,
onTabChange,
textInput,
onTextInputChange,
onAddEmoji,
onAddImage,
onAddText,
}) => {
const [imageUrl, setImageUrl] = useState("")
const handleImageAdd = () => {
if (imageUrl.trim()) {
onAddImage(imageUrl.trim())
setImageUrl("")
}
}
const handleTextAdd = () => {
if (textInput.trim()) {
onAddText(textInput.trim())
}
}
return (
<>
{/* 类型 Tab */}
<div className="sticker-tabs">
{(["emoji", "image", "text"] as StickerType[]).map((t) => (
<button
key={t}
className={`sticker-tab${activeTab === t ? " active" : ""}`}
onClick={() => onTabChange(t)}
>
{t === "emoji" ? "表情贴纸" : t === "image" ? "图片贴纸" : "文字花字"}
</button>
))}
</div>
{/* Tab 内容区 */}
<div className="sticker-tab-content">
{/* Emoji 素材库 */}
{activeTab === "emoji" && (
<div className="sticker-emoji-grid">
{EMOJI_LIST.map((emoji) => (
<button key={emoji} className="sticker-emoji-btn" onClick={() => onAddEmoji(emoji)}>
{emoji}
</button>
))}
</div>
)}
{/* 图片贴纸 */}
{activeTab === "image" && (
<div className="sticker-image-input">
<input
type="text"
className="sticker-url-input"
placeholder="输入图片 URL 添加贴纸..."
value={imageUrl}
onChange={(e) => setImageUrl(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter" && imageUrl.trim()) {
onAddImage(imageUrl.trim())
setImageUrl("")
}
}}
/>
<button className="sticker-url-add-btn" onClick={handleImageAdd}>
</button>
</div>
)}
{/* 文字花字 */}
{activeTab === "text" && (
<div className="sticker-text-section">
<div className="sticker-text-input-row">
<input
type="text"
className="sticker-text-input"
placeholder="输入文字内容..."
value={textInput}
onChange={(e) => onTextInputChange(e.target.value)}
/>
<button
className="sticker-text-add-btn"
disabled={!textInput.trim()}
onClick={handleTextAdd}
>
</button>
</div>
<div className="sticker-text-presets">
<div className="sticker-preset-title"></div>
<div className="sticker-preset-grid">
{(Object.keys(TEXT_STICKER_PRESET_LABELS) as TextStickerPreset[]).map((p) => (
<div
key={p}
className="sticker-preset-preview"
style={{
background:
p === "bubble"
? "rgba(0,0,0,0.5)"
: p === "gradient"
? "linear-gradient(90deg,#f093fb,#f5576c)"
: "#1a1a2e",
}}
>
<span style={TEXT_PRESET_STYLES[p]}></span>
<div className="sticker-preset-name">{TEXT_STICKER_PRESET_LABELS[p]}</div>
</div>
))}
</div>
</div>
</div>
)}
</div>
</>
)
}
@@ -1,54 +0,0 @@
import type { CSSProperties } from "react"
import type { TextStickerPreset } from "../../types"
/** 常用 emoji 素材 */
export const EMOJI_LIST = [
"😀",
"😂",
"🥰",
"😎",
"🤩",
"😱",
"🤔",
"😴",
"🥳",
"😍",
"❤️",
"🔥",
"⭐",
"✨",
"💯",
"👍",
"👏",
"🎉",
"🎵",
"💪",
"📌",
"💡",
"🎯",
"✅",
"❌",
"⬆️",
"⬇️",
"➡️",
"⭕",
"🔔",
]
/** 文字花字预设对应的 CSS 样式预览 */
export const TEXT_PRESET_STYLES: Record<TextStickerPreset, CSSProperties> = {
normal: { color: "#fff", textShadow: "none" },
highlight: { color: "#FFD700", textShadow: "0 0 8px rgba(255,215,0,0.6)" },
bubble: { color: "#fff", background: "rgba(0,0,0,0.5)", borderRadius: 8 },
neon: { color: "#0ff", textShadow: "0 0 6px #0ff, 0 0 12px #0ff" },
shadow: { color: "#fff", textShadow: "2px 2px 4px rgba(0,0,0,0.8)" },
outline: { color: "#fff", WebkitTextStroke: "1px #000" },
gradient: {
color: "transparent",
background: "linear-gradient(90deg,#f093fb,#f5576c)",
WebkitBackgroundClip: "text",
},
handwrite: { color: "#333", fontStyle: "italic", fontFamily: "cursive" },
}
export const genStickerId = () => `sticker_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
@@ -1,85 +0,0 @@
import { useState, useCallback, useMemo } from "react"
import type { StickerConfig, StickerItem, StickerType } from "../../../types"
import { DEFAULT_STICKER_CONFIG, DEFAULT_STICKER_ITEM } from "../../../types"
import { genStickerId } from "../constants"
interface UseStickerPanelOptions {
config: StickerConfig
onChange: (config: StickerConfig) => void
totalDuration: number
}
export const useStickerPanel = ({ config, onChange, totalDuration }: UseStickerPanelOptions) => {
const [selectedId, setSelectedId] = useState<string | null>(null)
const [activeTab, setActiveTab] = useState<StickerType>("emoji")
const [textInput, setTextInput] = useState("")
const selectedSticker = useMemo(
() => config.items.find((s) => s.id === selectedId) ?? null,
[config.items, selectedId],
)
/** 更新单个贴纸 */
const updateItem = useCallback(
(id: string, partial: Partial<StickerItem>) => {
onChange({
...config,
items: config.items.map((s) => (s.id === id ? { ...s, ...partial } : s)),
})
},
[config, onChange],
)
/** 添加贴纸 */
const addSticker = useCallback(
(type: StickerType, content: string) => {
const newItem: StickerItem = {
...DEFAULT_STICKER_ITEM,
id: genStickerId(),
type,
content,
duration: totalDuration > 0 ? totalDuration : 5,
z_index: config.items.length > 0 ? Math.max(...config.items.map((i) => i.z_index)) + 1 : 1,
}
onChange({
...config,
enabled: true,
items: [...config.items, newItem],
})
setSelectedId(newItem.id)
},
[config, onChange, totalDuration],
)
/** 删除贴纸 */
const removeSticker = useCallback(
(id: string) => {
onChange({
...config,
items: config.items.filter((s) => s.id !== id),
})
if (selectedId === id) setSelectedId(null)
},
[config, onChange, selectedId],
)
/** 重置所有 */
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_STICKER_CONFIG, enabled: config.enabled })
setSelectedId(null)
}, [config.enabled, onChange])
return {
selectedId,
setSelectedId,
selectedSticker,
activeTab,
setActiveTab,
textInput,
setTextInput,
updateItem,
addSticker,
removeSticker,
handleReset,
}
}
@@ -1,101 +0,0 @@
import React from "react"
import { Drawer, Switch } from "antd"
import type { StickerConfig } from "../../types"
import { useStickerPanel } from "./hooks/useStickerPanel"
import { StickerTabs } from "./components/StickerTabs"
import { StickerList } from "./components/StickerList"
import { StickerPropsEditor } from "./components/StickerPropsEditor"
export interface StickerPanelProps {
open: boolean
onClose: () => void
config: StickerConfig
onChange: (config: StickerConfig) => void
totalDuration: number
}
const StickerPanel: React.FC<StickerPanelProps> = ({
open,
onClose,
config,
onChange,
totalDuration,
}) => {
const {
selectedId,
setSelectedId,
selectedSticker,
activeTab,
setActiveTab,
textInput,
setTextInput,
updateItem,
addSticker,
removeSticker,
} = useStickerPanel({ config, onChange, totalDuration })
const handleEmojiAdd = (emoji: string) => {
addSticker("emoji", emoji)
}
const handleImageAdd = (url: string) => {
addSticker("image", url)
}
const handleTextAdd = (text: string) => {
addSticker("text", text)
setTextInput("")
}
return (
<Drawer
title="贴纸"
placement="right"
width={460}
open={open}
onClose={onClose}
className="sticker-panel-drawer"
>
{/* 顶部开关 */}
<div className="sticker-header">
<span className="sticker-header-label"></span>
<Switch
size="small"
checked={config.enabled}
onChange={(checked) => onChange({ ...config, enabled: checked })}
/>
</div>
{/* Tab 选择器 + 内容 */}
<StickerTabs
activeTab={activeTab}
onTabChange={setActiveTab}
textInput={textInput}
onTextInputChange={setTextInput}
onAddEmoji={handleEmojiAdd}
onAddImage={handleImageAdd}
onAddText={handleTextAdd}
/>
{/* 已添加贴纸列表 */}
<StickerList
items={config.items}
selectedId={selectedId}
onSelect={setSelectedId}
onRemove={removeSticker}
/>
{/* 选中贴纸的属性编辑 */}
{selectedSticker && (
<StickerPropsEditor
sticker={selectedSticker}
totalDuration={totalDuration}
onChange={updateItem}
/>
)}
</Drawer>
)
}
export default StickerPanel
@@ -1,147 +0,0 @@
import React from "react"
import { Drawer, Slider, ColorPicker, Select } from "antd"
import type { Color } from "antd/es/color-picker"
import type { SubtitleStyleConfig } from "@/pages/editing-planner/types/subtitle"
import {
FONT_OPTIONS,
ANIMATION_OPTIONS,
ASR_LANGUAGE_OPTIONS,
} from "@/pages/editing-planner/constants/subtitleStyle"
import SubtitlePreview from "./subtitle-style/SubtitlePreview"
import { SubtitleModeSwitch } from "./subtitle-style/SubtitleModeSwitch"
import { SubtitlePositionSelector } from "./subtitle-style/SubtitlePositionSelector"
import { SubtitleEffectButtons } from "./subtitle-style/SubtitleEffectButtons"
interface SubtitleStylePanelProps {
open: boolean
onClose: () => void
config: SubtitleStyleConfig
onChange: (config: SubtitleStyleConfig) => void
}
const SubtitleStylePanel: React.FC<SubtitleStylePanelProps> = ({
open,
onClose,
config,
onChange,
}) => {
const update = (partial: Partial<SubtitleStyleConfig>) => {
onChange({ ...config, ...partial })
}
return (
<Drawer
title="💬 字幕样式配置"
placement="right"
width={380}
styles={{ wrapper: { maxWidth: "100vw" } }}
open={open}
onClose={onClose}
className="subtitle-style-drawer"
>
<div className="sub-field">
<div className="sub-toggle-row">
<span className="sub-label"></span>
<div
className={`ep-toggle${config.enabled ? " active" : ""}`}
onClick={() => update({ enabled: !config.enabled })}
>
<div className="ep-toggle-knob" />
</div>
</div>
</div>
{config.enabled && (
<>
<div className="sub-field">
<label className="sub-label"></label>
<SubtitleModeSwitch mode={config.mode} onModeChange={(mode) => update({ mode })} />
</div>
{config.mode === "asr" && (
<div className="sub-field">
<label className="sub-label"></label>
<Select
className="sub-select"
value={config.asrLanguage}
onChange={(v) => update({ asrLanguage: v })}
options={ASR_LANGUAGE_OPTIONS}
popupMatchSelectWidth={false}
/>
</div>
)}
<div className="sub-field">
<label className="sub-label">
<span className="sub-value">{config.fontSize}px</span>
</label>
<Slider
min={12}
max={48}
value={config.fontSize}
onChange={(v) => update({ fontSize: v })}
/>
</div>
<div className="sub-field">
<label className="sub-label"></label>
<div className="sub-color-row">
<ColorPicker
value={config.fontColor}
onChange={(_color: Color, hex: string) => update({ fontColor: hex })}
showText
/>
</div>
</div>
<div className="sub-field">
<label className="sub-label"></label>
<Select
className="sub-select"
value={config.font}
onChange={(v) => update({ font: v })}
options={FONT_OPTIONS.map((f) => ({ value: f, label: f }))}
popupMatchSelectWidth={false}
/>
</div>
<div className="sub-field">
<label className="sub-label"></label>
<SubtitlePositionSelector
position={config.position}
onPositionChange={(position) => update({ position })}
/>
</div>
<div className="sub-field">
<label className="sub-label"></label>
<SubtitleEffectButtons
stroke={config.stroke}
shadow={config.shadow}
onStrokeChange={(stroke) => update({ stroke })}
onShadowChange={(shadow) => update({ shadow })}
/>
</div>
<div className="sub-field">
<label className="sub-label"></label>
<Select
className="sub-select"
value={config.animation}
onChange={(v) => update({ animation: v })}
options={ANIMATION_OPTIONS.map((o) => ({
value: o.value,
label: o.label,
}))}
popupMatchSelectWidth={false}
/>
</div>
<SubtitlePreview config={config} />
</>
)}
</Drawer>
)
}
export default SubtitleStylePanel
@@ -1,200 +0,0 @@
/**
* 水平轨道时间线 — 支持裁剪手柄、分割、右键菜单
* 时间标尺 + 片段轨道 + HTML5拖拽排序
*
* 子组件(timeline/):
* - TimeRuler - 时间标尺
* - ClipCard - 片段卡片
* - ClipTrack - 片段轨道(播放头+片段列表+添加卡片)
* - TimelineHeader - 时间线头部(标题+缩放+操作按钮)
* - TrimPreview - 裁剪预览 tooltip
* - ContextMenu - 右键菜单
*
* Hooks
* - useClipDrag - 片段拖拽排序
* - useTrimDrag - 裁剪拖拽
* - useTimelineMenus - 菜单 & 面板
* - usePlayheadDrag - 播放头拖拽
*/
import React from "react"
import type { ClipData, ClipType, TrimConfig } from "../types"
import { DEFAULT_PIXELS_PER_SECOND } from "../constants/timeline"
import { useClipDrag } from "../hooks/useClipDrag"
import { useTrimDrag } from "../hooks/useTrimDrag"
import { useTimelineMenus } from "../hooks/useTimelineMenus"
import { usePlayheadDrag } from "./timeline/hooks/usePlayheadDrag"
import { TimeRuler } from "./timeline/TimeRuler"
import { ClipTrack } from "./timeline/ClipTrack"
import { TimelineHeader } from "./timeline/TimelineHeader"
import { TrimPreview } from "./timeline/TrimPreview"
import { ContextMenu } from "./timeline/ContextMenu"
interface TimelinePanelProps {
clips: ClipData[]
selectedClipId: string | null
currentMode: string
onClipSelect: (clipId: string) => void
onClipReorder: (fromIdx: number, toIdx: number) => void
onClipRemove: (clipId: string) => void
onAddClip: (type: ClipType, duration: number) => void
/** 裁剪更新:调整片段的 trim_config 和 duration */
onClipTrim?: (clipId: string, trimConfig: TrimConfig, newDuration: number) => void
/** 在指定位置分割片段 */
onClipSplit?: (clipId: string, splitRatio: number) => void
/** 恢复片段原始长度 */
onClipResetTrim?: (clipId: string) => void
/** 当前播放时间(秒) */
currentTime?: number
/** 缩放:每秒像素数 */
pixelsPerSecond?: number
/** 缩放变更回调 */
onZoomChange?: (pps: number) => void
/** 播放头跳转回调 */
onSeek?: (time: number) => void
/** 总时长(秒),可选(默认由 clips 计算) */
totalDuration?: number
}
const TimelinePanel: React.FC<TimelinePanelProps> = ({
clips,
selectedClipId,
currentMode,
onClipSelect,
onClipReorder,
onClipRemove,
onAddClip,
onClipTrim,
onClipSplit,
onClipResetTrim,
currentTime = 0,
pixelsPerSecond = 40,
onZoomChange,
onSeek,
totalDuration: totalDurationProp,
}) => {
/* ── 缩放 & 时长 ── */
const pps = pixelsPerSecond ?? DEFAULT_PIXELS_PER_SECOND
const totalDuration = totalDurationProp ?? clips.reduce((s, c) => s + c.duration, 0)
/* ── 裁剪拖拽 ── */
const { trimDrag, trimPreview, handleTrimHandleMouseDown } = useTrimDrag(clips, pps, onClipTrim)
/* ── 片段拖拽排序 ── */
const {
dragIdx,
dragOverIdx,
handleDragStart,
handleDragOver,
handleDragEnd,
handleDrop,
handleEmptyDragOver,
} = useClipDrag(onClipReorder, !!trimDrag)
/* ── 菜单 & 面板 ── */
const {
contextMenu,
contextMenuRef,
handleContextMenu,
handleContextSplit,
handleContextResetTrim,
handleContextDelete,
addCardRef,
handleConfirmAdd,
hoveredClipId,
setHoveredClipId,
} = useTimelineMenus(clips, currentMode, onAddClip, onClipSplit, onClipResetTrim, onClipRemove)
/* ── 播放头拖拽 ── */
const { trackRef, handlePlayheadMouseDown, handleRulerClick } = usePlayheadDrag({
currentTime,
pps,
totalDuration,
onSeek,
})
/* ── 撤销最后一个片段 ── */
const handleUndoClip = () => {
if (clips.length > 1) {
const last = clips[clips.length - 1]
onClipRemove(last.id)
}
}
return (
<div className="ep-timeline-area">
{/* 时间线头部 */}
<TimelineHeader
totalDuration={totalDuration}
pps={pps}
onZoomChange={onZoomChange}
onUndoClip={handleUndoClip}
clipsCount={clips.length}
/>
{/* 一镜到底提示 */}
{currentMode === "one_take" && <div className="ep-one-take-hint">🎥 </div>}
{/* 时间标尺 */}
{currentMode !== "one_take" && (
<TimeRuler totalDuration={totalDuration} pps={pps} onClick={handleRulerClick} />
)}
{/* 水平片段轨道 */}
{currentMode !== "one_take" && (
<ClipTrack
clips={clips}
selectedClipId={selectedClipId}
pps={pps}
currentTime={currentTime}
totalDuration={totalDuration}
dragIdx={dragIdx}
dragOverIdx={dragOverIdx}
hoveredClipId={hoveredClipId}
trimDragActive={!!trimDrag}
showTrimHandles={!!onClipTrim}
trackRef={trackRef}
addCardRef={addCardRef}
onPlayheadMouseDown={handlePlayheadMouseDown}
onClipSelect={onClipSelect}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
onDrop={handleDrop}
onEmptyDragOver={handleEmptyDragOver}
onContextMenu={handleContextMenu}
onClipMouseEnter={setHoveredClipId}
onClipMouseLeave={() => setHoveredClipId(null)}
onTrimHandleMouseDown={handleTrimHandleMouseDown}
onClipRemove={onClipRemove}
onTogglePicker={handleConfirmAdd}
/>
)}
{/* 裁剪预览 tooltip */}
{trimPreview && (
<TrimPreview
startTime={trimPreview.startTime}
endTime={trimPreview.endTime}
duration={trimPreview.duration}
x={trimPreview.x}
y={trimPreview.y}
/>
)}
{/* 右键菜单 */}
{contextMenu && (
<ContextMenu
x={contextMenu.x}
y={contextMenu.y}
menuRef={contextMenuRef}
hasTrim={!!clips.find((c) => c.id === contextMenu.clipId)?.trim_config}
onSplit={handleContextSplit}
onResetTrim={handleContextResetTrim}
onDelete={handleContextDelete}
/>
)}
</div>
)
}
export default TimelinePanel
@@ -1,54 +0,0 @@
import React from "react"
import type { EditingTemplate } from "@/api/editing-planner"
interface TopBarProps {
currentTemplate: EditingTemplate | null
canUndo: boolean
canRedo: boolean
onUndo: () => void
onRedo: () => void
onOpenSaveModal: () => void
}
const TopBar: React.FC<TopBarProps> = ({
currentTemplate,
canUndo,
canRedo,
onUndo,
onRedo,
onOpenSaveModal,
}) => {
return (
<div className="ep-top-bar">
<div className="ep-top-bar-left">
<span className="ep-logo"></span>
<span className="ep-app-title"></span>
<span className="ep-divider">|</span>
<span className="ep-template-name">{currentTemplate?.name || "未选择模板"}</span>
</div>
<div className="ep-top-bar-right">
<button
className="ep-btn ep-btn-secondary"
onClick={onUndo}
disabled={!canUndo}
title="撤销 (Ctrl+Z)"
>
</button>
<button
className="ep-btn ep-btn-secondary"
onClick={onRedo}
disabled={!canRedo}
title="重做 (Ctrl+Shift+Z)"
>
</button>
<button className="ep-btn ep-btn-secondary" onClick={onOpenSaveModal}>
💾
</button>
</div>
</div>
)
}
export default TopBar
@@ -1,116 +0,0 @@
/**
* 转场特效选择器 — Drawer 形式
* 14 种转场预设卡片网格 + 转场时长滑块
* 支持全局默认转场 + 单个片段间独立设置
*/
import React, { useCallback } from "react"
import { Drawer, Slider } from "antd"
import { TRANSITION_OPTIONS } from "@/api/template-editor"
import type { TransitionConfig, TransitionType } from "../types"
import { DEFAULT_TRANSITION } from "../types"
/* ──────────── Props ──────────── */
interface TransitionSelectorProps {
open: boolean
onClose: () => void
/** 当前转场配置 */
config: TransitionConfig
onChange: (config: TransitionConfig) => void
/** 标题提示(区分全局 / 片段间) */
title?: string
}
const TransitionSelector: React.FC<TransitionSelectorProps> = ({
open,
onClose,
config,
onChange,
title = "转场特效",
}) => {
/* ── 选择转场类型 ── */
const handleSelectType = useCallback(
(type: TransitionType) => {
onChange({ ...config, type })
},
[config, onChange],
)
/* ── 修改时长 ── */
const handleChangeDuration = useCallback(
(duration: number) => {
onChange({ ...config, duration })
},
[config, onChange],
)
/* ── 重置为无转场 ── */
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_TRANSITION })
}, [onChange])
return (
<Drawer
title={`🎬 ${title}`}
placement="right"
width={480}
styles={{ wrapper: { maxWidth: "100vw" } }}
open={open}
onClose={onClose}
className="transition-selector-drawer"
>
{/* ── 时长滑块 ── */}
<div className="ts-duration-section">
<div className="ts-duration-header">
<span className="ts-duration-label"></span>
<span className="ts-duration-value">{config.duration.toFixed(1)}s</span>
</div>
<Slider
min={0.3}
max={2.0}
step={0.1}
value={config.duration}
onChange={handleChangeDuration}
tooltip={{ formatter: (v) => `${(v as number).toFixed(1)}s` }}
/>
<div className="ts-duration-marks">
<span>0.3s</span>
<span>1.0s</span>
<span>2.0s</span>
</div>
</div>
{/* ── 转场类型卡片网格 ── */}
<div className="ts-grid">
{TRANSITION_OPTIONS.map((opt) => {
const isActive = config.type === opt.value
return (
<div
key={opt.value}
className={`ts-card${isActive ? " active" : ""}`}
onClick={() => handleSelectType(opt.value)}
>
<div className="ts-card-icon">{opt.icon}</div>
<div className="ts-card-name">{opt.label}</div>
{isActive && <span className="ts-card-check"></span>}
</div>
)
})}
</div>
{/* ── 底部操作 ── */}
<div className="ts-footer">
<button className="ts-reset-btn" onClick={handleReset}>
</button>
<div className="ts-current">
{TRANSITION_OPTIONS.find((o) => o.value === config.type)?.label ?? "无转场"}
{" · "}
{config.duration.toFixed(1)}s
</div>
</div>
</Drawer>
)
}
export default TransitionSelector
@@ -1,48 +0,0 @@
import React from "react"
import { Slider } from "antd"
export interface TtsSliderProps {
label: string
value: number
min: number
max: number
step: number
onChange: (val: number) => void
formatter: (val: number) => string
marks?: string[]
}
export const TtsSlider: React.FC<TtsSliderProps> = ({
label,
value,
min,
max,
step,
onChange,
formatter,
marks,
}) => {
return (
<div className="tts-slider-section">
<div className="tts-slider-header">
<span className="tts-slider-label">{label}</span>
<span className="tts-slider-value">{formatter(value)}</span>
</div>
<Slider
min={min}
max={max}
step={step}
value={value}
onChange={onChange}
tooltip={{ formatter: (v) => (v != null ? formatter(v as number) : "") }}
/>
{marks && (
<div className="tts-slider-marks">
{marks.map((m, i) => (
<span key={i}>{m}</span>
))}
</div>
)}
</div>
)
}
@@ -1,107 +0,0 @@
import React, { useState } from "react"
import type { UnifiedVoiceItem } from "@/api/voices"
import { LoadingOutlined } from "@ant-design/icons"
export interface VoiceSelectorProps {
presetVoices: UnifiedVoiceItem[]
cloneVoices: UnifiedVoiceItem[]
selectedVoiceId: string
loading: boolean
playingVoiceId: string | null
loadingSampleVoiceId: string | null
onSelect: (voiceId: string) => void
onPlaySample: (voice: UnifiedVoiceItem) => void
}
type VoiceTab = "preset" | "clone"
const GENDER_ICON: Record<string, string> = {
male: "👨",
female: "👩",
young: "🧑",
}
export const VoiceSelector: React.FC<VoiceSelectorProps> = ({
presetVoices,
cloneVoices,
selectedVoiceId,
loading,
playingVoiceId,
loadingSampleVoiceId,
onSelect,
onPlaySample,
}) => {
const [activeTab, setActiveTab] = useState<VoiceTab>("preset")
const currentList = activeTab === "preset" ? presetVoices : cloneVoices
return (
<div className="tts-voice-section">
<div className="tts-voice-label">
{loading && <span className="tts-voice-loading">...</span>}
</div>
{/* Tab 切换 */}
<div className="tts-voice-tabs">
<button
className={`tts-voice-tab${activeTab === "preset" ? " active" : ""}`}
onClick={() => setActiveTab("preset")}
>
</button>
<button
className={`tts-voice-tab${activeTab === "clone" ? " active" : ""}`}
onClick={() => setActiveTab("clone")}
>
{cloneVoices.length > 0 && (
<span className="tts-voice-tab-count">{cloneVoices.length}</span>
)}
</button>
</div>
{/* 音色列表 */}
{loading ? (
<div className="tts-voice-empty"> ...</div>
) : currentList.length === 0 ? (
<div className="tts-voice-empty">
{activeTab === "preset" ? "暂无预设音色" : "暂无克隆音色,快去克隆一个吧"}
</div>
) : (
<div className="tts-voice-list">
{currentList.map((voice) => {
const isSelected = selectedVoiceId === voice.voice_id
const isPlaying = playingVoiceId === voice.voice_id
const isLoadingSample = loadingSampleVoiceId === voice.voice_id
const icon = GENDER_ICON[voice.gender] ?? "✨"
return (
<div
key={voice.voice_id}
className={`tts-voice-item${isSelected ? " selected" : ""}`}
onClick={() => onSelect(voice.voice_id)}
>
<div className="tts-voice-item-icon">{icon}</div>
<div className="tts-voice-item-info">
<div className="tts-voice-item-name">{voice.name}</div>
<div className="tts-voice-item-desc">{voice.description || voice.language}</div>
</div>
<button
className="tts-voice-item-play"
onClick={(e) => {
e.stopPropagation()
onPlaySample(voice)
}}
disabled={isLoadingSample}
title={isPlaying ? "暂停" : isLoadingSample ? "合成中" : "试听"}
>
{isLoadingSample ? <LoadingOutlined /> : isPlaying ? "⏸" : "▶"}
</button>
</div>
)
})}
</div>
)}
</div>
)
}
@@ -1,18 +0,0 @@
import type { TtsMode } from "../../types"
/** 音色卡片分类图标 */
export const VOICE_CATEGORY_MAP: Record<string, { icon: string; label: string }> = {
male: { icon: "👨", label: "男声" },
female: { icon: "👩", label: "女声" },
young: { icon: "🧑", label: "少年" },
service: { icon: "🎧", label: "客服" },
news: { icon: "📰", label: "新闻" },
emotion: { icon: "🎭", label: "情感" },
}
/** 配音模式选项 */
export const TTS_MODE_OPTIONS: { mode: TtsMode; icon: string; label: string }[] = [
{ mode: "none", icon: "🔇", label: "无配音" },
{ mode: "upload", icon: "📁", label: "上传配音" },
{ mode: "tts", icon: "🤖", label: "TTS 合成" },
]
@@ -1,271 +0,0 @@
import { useState, useCallback, useEffect, useRef, type ChangeEvent } from "react"
import { message } from "antd"
import type { TtsConfig, TtsMode } from "../../../types"
import { DEFAULT_TTS_CONFIG } from "../../../types"
import { previewTts } from "@/api/tts"
import { getVoiceClonePreview } from "@/api/voice-clone"
import { fetchVoices, type UnifiedVoiceItem } from "@/api/voices"
interface UseTtsPanelOptions {
open: boolean
config: TtsConfig
onChange: (config: TtsConfig) => void
onClose: () => void
}
export const useTtsPanel = ({ open, config, onChange, onClose }: UseTtsPanelOptions) => {
/* ── 音色列表(统一音色:预设 + 克隆) ── */
const [presetVoices, setPresetVoices] = useState<UnifiedVoiceItem[]>([])
const [cloneVoices, setCloneVoices] = useState<UnifiedVoiceItem[]>([])
const [voicesLoading, setVoicesLoading] = useState(false)
// 用 ref 保存最新 config 和 onChange,避免 useEffect 闭包陷阱
const configRef = useRef(config)
const onChangeRef = useRef(onChange)
configRef.current = config
onChangeRef.current = onChange
/* ── 试听状态 ── */
const [previewLoading, setPreviewLoading] = useState(false)
const audioRef = useRef<HTMLAudioElement | null>(null)
const mountedRef = useRef(true)
/* ── 加载音色列表 + 重置挂载状态 ── */
useEffect(() => {
if (!open) return
mountedRef.current = true
let active = true
setVoicesLoading(true)
fetchVoices({ status: "ready", limit: 100 })
.then((res) => {
if (!active) return
const presets = res.items.filter((v) => v.type === "preset")
const clones = res.items.filter((v) => v.type === "clone")
setPresetVoices(presets)
setCloneVoices(clones)
// 如果当前没有选中音色,默认选第一个预设(用 ref 读最新值,避免闭包旧值覆盖用户选择)
if (!configRef.current.voice_id && presets.length > 0) {
onChangeRef.current({
...configRef.current,
voice_id: presets[0].voice_id,
})
}
})
.catch(() => {
if (active) message.error("加载音色列表失败")
})
.finally(() => {
if (active) setVoicesLoading(false)
})
return () => {
active = false
}
}, [open])
/* ── 组件卸载时清理音频资源 ── */
useEffect(() => {
return () => {
audioRef.current?.pause()
audioRef.current = null
mountedRef.current = false
}
}, [])
/* ── 切换配音模式 ── */
const handleModeChange = useCallback(
(mode: TtsMode) => {
onChange({ ...config, mode })
},
[config, onChange],
)
/* ── 文本输入 ── */
const handleTextChange = useCallback(
(e: ChangeEvent<HTMLTextAreaElement>) => {
const text = e.target.value.slice(0, 5000)
onChange({ ...config, text })
},
[config, onChange],
)
/* ── 选择音色 ── */
const handleVoiceSelect = useCallback(
(voiceId: string) => {
onChange({ ...config, voice_id: voiceId })
},
[config, onChange],
)
/* ── 语速 ── */
const handleSpeedChange = useCallback(
(speed: number) => {
onChange({ ...config, speed })
},
[config, onChange],
)
/* ── 语调 ── */
const handlePitchChange = useCallback(
(pitch: number) => {
onChange({ ...config, pitch })
},
[config, onChange],
)
/* ── 音量 ── */
const handleVolumeChange = useCallback(
(volume: number) => {
onChange({ ...config, volume })
},
[config, onChange],
)
/* ── 字幕联动 ── */
const handleSubtitleSyncToggle = useCallback(() => {
onChange({ ...config, subtitle_sync: !config.subtitle_sync })
}, [config, onChange])
/* ── 试听 ── */
const handlePreview = useCallback(async () => {
if (!config.text.trim()) {
message.warning("请先输入合成文本")
return
}
if (!config.voice_id) {
message.warning("请先选择音色")
return
}
setPreviewLoading(true)
try {
const res = await previewTts({
text: config.text.slice(0, 200),
voice_id: config.voice_id,
speed: config.speed,
pitch: config.pitch,
})
if (!mountedRef.current) return
audioRef.current?.pause()
const audio = new Audio(res.audio_url)
audioRef.current = audio
audio
.play()
.then(() => {
if (mountedRef.current) message.success("试听播放中")
})
.catch(() => {
if (mountedRef.current) message.error("播放失败")
})
audio.onended = () => {
if (mountedRef.current) audioRef.current = null
}
} catch {
if (mountedRef.current) message.error("试听生成失败")
} finally {
if (mountedRef.current) setPreviewLoading(false)
}
}, [config])
/* ── 单音色示例试听 ── */
const [playingVoiceId, setPlayingVoiceId] = useState<string | null>(null)
const [loadingSampleVoiceId, setLoadingSampleVoiceId] = useState<string | null>(null)
const handlePlayVoiceSample = useCallback(
async (voice: UnifiedVoiceItem) => {
const voiceId = voice.voice_id
if (playingVoiceId === voiceId) {
audioRef.current?.pause()
audioRef.current = null
setPlayingVoiceId(null)
return
}
// 暂停上一个音频并清除事件监听,避免竞态
if (audioRef.current) {
audioRef.current.onended = null
audioRef.current.pause()
}
let audioUrl: string | null = voice.preview_url ?? voice.audio_url
// 克隆音色:调用试听接口实时合成
if (voice.type === "clone" && voice.voice_clone_profile_id) {
setLoadingSampleVoiceId(voiceId)
try {
const preview = await getVoiceClonePreview(voice.voice_clone_profile_id)
audioUrl = preview.audio_url
} catch {
if (mountedRef.current) {
message.error("试听音频生成失败,请重试")
}
setLoadingSampleVoiceId(null)
return
} finally {
if (mountedRef.current) {
setLoadingSampleVoiceId(null)
}
}
}
if (!audioUrl) {
message.warning("该音色暂无示例音频")
return
}
const audio = new Audio(audioUrl)
audioRef.current = audio
audio
.play()
.then(() => {
// 只有当前活跃的音频实例才能更新状态,防止快速切换竞态
if (mountedRef.current && audioRef.current === audio) {
setPlayingVoiceId(voiceId)
}
})
.catch(() => {
if (mountedRef.current && audioRef.current === audio) {
message.error("播放失败")
audioRef.current = null
}
})
audio.onended = () => {
if (mountedRef.current && audioRef.current === audio) {
setPlayingVoiceId(null)
audioRef.current = null
}
}
},
[playingVoiceId],
)
/* ── 重置 ── */
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_TTS_CONFIG })
}, [onChange])
/* ── 关闭时停止音频 ── */
const handleClose = useCallback(() => {
mountedRef.current = false
audioRef.current?.pause()
audioRef.current = null
setPlayingVoiceId(null)
onClose()
}, [onClose])
return {
presetVoices,
cloneVoices,
voicesLoading,
previewLoading,
playingVoiceId,
loadingSampleVoiceId,
handleModeChange,
handleTextChange,
handleVoiceSelect,
handlePlayVoiceSample,
handleSpeedChange,
handlePitchChange,
handleVolumeChange,
handleSubtitleSyncToggle,
handlePreview,
handleReset,
handleClose,
}
}
@@ -1,180 +0,0 @@
/**
* TTS 配音面板 — Drawer 形式
* 配音模式切换 + 文本输入 + 音色选择(预设+克隆) + 语速/语调/音量 + 试听 + 字幕联动
*/
import React from "react"
import { Drawer } from "antd"
import type { TtsConfig } from "../../types"
import { useTtsPanel } from "./hooks/useTtsPanel"
import { VoiceSelector } from "./components/VoiceSelector"
import { TtsSlider } from "./components/TtsSlider"
import { TTS_MODE_OPTIONS } from "./constants"
export interface TtsPanelProps {
open: boolean
onClose: () => void
/** 当前片段 TTS 配置 */
config: TtsConfig
onChange: (config: TtsConfig) => void
}
const TtsPanel: React.FC<TtsPanelProps> = ({ open, onClose, config, onChange }) => {
const {
presetVoices,
cloneVoices,
voicesLoading,
previewLoading,
playingVoiceId,
loadingSampleVoiceId,
handleModeChange,
handleTextChange,
handleVoiceSelect,
handlePlayVoiceSample,
handleSpeedChange,
handlePitchChange,
handleVolumeChange,
handleSubtitleSyncToggle,
handlePreview,
handleReset,
handleClose,
} = useTtsPanel({ open, config, onChange, onClose })
return (
<Drawer
title="🎙️ TTS 配音"
placement="right"
width={400}
open={open}
onClose={handleClose}
className="tts-panel-drawer"
>
{/* ── 配音模式切换 ── */}
<div className="tts-mode-section">
<div className="tts-mode-label"></div>
<div className="tts-mode-group">
{TTS_MODE_OPTIONS.map((m) => (
<button
key={m.mode}
className={`tts-mode-btn${config.mode === m.mode ? " active" : ""}`}
onClick={() => handleModeChange(m.mode)}
>
<span className="tts-mode-icon">{m.icon}</span>
<span className="tts-mode-text">{m.label}</span>
</button>
))}
</div>
</div>
{/* ── TTS 配置(仅 tts 模式显示) ── */}
{config.mode === "tts" && (
<>
{/* 文本输入 */}
<div className="tts-text-section">
<div className="tts-text-header">
<span className="tts-text-label"></span>
<span className="tts-text-count">{config.text.length}/5000</span>
</div>
<textarea
className="tts-text-input"
placeholder="请输入需要合成的文本内容..."
value={config.text}
onChange={handleTextChange}
maxLength={5000}
rows={5}
/>
</div>
{/* 音色选择 */}
<VoiceSelector
presetVoices={presetVoices}
cloneVoices={cloneVoices}
selectedVoiceId={config.voice_id}
loading={voicesLoading}
playingVoiceId={playingVoiceId}
loadingSampleVoiceId={loadingSampleVoiceId}
onSelect={handleVoiceSelect}
onPlaySample={handlePlayVoiceSample}
/>
{/* 语速 */}
<TtsSlider
label="语速"
value={config.speed}
min={0.5}
max={2.0}
step={0.05}
onChange={handleSpeedChange}
formatter={(v) => `${v.toFixed(2)}x`}
marks={["0.5x", "1.0x", "2.0x"]}
/>
{/* 语调 */}
<TtsSlider
label="语调"
value={config.pitch}
min={-12}
max={12}
step={1}
onChange={handlePitchChange}
formatter={(v) => `${v > 0 ? "+" : ""}${v} 半音`}
marks={["-12", "0", "+12"]}
/>
{/* 音量 */}
<TtsSlider
label="音量"
value={config.volume}
min={0}
max={100}
step={1}
onChange={handleVolumeChange}
formatter={(v) => `${v}%`}
/>
{/* 试听按钮 */}
<div className="tts-preview-section">
<button className="tts-preview-btn" onClick={handlePreview} disabled={previewLoading}>
{previewLoading ? "⏳ 生成中..." : "🔊 试听"}
</button>
</div>
{/* 字幕联动 */}
<div className="tts-subtitle-section">
<div className="tts-subtitle-info">
<span className="tts-subtitle-label"></span>
<span className="tts-subtitle-desc">
{config.subtitle_sync ? "TTS 文本自动同步到字幕" : "字幕需手动编辑"}
</span>
</div>
<div
className={`ep-toggle${config.subtitle_sync ? " active" : ""}`}
onClick={handleSubtitleSyncToggle}
>
<div className="ep-toggle-knob" />
</div>
</div>
</>
)}
{/* ── 上传配音模式提示 ── */}
{config.mode === "upload" && (
<div className="tts-upload-hint">
<p></p>
<p></p>
</div>
)}
{/* ── 底部操作 ── */}
{config.mode === "tts" && (
<div className="tts-footer">
<button className="tts-reset-btn" onClick={handleReset}>
</button>
</div>
)}
</Drawer>
)
}
export default TtsPanel
@@ -1,123 +0,0 @@
/**
* 水印配置面板 — Drawer 形式
* 三个 Tab:图片水印 / 文字水印 / 滚动水印
* 通用设置:位置、不透明度
*/
import React from "react"
import { Drawer } from "antd"
import type { WatermarkConfig } from "@/pages/editing-planner/types"
import WatermarkTypeTabs from "./watermark/WatermarkTypeTabs"
import ImageWatermarkSection from "./watermark/ImageWatermarkSection"
import TextWatermarkSection from "./watermark/TextWatermarkSection"
import ScrollWatermarkSection from "./watermark/ScrollWatermarkSection"
import WatermarkCommonSection from "./watermark/WatermarkCommonSection"
import { useWatermarkConfig } from "@/pages/editing-planner/hooks/useWatermarkConfig"
interface WatermarkPanelProps {
open: boolean
onClose: () => void
config: WatermarkConfig
onChange: (config: WatermarkConfig) => void
}
const WatermarkPanel: React.FC<WatermarkPanelProps> = ({ open, onClose, config, onChange }) => {
const {
localImageUrl,
handleTypeChange,
handlePositionChange,
handleOpacityChange,
handleImageUrlChange,
handleImageWidthChange,
handleImageHeightChange,
handleTextChange,
handleFontSizeChange,
handleColorChange,
handleScrollDirectionChange,
handleScrollSpeedChange,
handleReset,
} = useWatermarkConfig({ config, onChange })
return (
<Drawer
title="🔖 水印设置"
placement="right"
width={400}
styles={{ wrapper: { maxWidth: "100vw" } }}
open={open}
onClose={onClose}
className="watermark-panel-drawer"
>
{/* ── Tab 切换 ── */}
<WatermarkTypeTabs activeTab={config.type} onTypeChange={handleTypeChange} />
{/* ── 无水印提示 ── */}
{config.type === "none" && (
<div className="wp-empty-hint">
<span className="wp-empty-icon">🚫</span>
<p></p>
<p className="wp-empty-desc"></p>
</div>
)}
{/* ── 图片水印配置 ── */}
{config.type === "image" && (
<ImageWatermarkSection
imageUrl={config.image_url || ""}
localImageUrl={localImageUrl}
width={config.width}
height={config.height}
onImageUrlChange={handleImageUrlChange}
onWidthChange={handleImageWidthChange}
onHeightChange={handleImageHeightChange}
/>
)}
{/* ── 文字水印配置 ── */}
{config.type === "text" && (
<TextWatermarkSection
text={config.text || ""}
fontSize={config.font_size ?? 24}
color={config.color ?? "#ffffff"}
onTextChange={handleTextChange}
onFontSizeChange={handleFontSizeChange}
onColorChange={handleColorChange}
/>
)}
{/* ── 滚动水印配置 ── */}
{config.type === "scroll" && (
<ScrollWatermarkSection
text={config.text || ""}
scrollDirection={config.scroll_direction ?? "horizontal"}
scrollSpeed={config.scroll_speed ?? 50}
fontSize={config.font_size ?? 24}
color={config.color ?? "#ffffff"}
onTextChange={handleTextChange}
onScrollDirectionChange={handleScrollDirectionChange}
onScrollSpeedChange={handleScrollSpeedChange}
onFontSizeChange={handleFontSizeChange}
onColorChange={handleColorChange}
/>
)}
{/* ── 通用设置(非 none 时显示) ── */}
{config.type !== "none" && (
<WatermarkCommonSection
position={config.position}
opacity={config.opacity}
onPositionChange={handlePositionChange}
onOpacityChange={handleOpacityChange}
/>
)}
{/* ── 底部操作 ── */}
<div className="wp-footer">
<button className="wp-reset-btn" onClick={handleReset}>
</button>
</div>
</Drawer>
)
}
export default WatermarkPanel
@@ -1,66 +0,0 @@
import React from "react"
import type { BgmPreset } from "@/api/bgm"
interface BgmItemProps {
bgm: BgmPreset
isSelected: boolean
isPlaying: boolean
onSelect: () => void
onPreview: () => void
}
/**
* 单个 BGM 列表项组件
*/
export const BgmItem: React.FC<BgmItemProps> = ({
bgm,
isSelected,
isPlaying,
onSelect,
onPreview,
}) => {
const formatDuration = (seconds: number) => {
const mins = Math.floor(seconds / 60)
const secs = String(Math.floor(seconds % 60)).padStart(2, "0")
return `${mins}:${secs}`
}
return (
<div className={`bgm-item${isSelected ? " selected" : ""}`} onClick={onSelect}>
<div className="bgm-item-cover">
{bgm.cover_url ? (
<img src={bgm.cover_url} alt={bgm.name} />
) : (
<span className="bgm-item-cover-icon">🎵</span>
)}
</div>
<div className="bgm-item-info">
<div className="bgm-item-name">{bgm.name}</div>
<div className="bgm-item-meta">
<span className="bgm-item-category">{bgm.category}</span>
<span className="bgm-item-duration">{formatDuration(bgm.duration)}</span>
</div>
{bgm.tags.length > 0 && (
<div className="bgm-item-tags">
{bgm.tags.slice(0, 3).map((t) => (
<span key={t} className="bgm-item-tag">
{t}
</span>
))}
</div>
)}
</div>
<button
className={`bgm-item-preview-btn${isPlaying ? " playing" : ""}`}
onClick={(e) => {
e.stopPropagation()
onPreview()
}}
title={isPlaying ? "暂停" : "试听"}
>
{isPlaying ? "⏸" : "▶️"}
</button>
{isSelected && <span className="bgm-item-check"></span>}
</div>
)
}
@@ -1,88 +0,0 @@
import React from "react"
import { Slider } from "antd"
import type { BgmMixConfig as BgmMixConfigType, BgmPreset } from "@/api/bgm"
interface BgmMixConfigProps {
config: BgmMixConfigType
selectedBgm: BgmPreset | undefined
onChange: (config: BgmMixConfigType) => void
onClear: () => void
}
/**
* BGM 混音配置面板
* 音量、淡入淡出、人声闪避等设置
*/
export const BgmMixConfig: React.FC<BgmMixConfigProps> = ({
config,
selectedBgm,
onChange,
onClear,
}) => {
return (
<div className="bgm-mix-config">
<div className="bgm-mix-header">
<span></span>
<button className="bgm-mix-clear" onClick={onClear}>
BGM
</button>
</div>
<div className="bgm-mix-selected">
{selectedBgm ? `当前:${selectedBgm.name}` : `当前:${config.music_id}`}
</div>
{/* 音量 */}
<div className="bgm-mix-field">
<label className="bgm-mix-label">
<span className="bgm-mix-value">{config.volume}%</span>
</label>
<Slider
min={0}
max={100}
value={config.volume}
onChange={(v) => onChange({ ...config, volume: v })}
/>
</div>
{/* 淡入 */}
<div className="bgm-mix-field">
<label className="bgm-mix-label">
<span className="bgm-mix-value">{config.fade_in.toFixed(1)}s</span>
</label>
<Slider
min={0}
max={3}
step={0.1}
value={config.fade_in}
onChange={(v) => onChange({ ...config, fade_in: v })}
/>
</div>
{/* 淡出 */}
<div className="bgm-mix-field">
<label className="bgm-mix-label">
<span className="bgm-mix-value">{config.fade_out.toFixed(1)}s</span>
</label>
<Slider
min={0}
max={3}
step={0.1}
value={config.fade_out}
onChange={(v) => onChange({ ...config, fade_out: v })}
/>
</div>
{/* 人声闪避 */}
<div className="bgm-mix-field bgm-mix-toggle-row">
<label className="bgm-mix-label">sidechain</label>
<div
className={`ep-toggle${config.voice_dodge ? " active" : ""}`}
onClick={() => onChange({ ...config, voice_dodge: !config.voice_dodge })}
>
<div className="ep-toggle-knob" />
</div>
</div>
</div>
)
}
@@ -1,132 +0,0 @@
/**
* BGM 选择器 — Drawer 形式
* 预设 BGM 列表(按风格分类)、搜索、试听、音量/淡入淡出/人声闪避配置
*/
import React, { useCallback } from "react"
import { Drawer, Input, Tag } from "antd"
import { type BgmMixConfig, DEFAULT_BGM_MIX_CONFIG } from "@/api/bgm"
import { useBgmSelector, CATEGORY_LIST } from "./useBgmSelector"
import { BgmItem } from "./BgmItem"
import { BgmMixConfig as BgmMixConfigPanel } from "./BgmMixConfig"
const { Search } = Input
interface BgmSelectorProps {
open: boolean
onClose: () => void
config: BgmMixConfig
onChange: (config: BgmMixConfig) => void
/** 模板/草稿 ID,用于请求 BGM 预设 */
templateId?: string
}
const BgmSelector: React.FC<BgmSelectorProps> = ({
open,
onClose,
config,
onChange,
templateId,
}) => {
const {
presets,
loading,
activeCategory,
setActiveCategory,
keyword,
setKeyword,
previewingId,
loadPresets,
handlePreview,
stopPreview,
} = useBgmSelector(open, templateId)
/* ── 选中 BGM ── */
const handleSelect = useCallback(
(bgmId: string) => {
onChange({
...config,
enabled: true,
music_id: bgmId,
})
},
[config, onChange],
)
/* ── 关闭时停止播放 ── */
const handleClose = useCallback(() => {
stopPreview()
onClose()
}, [stopPreview, onClose])
/* ── 移除 BGM ── */
const handleClear = useCallback(() => {
stopPreview()
onChange({ ...DEFAULT_BGM_MIX_CONFIG })
}, [stopPreview, onChange])
/* ── 当前选中的 BGM ── */
const selectedBgm = presets.find((p) => p.id === config.music_id)
return (
<Drawer
title="🎵 BGM 音乐选择"
placement="right"
width={420}
open={open}
onClose={handleClose}
className="bgm-selector-drawer"
>
{/* 搜索框 */}
<div className="bgm-search-row">
<Search
placeholder="搜索 BGM 名称..."
allowClear
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
onSearch={() => loadPresets()}
/>
</div>
{/* 分类标签 */}
<div className="bgm-category-bar">
{CATEGORY_LIST.map((cat) => (
<Tag
key={cat.key}
className={`bgm-category-tag${activeCategory === cat.key ? " active" : ""}`}
onClick={() => setActiveCategory(cat.key)}
>
{cat.icon} {cat.label}
</Tag>
))}
</div>
{/* BGM 列表 */}
<div className="bgm-list">
{loading && <div className="bgm-loading">...</div>}
{!loading && presets.length === 0 && <div className="bgm-empty"> BGM </div>}
{presets.map((bgm) => (
<BgmItem
key={bgm.id}
bgm={bgm}
isSelected={config.music_id === bgm.id}
isPlaying={previewingId === bgm.id}
onSelect={() => handleSelect(bgm.id)}
onPreview={() => handlePreview(bgm)}
/>
))}
</div>
{/* 混音配置 */}
{config.enabled && config.music_id && (
<BgmMixConfigPanel
config={config}
selectedBgm={selectedBgm}
onChange={onChange}
onClear={handleClear}
/>
)}
</Drawer>
)
}
export default BgmSelector
@@ -1,91 +0,0 @@
import { useState, useRef, useCallback, useEffect } from "react"
import { message } from "antd"
import { getBgmPresets, type BgmPreset, type BgmCategory } from "@/api/bgm"
/* ──────────── 分类标签 ──────────── */
export const CATEGORY_LIST: {
key: BgmCategory | "all"
label: string
icon: string
}[] = [
{ key: "all", label: "全部", icon: "🎶" },
{ key: "轻快", label: "轻快", icon: "🎉" },
{ key: "治愈", label: "治愈", icon: "🌿" },
{ key: "科技", label: "科技", icon: "🔬" },
{ key: "电商", label: "电商", icon: "🛒" },
]
/**
* BGM 选择器数据与交互 Hook
* 封装列表加载、搜索、分类筛选、试听播放逻辑
*/
export function useBgmSelector(open: boolean, templateId?: string) {
const [presets, setPresets] = useState<BgmPreset[]>([])
const [loading, setLoading] = useState(false)
const [activeCategory, setActiveCategory] = useState<BgmCategory | "all">("all")
const [keyword, setKeyword] = useState("")
const [previewingId, setPreviewingId] = useState<string | null>(null)
const audioRef = useRef<HTMLAudioElement | null>(null)
/* ── 加载 BGM 列表 ── */
const loadPresets = useCallback(async () => {
if (!templateId) {
setPresets([])
return
}
setLoading(true)
try {
const params: { category?: string; keyword?: string } = {}
if (activeCategory !== "all") params.category = activeCategory
if (keyword.trim()) params.keyword = keyword.trim()
const data = await getBgmPresets(templateId, params)
setPresets(data)
} catch {
message.error("加载 BGM 列表失败")
} finally {
setLoading(false)
}
}, [activeCategory, keyword, templateId])
useEffect(() => {
if (open) loadPresets()
}, [open, loadPresets])
/* ── 试听 ── */
const handlePreview = useCallback(
(bgm: BgmPreset) => {
if (previewingId === bgm.id) {
audioRef.current?.pause()
setPreviewingId(null)
return
}
audioRef.current?.pause()
const audio = new Audio(bgm.url)
audioRef.current = audio
audio.play().catch(() => {})
audio.onended = () => setPreviewingId(null)
setPreviewingId(bgm.id)
},
[previewingId],
)
/* ── 停止播放(关闭/移除时调用) ── */
const stopPreview = useCallback(() => {
audioRef.current?.pause()
setPreviewingId(null)
}, [])
return {
presets,
loading,
activeCategory,
setActiveCategory,
keyword,
setKeyword,
previewingId,
loadPresets,
handlePreview,
stopPreview,
}
}
@@ -1,41 +0,0 @@
/**
* BGM 设置区块
*/
import React from "react"
import type { BgmSettings } from "@/pages/editing-planner/types/clipProperties"
interface BgmSettingsSectionProps {
settings: BgmSettings
onOpenBgmDrawer?: () => void
}
const BgmSettingsSection: React.FC<BgmSettingsSectionProps> = ({ settings, onOpenBgmDrawer }) => {
return (
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">🎵</span>
BGM
</div>
{settings.enabled && settings.music_id ? (
<div className="ep-bgm-current">
<span className="ep-bgm-current-label">🎵 BGM</span>
<span className="ep-bgm-current-id">{settings.music_id}</span>
{settings.volume !== undefined && (
<span className="ep-bgm-current-vol"> {settings.volume}%</span>
)}
</div>
) : (
<div className="ep-bgm-empty"></div>
)}
{onOpenBgmDrawer && (
<button className="ep-advanced-btn" onClick={onOpenBgmDrawer}>
🎵 {settings.enabled ? "更换 BGM / 调整混音" : "选择 BGM 音乐"}
</button>
)}
</div>
)
}
export default BgmSettingsSection
@@ -1,5 +0,0 @@
/**
* ClipDetailSection 入口(向后兼容)
* 实际实现位于 ./clip-detail-section/ 目录
*/
export { default } from "./clip-detail-section"
@@ -1,39 +0,0 @@
/**
* 编辑统计区块
*/
import React from "react"
import { formatModeLabel } from "@/pages/editing-planner/utils/clipProperties"
import type { TemplateMode } from "@/api/editing-planner"
interface StatsSectionProps {
clipsCount: number
totalDuration: number
currentMode: TemplateMode
}
const StatsSection: React.FC<StatsSectionProps> = ({ clipsCount, totalDuration, currentMode }) => {
return (
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">📊</span>
</div>
<div className="ep-clip-detail">
<div className="ep-clip-detail-field">
<div className="ep-clip-detail-label"></div>
<div className="ep-clip-detail-value">{clipsCount}</div>
</div>
<div className="ep-clip-detail-field">
<div className="ep-clip-detail-label"></div>
<div className="ep-clip-detail-value">{totalDuration.toFixed(1)}s</div>
</div>
<div className="ep-clip-detail-field">
<div className="ep-clip-detail-label"></div>
<div className="ep-clip-detail-value">{formatModeLabel(currentMode)}</div>
</div>
</div>
</div>
)
}
export default StatsSection
@@ -1,113 +0,0 @@
/**
* 字幕设置区块
*/
import React from "react"
import {
POSITION_OPTIONS,
FONT_OPTIONS,
ANIMATION_OPTIONS,
} from "@/pages/editing-planner/constants/clipProperties"
import type { SubtitleSettings } from "@/pages/editing-planner/types/clipProperties"
interface SubtitleSettingsSectionProps {
settings: SubtitleSettings
onChange: (partial: Partial<SubtitleSettings>) => void
onOpenSubtitleDrawer?: () => void
}
const SubtitleSettingsSection: React.FC<SubtitleSettingsSectionProps> = ({
settings,
onChange,
onOpenSubtitleDrawer,
}) => {
return (
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">💬</span>
</div>
<div className="ep-toggle-row">
<span className="ep-toggle-label"></span>
<div
className={`ep-toggle ${settings.enabled ? "active" : ""}`}
onClick={() => onChange({ enabled: !settings.enabled })}
>
<div className="ep-toggle-knob" />
</div>
</div>
{settings.enabled && (
<>
<div className="ep-field">
<label className="ep-field-label"></label>
<select
className="ep-form-select"
value={settings.position}
onChange={(e) => onChange({ position: e.target.value })}
>
{POSITION_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
<div className="ep-field">
<label className="ep-field-label"></label>
<select
className="ep-form-select"
value={settings.font}
onChange={(e) => onChange({ font: e.target.value })}
>
{FONT_OPTIONS.map((f) => (
<option key={f} value={f}>
{f}
</option>
))}
</select>
</div>
<div className="ep-field">
<label className="ep-field-label"></label>
<div className="ep-slider-row">
<input
className="ep-slider"
type="range"
min={12}
max={48}
value={settings.fontSize}
onChange={(e) => onChange({ fontSize: Number(e.target.value) })}
/>
<span className="ep-slider-value">{settings.fontSize}px</span>
</div>
</div>
<div className="ep-field">
<label className="ep-field-label"></label>
<select
className="ep-form-select"
value={settings.animation}
onChange={(e) => onChange({ animation: e.target.value })}
>
{ANIMATION_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
{onOpenSubtitleDrawer && (
<button className="ep-advanced-btn" onClick={onOpenSubtitleDrawer}>
🎨
</button>
)}
</>
)}
</div>
)
}
export default SubtitleSettingsSection
@@ -1,136 +0,0 @@
/**
* 标题设置区块 — #1789
* 提供字号滑块、字体预设、位置、颜色等控制入口
*/
import React from "react"
import type { TitleConfig } from "@/api/template-editor"
import { POSITION_OPTIONS, FONT_OPTIONS } from "@/pages/editing-planner/constants/clipProperties"
interface TitleSettingsSectionProps {
config: TitleConfig
onChange: (config: TitleConfig | ((prev: TitleConfig) => TitleConfig)) => void
}
const TITLE_COLOR_PRESETS = [
"#ffffff",
"#000000",
"#ff4444",
"#ffaa00",
"#44ff44",
"#4488ff",
"#ff44ff",
"#ffff44",
]
const TitleSettingsSection: React.FC<TitleSettingsSectionProps> = ({ config, onChange }) => {
const update = (partial: Partial<TitleConfig>) => {
onChange((prev: TitleConfig) => ({ ...prev, ...partial }))
}
return (
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">📝</span>
</div>
{/* AI 自动选择开关 */}
<div className="ep-toggle-row">
<span className="ep-toggle-label">AI </span>
<div
className={`ep-toggle ${config.ai_auto_select ? "active" : ""}`}
onClick={() => update({ ai_auto_select: !config.ai_auto_select })}
>
<div className="ep-toggle-knob" />
</div>
</div>
{!config.ai_auto_select && (
<>
{/* 标题文本 */}
<div className="ep-field">
<label className="ep-field-label"></label>
<input
className="ep-form-select"
type="text"
placeholder="输入标题内容"
value={config.content}
onChange={(e) => update({ content: e.target.value })}
/>
</div>
{/* 位置 */}
<div className="ep-field">
<label className="ep-field-label"></label>
<select
className="ep-form-select"
value={config.position}
onChange={(e) => update({ position: e.target.value })}
>
{POSITION_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
{/* 字体预设 */}
<div className="ep-field">
<label className="ep-field-label"></label>
<select
className="ep-form-select"
value={config.font_preset}
onChange={(e) => update({ font_preset: e.target.value })}
>
{FONT_OPTIONS.map((f) => (
<option key={f} value={f}>
{f}
</option>
))}
</select>
</div>
{/* 字号滑块 */}
<div className="ep-field">
<label className="ep-field-label"></label>
<div className="ep-slider-row">
<input
className="ep-slider"
type="range"
min={12}
max={72}
value={config.font_size}
onChange={(e) => update({ font_size: Number(e.target.value) })}
/>
<span className="ep-slider-value">{config.font_size}px</span>
</div>
</div>
{/* 颜色 */}
<div className="ep-field">
<label className="ep-field-label"></label>
<div className="ep-color-presets">
{TITLE_COLOR_PRESETS.map((color) => (
<div
key={color}
className={`ep-color-swatch${config.font_color === color ? " active" : ""}`}
style={{ backgroundColor: color }}
onClick={() => update({ font_color: color })}
/>
))}
<input
type="color"
className="ep-color-picker"
value={config.font_color}
onChange={(e) => update({ font_color: e.target.value })}
/>
</div>
</div>
</>
)}
</div>
)
}
export default TitleSettingsSection
@@ -1,85 +0,0 @@
import React from "react"
import { TRANSITION_OPTIONS } from "@/api/template-editor"
import type { ClipData } from "@/pages/editing-planner/types"
interface AdvancedEntriesProps {
clip: ClipData
onOpenTransitionDrawer?: (clipId: string) => void
onOpenSpeedDrawer?: (clipId: string) => void
onOpenTtsDrawer?: (clipId: string) => void
}
/**
* 高级功能入口按钮(转场/调速/TTS)
*/
export const AdvancedEntries: React.FC<AdvancedEntriesProps> = ({
clip,
onOpenTransitionDrawer,
onOpenSpeedDrawer,
onOpenTtsDrawer,
}) => {
const transitionLabel = (() => {
const t = clip.transition
if (!t || t.type === "none") return "无转场"
const opt = TRANSITION_OPTIONS.find((o) => o.value === t.type)
return `${opt?.label ?? t.type} · ${t.duration.toFixed(1)}s`
})()
const speedLabel = clip.speed ? `${clip.speed.rate.toFixed(2)}x` : "1.00x"
const ttsLabel = (() => {
const tts = clip.tts_config
if (!tts || tts.mode === "none") return "无配音"
if (tts.mode === "upload") return "上传配音"
return `TTS · ${tts.voice_id ? "已选音色" : "未选音色"}`
})()
return (
<>
{/* 转场效果入口 */}
{onOpenTransitionDrawer && (
<div className="ep-clip-detail-field">
<button
className="ep-advanced-btn ep-advanced-btn--transition"
onClick={() => onOpenTransitionDrawer(clip.id)}
>
<span className="ep-advanced-btn-icon">🎬</span>
<span className="ep-advanced-btn-label"></span>
<span className="ep-advanced-btn-value">{transitionLabel}</span>
<span className="ep-advanced-btn-arrow"></span>
</button>
</div>
)}
{/* 播放速度入口 */}
{onOpenSpeedDrawer && (
<div className="ep-clip-detail-field">
<button
className="ep-advanced-btn ep-advanced-btn--speed"
onClick={() => onOpenSpeedDrawer(clip.id)}
>
<span className="ep-advanced-btn-icon"></span>
<span className="ep-advanced-btn-label"></span>
<span className="ep-advanced-btn-value">{speedLabel}</span>
<span className="ep-advanced-btn-arrow"></span>
</button>
</div>
)}
{/* TTS 配音入口 */}
{onOpenTtsDrawer && (
<div className="ep-clip-detail-field">
<button
className="ep-advanced-btn ep-advanced-btn--tts"
onClick={() => onOpenTtsDrawer(clip.id)}
>
<span className="ep-advanced-btn-icon">🎙</span>
<span className="ep-advanced-btn-label">TTS </span>
<span className="ep-advanced-btn-value">{ttsLabel}</span>
<span className="ep-advanced-btn-arrow"></span>
</button>
</div>
)}
</>
)
}
@@ -1,69 +0,0 @@
import React from "react"
import type { ClipData, ClipType } from "@/pages/editing-planner/types"
import { CLIP_TYPE_ICONS, CLIP_TYPE_LABELS } from "@/pages/editing-planner/constants/clipProperties"
import type { TemplateMode } from "@/api/editing-planner"
interface ClipTypeAndDurationProps {
clip: ClipData
currentMode: TemplateMode
onClipUpdate: (clipId: string, data: Partial<ClipData>) => void
}
/**
* 片段类型选择 + 时长设置
*/
export const ClipTypeAndDuration: React.FC<ClipTypeAndDurationProps> = ({
clip,
currentMode,
onClipUpdate,
}) => {
const isTypeDisabled = (t: ClipType) => {
if (currentMode === "pip") return t !== "pip"
if (currentMode === "voice_over") return t !== "voice"
return false
}
return (
<>
{/* 类型选择器 */}
<div className="ep-clip-detail-field">
<div className="ep-clip-detail-label"></div>
<div className="ep-clip-type-selector">
{(["voice", "pip"] as ClipType[]).map((t) => {
const disabled = isTypeDisabled(t)
return (
<button
key={t}
className={`ep-clip-type-btn${clip.type === t ? " active" : ""}${disabled ? " disabled" : ""}`}
disabled={disabled}
onClick={() => !disabled && onClipUpdate(clip.id, { type: t })}
>
{CLIP_TYPE_ICONS[t]} {CLIP_TYPE_LABELS[t]}
</button>
)
})}
</div>
</div>
{/* 时长 */}
<div className="ep-clip-detail-field">
<div className="ep-clip-detail-label"></div>
<div className="ep-clip-detail-row">
<input
className="ep-duration-input"
type="number"
min={1}
max={120}
value={clip.duration}
onChange={(e) =>
onClipUpdate(clip.id, {
duration: Math.max(1, Math.min(120, Number(e.target.value) || 1)),
})
}
/>
<span className="ep-clip-detail-unit"></span>
</div>
</div>
</>
)
}
@@ -1,132 +0,0 @@
import React from "react"
import { useNavigate } from "react-router-dom"
import type { ClipData } from "@/pages/editing-planner/types"
import { getGenderLabel } from "@/pages/editing-planner/utils/clipProperties"
import type { AssetItem } from "@/api/assets"
interface VoiceMaterialSectionProps {
clip: ClipData
voiceMaterials: AssetItem[]
voiceMaterialsLoading: boolean
onClipUpdate: (clipId: string, data: Partial<ClipData>) => void
onRefreshVoiceMaterials?: () => void
onClipVoiceSelect?: (clipId: string, asset: AssetItem | null) => void
previewingId: string | null
onPreviewVoice: (asset: AssetItem) => void
onStopPreview: () => void
}
/**
* 配音素材选择区(仅 voice 类型显示)
*/
export const VoiceMaterialSection: React.FC<VoiceMaterialSectionProps> = ({
clip,
voiceMaterials,
voiceMaterialsLoading,
onClipUpdate,
onRefreshVoiceMaterials,
onClipVoiceSelect,
previewingId,
onPreviewVoice,
onStopPreview,
}) => {
const navigate = useNavigate()
return (
<>
{/* 素材起始时间 */}
<div className="ep-clip-detail-field">
<div className="ep-clip-detail-label"></div>
<div className="ep-clip-detail-row">
<input
className="ep-duration-input"
type="number"
min={0}
max={9999}
step={0.1}
value={clip.startOffset}
onChange={(e) =>
onClipUpdate(clip.id, {
startOffset: Math.max(0, Math.min(9999, Number(e.target.value) || 0)),
})
}
/>
<span className="ep-clip-detail-unit"></span>
</div>
</div>
{/* 配音素材选择 */}
<div className="ep-clip-detail-field">
<div className="ep-clip-detail-label">
{onRefreshVoiceMaterials && (
<button
type="button"
className="ep-voice-refresh-btn"
title="刷新配音列表"
onClick={() => onRefreshVoiceMaterials()}
disabled={voiceMaterialsLoading}
>
{voiceMaterialsLoading ? "⏳" : "🔄"}
</button>
)}
</div>
{voiceMaterialsLoading && voiceMaterials.length === 0 ? (
<div className="ep-voice-loading">...</div>
) : (
<>
<div className="ep-voice-select-row">
<select
className="ep-clip-detail-select"
value={clip.voice_asset_id ?? ""}
onChange={(e) => {
const assetId = e.target.value
if (!onClipVoiceSelect) return
if (!assetId) {
onClipVoiceSelect(clip.id, null)
} else {
const asset = voiceMaterials.find((m) => m.id === assetId)
if (asset) onClipVoiceSelect(clip.id, asset)
}
onStopPreview()
}}
>
<option value=""></option>
{voiceMaterials.map((m) => {
const gender = getGenderLabel(m)
const label = gender ? `${m.name}${gender}` : m.name
return (
<option key={m.id} value={m.id}>
{label}
</option>
)
})}
</select>
{clip.voice_asset_id && (
<button
type="button"
className="ep-voice-preview-btn"
title={previewingId === clip.voice_asset_id ? "暂停" : "试听"}
onClick={() => {
const asset = voiceMaterials.find((m) => m.id === clip.voice_asset_id)
if (asset) onPreviewVoice(asset)
}}
>
{previewingId === clip.voice_asset_id ? "⏸" : "▶️"}
</button>
)}
</div>
{voiceMaterials.length === 0 && (
<div className="ep-voice-empty"></div>
)}
</>
)}
<button className="ep-voice-upload-btn" onClick={() => navigate("/app/voice-materials")}>
+
</button>
</div>
</>
)
}
@@ -1,78 +0,0 @@
/**
* 片段详情区块
*/
import React from "react"
import type { ClipData } from "@/pages/editing-planner/types"
import type { AssetItem } from "@/api/assets"
import type { TemplateMode } from "@/api/editing-planner"
import { ClipTypeAndDuration } from "./ClipTypeAndDuration"
import { AdvancedEntries } from "./AdvancedEntries"
import { VoiceMaterialSection } from "./VoiceMaterialSection"
interface ClipDetailSectionProps {
clip: ClipData
currentMode: TemplateMode
voiceMaterials?: AssetItem[]
voiceMaterialsLoading?: boolean
onClipUpdate: (clipId: string, data: Partial<ClipData>) => void
onRefreshVoiceMaterials?: () => void
onClipVoiceSelect?: (clipId: string, asset: AssetItem | null) => void
onOpenTransitionDrawer?: (clipId: string) => void
onOpenSpeedDrawer?: (clipId: string) => void
onOpenTtsDrawer?: (clipId: string) => void
previewingId: string | null
onPreviewVoice: (asset: AssetItem) => void
onStopPreview: () => void
}
const ClipDetailSection: React.FC<ClipDetailSectionProps> = ({
clip,
currentMode,
voiceMaterials = [],
voiceMaterialsLoading = false,
onClipUpdate,
onRefreshVoiceMaterials,
onClipVoiceSelect,
onOpenTransitionDrawer,
onOpenSpeedDrawer,
onOpenTtsDrawer,
previewingId,
onPreviewVoice,
onStopPreview,
}) => {
return (
<div className="ep-settings-section">
<div className="ep-section-title">
<span className="ep-section-icon">🎞</span>
</div>
<div className="ep-clip-detail">
<ClipTypeAndDuration clip={clip} currentMode={currentMode} onClipUpdate={onClipUpdate} />
<AdvancedEntries
clip={clip}
onOpenTransitionDrawer={onOpenTransitionDrawer}
onOpenSpeedDrawer={onOpenSpeedDrawer}
onOpenTtsDrawer={onOpenTtsDrawer}
/>
{clip.type === "voice" && (
<VoiceMaterialSection
clip={clip}
voiceMaterials={voiceMaterials}
voiceMaterialsLoading={voiceMaterialsLoading}
onClipUpdate={onClipUpdate}
onRefreshVoiceMaterials={onRefreshVoiceMaterials}
onClipVoiceSelect={onClipVoiceSelect}
previewingId={previewingId}
onPreviewVoice={onPreviewVoice}
onStopPreview={onStopPreview}
/>
)}
</div>
</div>
)
}
export default ClipDetailSection
@@ -1,74 +0,0 @@
import React from "react"
import { DEFAULT_TRANSITION, DEFAULT_SPEED, DEFAULT_TTS_CONFIG } from "../../types"
import TransitionSelector from "../TransitionSelector"
import SpeedPanel from "../SpeedPanel"
import TtsPanel from "../TtsPanel"
import type { ClipLevelDrawersProps } from "./types"
/**
* 片段级抽屉(转场/调速/TTS)
* 这些抽屉针对特定片段,需要 targetClipId 来定位和读取当前配置
*/
export const ClipLevelDrawers: React.FC<ClipLevelDrawersProps> = ({
clips,
transitionDrawerOpen,
transitionTargetClipId,
onCloseTransitionDrawer,
onTransitionChange,
speedDrawerOpen,
speedTargetClipId,
onCloseSpeedDrawer,
onSpeedChange,
onApplySpeedAll,
ttsDrawerOpen,
ttsTargetClipId,
onCloseTtsDrawer,
onTtsChange,
}) => {
const transitionConfig = transitionTargetClipId
? (clips.find((c) => c.id === transitionTargetClipId)?.transition ?? DEFAULT_TRANSITION)
: DEFAULT_TRANSITION
const transitionTitle = transitionTargetClipId ? "片段转场设置" : "全局默认转场"
const speedConfig = speedTargetClipId
? (clips.find((c) => c.id === speedTargetClipId)?.speed ?? DEFAULT_SPEED)
: DEFAULT_SPEED
const ttsConfig = ttsTargetClipId
? (clips.find((c) => c.id === ttsTargetClipId)?.tts_config ?? DEFAULT_TTS_CONFIG)
: DEFAULT_TTS_CONFIG
return (
<>
{/* 转场特效选择器 */}
<TransitionSelector
open={transitionDrawerOpen}
onClose={onCloseTransitionDrawer}
config={transitionConfig}
onChange={onTransitionChange}
title={transitionTitle}
/>
{/* 片段调速面板 */}
{speedTargetClipId && (
<SpeedPanel
open={speedDrawerOpen}
onClose={onCloseSpeedDrawer}
config={speedConfig}
onChange={onSpeedChange}
onApplyAll={onApplySpeedAll}
/>
)}
{/* TTS 配音面板 */}
{ttsTargetClipId && (
<TtsPanel
open={ttsDrawerOpen}
onClose={onCloseTtsDrawer}
config={ttsConfig}
onChange={onTtsChange}
/>
)}
</>
)
}
@@ -1,119 +0,0 @@
import React from "react"
import BgmSelector from "../BgmSelector"
import SubtitleStylePanel from "../SubtitleStylePanel"
import WatermarkPanel from "../WatermarkPanel"
import IntroOutroPanel from "../IntroOutroPanel"
import PipConfigPanel from "../PipConfigPanel"
import FilterPanel from "../FilterPanel"
import GreenScreenPanel from "../GreenScreenPanel"
import StickerPanel from "../StickerPanel"
import type { GlobalDrawersProps, BgmDrawerProps, SubtitleDrawerProps } from "./types"
/**
* 全局设置抽屉(BGM/字幕/水印/片头片尾/混剪/滤镜/绿幕/贴纸)
*/
export const GlobalDrawers: React.FC<BgmDrawerProps & SubtitleDrawerProps & GlobalDrawersProps> = ({
bgmDrawerOpen,
bgmSettings,
onCloseBgmDrawer,
onChangeBgmSettings,
subtitleDrawerOpen,
subtitleSettings,
onCloseSubtitleDrawer,
onChangeSubtitleSettings,
totalDuration,
watermarkDrawerOpen,
watermarkSettings,
onCloseWatermarkDrawer,
onWatermarkChange,
introOutroDrawerOpen,
introOutroSettings,
onCloseIntroOutroDrawer,
onIntroOutroChange,
pipDrawerOpen,
pipSettings,
onClosePipDrawer,
onPipChange,
filterDrawerOpen,
filterSettings,
onCloseFilterDrawer,
onFilterChange,
chromaKeyDrawerOpen,
chromaKeySettings,
onCloseChromaKeyDrawer,
onChromaKeyChange,
stickerDrawerOpen,
stickerSettings,
onCloseStickerDrawer,
onStickerChange,
}) => {
return (
<>
{/* BGM 选择器 */}
<BgmSelector
open={bgmDrawerOpen}
onClose={onCloseBgmDrawer}
config={bgmSettings}
onChange={onChangeBgmSettings}
/>
{/* 字幕样式配置 */}
<SubtitleStylePanel
open={subtitleDrawerOpen}
onClose={onCloseSubtitleDrawer}
config={subtitleSettings}
onChange={onChangeSubtitleSettings}
/>
{/* 水印配置面板 */}
<WatermarkPanel
open={watermarkDrawerOpen}
onClose={onCloseWatermarkDrawer}
config={watermarkSettings}
onChange={onWatermarkChange}
/>
{/* 片头片尾配置面板 */}
<IntroOutroPanel
open={introOutroDrawerOpen}
onClose={onCloseIntroOutroDrawer}
config={introOutroSettings}
onChange={onIntroOutroChange}
/>
{/* 混剪配置面板 */}
<PipConfigPanel
open={pipDrawerOpen}
onClose={onClosePipDrawer}
config={pipSettings}
onChange={onPipChange}
totalDuration={totalDuration}
/>
{/* 滤镜调色面板 */}
<FilterPanel
open={filterDrawerOpen}
onClose={onCloseFilterDrawer}
config={filterSettings}
onChange={onFilterChange}
/>
{/* 绿幕抠像面板 */}
<GreenScreenPanel
open={chromaKeyDrawerOpen}
onClose={onCloseChromaKeyDrawer}
config={chromaKeySettings}
onChange={onChromaKeyChange}
/>
{/* 贴纸面板 */}
<StickerPanel
open={stickerDrawerOpen}
onClose={onCloseStickerDrawer}
config={stickerSettings}
onChange={onStickerChange}
totalDuration={totalDuration}
/>
</>
)
}
@@ -1,101 +0,0 @@
import type { TemplateCategory } from "@/api/editing-planner"
import type {
ClipData,
TransitionConfig,
SpeedConfig,
TtsConfig,
WatermarkConfig,
IntroOutroConfig,
PipConfig,
FilterConfig,
ChromaKeyConfig,
StickerConfig,
} from "../../types"
import type { SubtitleStyleConfig } from "../../types/subtitle"
import type { BgmMixConfig } from "@/api/bgm"
/** 保存弹窗 Props */
export interface SaveModalDrawerProps {
saveModalOpen: boolean
saveLoading: boolean
isUpdate: boolean
draftName: string
draftCategory: string
draftTags: string
categories: TemplateCategory[]
estimatedDuration: number
onNameChange: (name: string) => void
onCategoryChange: (cat: string) => void
onTagsChange: (tags: string) => void
onSave: () => Promise<void>
onCancelSave: () => void
}
/** BGM 抽屉 Props */
export interface BgmDrawerProps {
bgmDrawerOpen: boolean
bgmSettings: BgmMixConfig
onCloseBgmDrawer: () => void
onChangeBgmSettings: (config: BgmMixConfig) => void
}
/** 字幕抽屉 Props */
export interface SubtitleDrawerProps {
subtitleDrawerOpen: boolean
subtitleSettings: SubtitleStyleConfig
onCloseSubtitleDrawer: () => void
onChangeSubtitleSettings: (config: SubtitleStyleConfig) => void
}
/** 单个片段级抽屉通用 Props */
export interface ClipLevelDrawersProps {
clips: ClipData[]
transitionDrawerOpen: boolean
transitionTargetClipId: string | null
onCloseTransitionDrawer: () => void
onTransitionChange: (config: TransitionConfig) => void
speedDrawerOpen: boolean
speedTargetClipId: string | null
onCloseSpeedDrawer: () => void
onSpeedChange: (config: SpeedConfig) => void
onApplySpeedAll: (config: SpeedConfig) => void
ttsDrawerOpen: boolean
ttsTargetClipId: string | null
onCloseTtsDrawer: () => void
onTtsChange: (config: TtsConfig) => void
}
/** 全局设置抽屉 Props */
export interface GlobalDrawersProps {
totalDuration: number
watermarkDrawerOpen: boolean
watermarkSettings: WatermarkConfig
onCloseWatermarkDrawer: () => void
onWatermarkChange: (config: WatermarkConfig) => void
introOutroDrawerOpen: boolean
introOutroSettings: IntroOutroConfig
onCloseIntroOutroDrawer: () => void
onIntroOutroChange: (config: IntroOutroConfig) => void
pipDrawerOpen: boolean
pipSettings: PipConfig
onClosePipDrawer: () => void
onPipChange: (config: PipConfig) => void
filterDrawerOpen: boolean
filterSettings: FilterConfig
onCloseFilterDrawer: () => void
onFilterChange: (config: FilterConfig) => void
chromaKeyDrawerOpen: boolean
chromaKeySettings: ChromaKeyConfig
onCloseChromaKeyDrawer: () => void
onChromaKeyChange: (config: ChromaKeyConfig) => void
stickerDrawerOpen: boolean
stickerSettings: StickerConfig
onCloseStickerDrawer: () => void
onStickerChange: (config: StickerConfig) => void
}
export type EditingDrawersProps = SaveModalDrawerProps &
BgmDrawerProps &
SubtitleDrawerProps &
ClipLevelDrawersProps &
GlobalDrawersProps
@@ -1,40 +0,0 @@
/**
* 滤镜手动调节组件
*/
import React from "react"
import type { FilterConfig } from "../../types"
import { MANUAL_ADJUST_ITEMS } from "../../constants/filter"
type FilterKey = keyof Pick<
FilterConfig,
"brightness" | "contrast" | "saturation" | "temperature" | "tint" | "sharpness"
>
interface FilterManualAdjustProps {
config: FilterConfig
onChange: (key: FilterKey, value: number) => void
}
const FilterManualAdjust: React.FC<FilterManualAdjustProps> = ({ config, onChange }) => {
return (
<div className="filter-section">
<div className="filter-section-title"></div>
{MANUAL_ADJUST_ITEMS.map((item) => (
<div key={item.key} className="filter-slider-row">
<span className="filter-slider-label">{item.label}</span>
<input
type="range"
className="filter-slider"
min={item.min}
max={item.max}
value={config[item.key as FilterKey]}
onChange={(e) => onChange(item.key as FilterKey, Number(e.target.value))}
/>
<span className="filter-slider-value">{config[item.key as FilterKey]}</span>
</div>
))}
</div>
)
}
export default FilterManualAdjust
@@ -1,34 +0,0 @@
/**
* 滤镜预设选择组件
*/
import React from "react"
import type { FilterPreset } from "../../types"
import { FILTER_PRESET_LABELS } from "../../types"
import { PRESET_LIST, PRESET_GRADIENTS } from "../../constants/filter"
interface FilterPresetGridProps {
selectedPreset: FilterPreset
onPresetSelect: (preset: FilterPreset) => void
}
const FilterPresetGrid: React.FC<FilterPresetGridProps> = ({ selectedPreset, onPresetSelect }) => {
return (
<div className="filter-section">
<div className="filter-section-title"></div>
<div className="filter-presets">
{PRESET_LIST.map((p) => (
<button
key={p}
className={`filter-preset-item${selectedPreset === p ? " active" : ""}`}
onClick={() => onPresetSelect(p)}
>
<div className="filter-preset-preview" style={{ background: PRESET_GRADIENTS[p] }} />
<span className="filter-preset-label">{FILTER_PRESET_LABELS[p]}</span>
</button>
))}
</div>
</div>
)
}
export default FilterPresetGrid
@@ -1,37 +0,0 @@
import React from "react"
interface GreenScreenCustomColorProps {
color: string
onColorChange: (color: string) => void
}
export const GreenScreenCustomColor: React.FC<GreenScreenCustomColorProps> = ({
color,
onColorChange,
}) => {
const handleColorInput = (e: React.ChangeEvent<HTMLInputElement>) => {
onColorChange(e.target.value)
}
return (
<div className="green-section">
<div className="green-section-title"></div>
<div className="green-color-row">
<input
type="color"
className="green-color-picker"
value={color}
onChange={handleColorInput}
/>
<input
type="text"
className="green-color-hex"
value={color}
onChange={handleColorInput}
placeholder="#00FF00"
/>
<div className="green-color-swatch" style={{ background: color }} />
</div>
</div>
)
}
@@ -1,36 +0,0 @@
import React from "react"
import type { ChromaKeyColorPreset } from "../../types"
import { CHROMA_KEY_PRESET_LABELS, CHROMA_KEY_PRESET_COLORS } from "../../types"
interface GreenScreenPresetsProps {
selectedPreset: ChromaKeyColorPreset | null
onPresetSelect: (preset: ChromaKeyColorPreset) => void
}
const PRESET_LIST: ChromaKeyColorPreset[] = ["green", "blue", "red", "pure_green", "soft_green"]
export const GreenScreenPresets: React.FC<GreenScreenPresetsProps> = ({
selectedPreset,
onPresetSelect,
}) => {
return (
<div className="green-section">
<div className="green-section-title"></div>
<div className="green-presets">
{PRESET_LIST.map((p) => (
<button
key={p}
className={`green-preset-btn${selectedPreset === p ? " active" : ""}`}
onClick={() => onPresetSelect(p)}
>
<span
className="green-preset-dot"
style={{ background: CHROMA_KEY_PRESET_COLORS[p] }}
/>
<span className="green-preset-label">{CHROMA_KEY_PRESET_LABELS[p]}</span>
</button>
))}
</div>
</div>
)
}
@@ -1,28 +0,0 @@
import React from "react"
interface GreenScreenPreviewProps {
color: string
blend: number
}
export const GreenScreenPreview: React.FC<GreenScreenPreviewProps> = ({ color, blend }) => {
return (
<div className="green-section">
<div className="green-section-title"></div>
<div className="green-preview-box">
<div className="green-preview-bg" style={{ background: color, opacity: 0.3 }} />
<div className="green-preview-subject">
<div className="green-preview-circle" />
<div className="green-preview-text"></div>
</div>
<div
className="green-preview-edge"
style={{
borderColor: color,
filter: `blur(${blend / 10}px)`,
}}
/>
</div>
</div>
)
}
@@ -1,68 +0,0 @@
import React from "react"
interface SliderConfig {
label: string
value: number
description: string
onChange: (value: number) => void
}
interface GreenScreenSlidersProps {
similarity: number
blend: number
spill: number
onSimilarityChange: (value: number) => void
onBlendChange: (value: number) => void
onSpillChange: (value: number) => void
}
const SliderRow: React.FC<SliderConfig> = ({ label, value, description, onChange }) => (
<div className="green-slider-row">
<div className="green-slider-header">
<span className="green-slider-label">{label}</span>
<span className="green-slider-value">{value}%</span>
</div>
<input
type="range"
className="green-slider"
min={0}
max={100}
value={value}
onChange={(e) => onChange(Number(e.target.value))}
/>
<div className="green-slider-desc">{description}</div>
</div>
)
export const GreenScreenSliders: React.FC<GreenScreenSlidersProps> = ({
similarity,
blend,
spill,
onSimilarityChange,
onBlendChange,
onSpillChange,
}) => {
return (
<div className="green-section">
<div className="green-section-title"></div>
<SliderRow
label="相似度"
value={similarity}
description="越大容忍的色差范围越广"
onChange={onSimilarityChange}
/>
<SliderRow
label="边缘平滑"
value={blend}
description="越大边缘越柔和自然"
onChange={onBlendChange}
/>
<SliderRow
label="溢色抑制"
value={spill}
description="去除边缘颜色溢出"
onChange={onSpillChange}
/>
</div>
)
}
@@ -1,123 +0,0 @@
/**
* 片头/片尾通用区块组件(片头片尾结构对称,复用同一个组件)
*/
import React from "react"
import type { IntroOutroItem, IntroOutroKind, TransitionType } from "../../types"
import { TRANSITION_OPTIONS } from "@/api/template-editor"
import { KIND_OPTIONS } from "../../constants/introOutro"
interface IntroOutroBlockProps {
title: string
icon: string
item: IntroOutroItem
transitionLabel: string
onKindChange: (kind: IntroOutroKind) => void
onChange: (partial: Partial<IntroOutroItem>) => void
}
const IntroOutroBlock: React.FC<IntroOutroBlockProps> = ({
title,
icon,
item,
transitionLabel,
onKindChange,
onChange,
}) => {
const hasContent = item.kind !== "none"
return (
<div className="iop-block">
<div className="iop-block-header">
<span className="iop-block-icon">{icon}</span>
<span className="iop-block-title">{title}</span>
</div>
{/* 类型选择 */}
<div className="iop-kind-row">
{KIND_OPTIONS.map((opt) => (
<button
key={opt.value}
className={`iop-kind-btn${item.kind === opt.value ? " active" : ""}`}
onClick={() => onKindChange(opt.value)}
>
<span className="iop-kind-icon">{opt.icon}</span>
<span className="iop-kind-label">{opt.label}</span>
</button>
))}
</div>
{/* 视频/图片配置 */}
{hasContent && (
<>
<div className="iop-field">
<label className="iop-field-label">{item.kind === "video" ? "视频" : "图片"} URL</label>
<input
className="iop-input"
type="text"
placeholder={
item.kind === "video"
? `https://example.com/${title}.mp4`
: `https://example.com/${title}.png`
}
value={item.url ?? ""}
onChange={(e) => onChange({ url: e.target.value })}
/>
</div>
<div className="iop-field">
<label className="iop-field-label"></label>
<div className="iop-slider-row">
<input
className="iop-slider"
type="range"
min={1}
max={15}
step={0.5}
value={item.duration}
onChange={(e) => onChange({ duration: Number(e.target.value) })}
/>
<span className="iop-slider-value">{item.duration}s</span>
</div>
</div>
<div className="iop-field">
<label className="iop-field-label">{transitionLabel}</label>
<select
className="iop-select"
value={item.transition ?? "none"}
onChange={(e) => onChange({ transition: e.target.value as TransitionType })}
>
{TRANSITION_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
{item.transition && item.transition !== "none" && (
<div className="iop-field">
<label className="iop-field-label"></label>
<div className="iop-slider-row">
<input
className="iop-slider"
type="range"
min={0.3}
max={2.0}
step={0.1}
value={item.transition_duration ?? 0.5}
onChange={(e) => onChange({ transition_duration: Number(e.target.value) })}
/>
<span className="iop-slider-value">
{(item.transition_duration ?? 0.5).toFixed(1)}s
</span>
</div>
</div>
)}
</>
)}
</div>
)
}
export default IntroOutroBlock
@@ -1,32 +0,0 @@
import React from "react"
interface SubtitleEffectButtonsProps {
stroke: boolean
shadow: boolean
onStrokeChange: (enabled: boolean) => void
onShadowChange: (enabled: boolean) => void
}
export const SubtitleEffectButtons: React.FC<SubtitleEffectButtonsProps> = ({
stroke,
shadow,
onStrokeChange,
onShadowChange,
}) => {
return (
<div className="sub-effect-btns">
<button
className={`sub-effect-btn${stroke ? " active" : ""}`}
onClick={() => onStrokeChange(!stroke)}
>
S
</button>
<button
className={`sub-effect-btn${shadow ? " active" : ""}`}
onClick={() => onShadowChange(!shadow)}
>
</button>
</div>
)
}
@@ -1,25 +0,0 @@
import React from "react"
interface SubtitleModeSwitchProps {
mode: "manual" | "asr"
onModeChange: (mode: "manual" | "asr") => void
}
export const SubtitleModeSwitch: React.FC<SubtitleModeSwitchProps> = ({ mode, onModeChange }) => {
return (
<div className="sub-mode-switch">
<button
className={`sub-mode-btn${mode === "manual" ? " active" : ""}`}
onClick={() => onModeChange("manual")}
>
</button>
<button
className={`sub-mode-btn${mode === "asr" ? " active" : ""}`}
onClick={() => onModeChange("asr")}
>
🤖 ASR
</button>
</div>
)
}
@@ -1,27 +0,0 @@
import React from "react"
import type { SubtitleStyleConfig } from "@/pages/editing-planner/types/subtitle"
import { POSITION_OPTIONS } from "@/pages/editing-planner/constants/subtitleStyle"
interface SubtitlePositionSelectorProps {
position: SubtitleStyleConfig["position"]
onPositionChange: (position: SubtitleStyleConfig["position"]) => void
}
export const SubtitlePositionSelector: React.FC<SubtitlePositionSelectorProps> = ({
position,
onPositionChange,
}) => {
return (
<div className="sub-position-group">
{POSITION_OPTIONS.map((opt) => (
<button
key={opt.value}
className={`sub-position-btn${position === opt.value ? " active" : ""}`}
onClick={() => onPositionChange(opt.value as SubtitleStyleConfig["position"])}
>
{opt.label}
</button>
))}
</div>
)
}
@@ -1,37 +0,0 @@
/**
* 字幕预览组件
*/
import React from "react"
import type { SubtitleStyleConfig } from "../../types/subtitle"
interface SubtitlePreviewProps {
config: SubtitleStyleConfig
previewText?: string
}
const SubtitlePreview: React.FC<SubtitlePreviewProps> = ({
config,
previewText = "这是一段字幕预览",
}) => {
return (
<div className="sub-field">
<label className="sub-label"></label>
<div className="sub-preview-box">
<span
className="sub-preview-text"
style={{
fontSize: `${Math.min(config.fontSize, 28)}px`,
color: config.fontColor,
fontFamily: config.font,
WebkitTextStroke: config.stroke ? "1px #000" : undefined,
textShadow: config.shadow ? "2px 2px 4px rgba(0,0,0,0.8)" : undefined,
}}
>
{previewText}
</span>
</div>
</div>
)
}
export default SubtitlePreview
@@ -1,54 +0,0 @@
import React from "react"
import type { ClipType } from "../../types"
import { CLIP_TYPE_ICONS, CLIP_TYPE_LABELS } from "../../constants/timeline"
interface AddClipPickerProps {
pickerRef: React.RefObject<HTMLDivElement>
position: { top: number; right: number }
availableTypes: ClipType[]
addType: ClipType
onTypeChange: (type: ClipType) => void
onConfirm: () => void
}
export const AddClipPicker: React.FC<AddClipPickerProps> = ({
pickerRef,
position,
availableTypes,
addType,
onTypeChange,
onConfirm,
}) => {
return (
<div
ref={pickerRef}
className="ep-add-clip-picker ep-add-clip-picker--portal"
style={{
position: "fixed",
top: position.top,
right: position.right,
}}
>
<div className="ep-add-clip-picker-title"></div>
{/* 类型选择 */}
<div className="ep-add-clip-type-row">
<span className="ep-add-clip-type-label">:</span>
{availableTypes.map((t) => (
<button
key={t}
className={`ep-add-clip-type-btn${addType === t ? " active" : ""}`}
onClick={() => onTypeChange(t)}
>
{CLIP_TYPE_ICONS[t]} {CLIP_TYPE_LABELS[t]}
</button>
))}
</div>
{/* 确认按钮 */}
<button className="ep-add-clip-confirm-btn" onClick={onConfirm}>
</button>
</div>
)
}
@@ -1,152 +0,0 @@
import React from "react"
import type { ClipData } from "../../types"
import { TRANSITION_OPTIONS } from "@/api/template-editor"
import { CLIP_TYPE_ICONS, CLIP_TYPE_LABELS, MIN_CLIP_WIDTH } from "../../constants/timeline"
interface ClipCardProps {
clip: ClipData
idx: number
isSelected: boolean
isDragging: boolean
isDragOver: boolean
isHovered: boolean
pps: number
trimDragActive: boolean
showTrimHandles: boolean
onDragStart: (e: React.DragEvent, idx: number) => void
onDragOver: (e: React.DragEvent, idx: number) => void
onDragEnd: () => void
onDrop: (e: React.DragEvent, idx: number) => void
onSelect: (clipId: string) => void
onContextMenu: (e: React.MouseEvent, clipId: string) => void
onMouseEnter: () => void
onMouseLeave: () => void
onTrimHandleMouseDown: (e: React.MouseEvent, clipId: string, direction: "left" | "right") => void
onRemove: (clipId: string) => void
}
export const ClipCard: React.FC<ClipCardProps> = ({
clip,
idx,
isSelected,
isDragging,
isDragOver,
isHovered,
pps,
trimDragActive,
showTrimHandles,
onDragStart,
onDragOver,
onDragEnd,
onDrop,
onSelect,
onContextMenu,
onMouseEnter,
onMouseLeave,
onTrimHandleMouseDown,
onRemove,
}) => {
/* 转场指示器 */
const trans = clip.transition
const showTransition = idx > 0 && trans && trans.type !== "none"
const transOpt = showTransition
? TRANSITION_OPTIONS.find((o) => o.value === trans!.type)
: undefined
/* 速度徽章 */
const speed = clip.speed
const showSpeed = speed && Math.abs(speed.rate - 1.0) > 0.01
/* 裁剪状态 */
const hasTrim = !!clip.trim_config
return (
<React.Fragment key={clip.id}>
{/* 转场指示器 */}
{showTransition && transOpt && (
<div
className="ep-transition-indicator"
title={`${transOpt.label} · ${trans!.duration.toFixed(1)}s`}
>
<span className="ep-trans-icon">{transOpt.icon}</span>
<span className="ep-trans-duration">{trans!.duration.toFixed(1)}s</span>
</div>
)}
<div
className={`ep-clip-card ${isSelected ? "selected" : ""} ${isDragging ? "dragging" : ""} ${isDragOver ? "drag-over" : ""} ${hasTrim ? "trimmed" : ""}`}
style={{ width: Math.max(clip.duration * pps, MIN_CLIP_WIDTH) }}
draggable={!trimDragActive}
onDragStart={(e) => onDragStart(e, idx)}
onDragOver={(e) => onDragOver(e, idx)}
onDragEnd={onDragEnd}
onDrop={(e) => onDrop(e, idx)}
onClick={() => onSelect(clip.id)}
onContextMenu={(e) => onContextMenu(e, clip.id)}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{/* 左裁剪手柄 */}
{isHovered && showTrimHandles && (
<div
className="ep-trim-handle ep-trim-handle-left"
onMouseDown={(e) => onTrimHandleMouseDown(e, clip.id, "left")}
title="拖动调整入点"
>
<div className="ep-trim-handle-line" />
</div>
)}
{/* 类型图标 */}
<div className="ep-clip-thumbnail">{CLIP_TYPE_ICONS[clip.type] || "🎬"}</div>
{/* 片段信息 */}
<div className="ep-clip-info">
<span className="ep-clip-name">
{CLIP_TYPE_LABELS[clip.type] || "片段"} {idx + 1}
</span>
{hasTrim && (
<span className="ep-trim-indicator" title="已裁剪">
</span>
)}
</div>
{/* 速度徽章 */}
{showSpeed && <span className="ep-speed-badge">{speed!.rate.toFixed(1)}x</span>}
{/* 裁剪徽章 */}
{hasTrim && (
<span
className="ep-trim-badge"
title={`入点 ${clip.trim_config!.start_time.toFixed(1)}s / 出点 ${clip.trim_config!.end_time.toFixed(1)}s`}
>
</span>
)}
{/* 右裁剪手柄 */}
{isHovered && showTrimHandles && (
<div
className="ep-trim-handle ep-trim-handle-right"
onMouseDown={(e) => onTrimHandleMouseDown(e, clip.id, "right")}
title="拖动调整出点"
>
<div className="ep-trim-handle-line" />
</div>
)}
{/* 删除按钮 */}
<button
className="ep-clip-remove"
onClick={(e) => {
e.stopPropagation()
onRemove(clip.id)
}}
>
</button>
</div>
</React.Fragment>
)
}
@@ -1,146 +0,0 @@
import React from "react"
import type { ClipData } from "../../types"
import { ClipCard } from "./ClipCard"
interface ClipTrackProps {
/** 片段列表 */
clips: ClipData[]
/** 选中的片段ID */
selectedClipId: string | null
/** 缩放:每秒像素数 */
pps: number
/** 当前播放时间(秒) */
currentTime: number
/** 总时长(秒) */
totalDuration: number
/** 正在拖拽的片段索引 */
dragIdx: number | null
/** 拖拽悬停的片段索引 */
dragOverIdx: number | null
/** 悬停的片段ID */
hoveredClipId: string | null
/** 是否正在裁剪拖拽 */
trimDragActive: boolean
/** 是否显示裁剪手柄 */
showTrimHandles: boolean
/** 轨道ref */
trackRef: React.RefObject<HTMLDivElement>
/** 添加卡片ref */
addCardRef: React.RefObject<HTMLDivElement>
/** 播放头拖拽开始 */
onPlayheadMouseDown: (e: React.MouseEvent) => void
/** 片段选中 */
onClipSelect: (clipId: string) => void
/** 拖拽开始 */
onDragStart: (e: React.DragEvent, idx: number) => void
/** 拖拽悬停 */
onDragOver: (e: React.DragEvent, idx: number) => void
/** 拖拽结束 */
onDragEnd: () => void
/** 放置 */
onDrop: (e: React.DragEvent, idx: number) => void
/** 空区域拖拽悬停 */
onEmptyDragOver: (e: React.DragEvent) => void
/** 右键菜单 */
onContextMenu: (e: React.MouseEvent, clipId: string) => void
/** 片段悬停进入 */
onClipMouseEnter: (clipId: string) => void
/** 片段悬停离开 */
onClipMouseLeave: () => void
/** 裁剪手柄按下 */
onTrimHandleMouseDown: (e: React.MouseEvent, clipId: string, direction: "left" | "right") => void
/** 删除片段 */
onClipRemove: (clipId: string) => void
/** 点击添加卡片 */
onTogglePicker: () => void
}
/**
* 片段轨道组件
* 包含播放头、片段列表、空状态和添加卡片
*/
export const ClipTrack: React.FC<ClipTrackProps> = ({
clips,
selectedClipId,
pps,
currentTime,
totalDuration,
dragIdx,
dragOverIdx,
hoveredClipId,
trimDragActive,
showTrimHandles,
trackRef,
addCardRef,
onPlayheadMouseDown,
onClipSelect,
onDragStart,
onDragOver,
onDragEnd,
onDrop,
onEmptyDragOver,
onContextMenu,
onClipMouseEnter,
onClipMouseLeave,
onTrimHandleMouseDown,
onClipRemove,
onTogglePicker,
}) => {
return (
<div className="ep-clip-track" ref={trackRef} onDragOver={onEmptyDragOver}>
{/* 播放头 */}
{totalDuration > 0 && (
<div
className="ep-playhead"
style={{ left: currentTime * pps }}
onMouseDown={onPlayheadMouseDown}
>
<div className="ep-playhead-handle" />
</div>
)}
{clips.length === 0 ? (
<div className="ep-track-empty">
<div className="ep-track-empty-icon">🎬</div>
<div className="ep-track-empty-text"> + </div>
</div>
) : (
clips.map((clip, idx) => (
<ClipCard
key={clip.id}
clip={clip}
idx={idx}
isSelected={selectedClipId === clip.id}
isDragging={dragIdx === idx}
isDragOver={dragOverIdx === idx}
isHovered={hoveredClipId === clip.id}
pps={pps}
trimDragActive={trimDragActive}
showTrimHandles={showTrimHandles}
onDragStart={onDragStart}
onDragOver={onDragOver}
onDragEnd={onDragEnd}
onDrop={onDrop}
onSelect={onClipSelect}
onContextMenu={onContextMenu}
onMouseEnter={() => onClipMouseEnter(clip.id)}
onMouseLeave={onClipMouseLeave}
onTrimHandleMouseDown={onTrimHandleMouseDown}
onRemove={onClipRemove}
/>
))
)}
{/* ── 轨道末尾 "+" 添加卡片 ── */}
<div className="ep-track-add-card-wrapper">
<div
ref={addCardRef}
className="ep-track-add-card"
onClick={onTogglePicker}
title="添加片段"
>
+
</div>
</div>
</div>
)
}
@@ -1,49 +0,0 @@
import React from "react"
interface ContextMenuProps {
x: number
y: number
menuRef: React.RefObject<HTMLDivElement>
hasTrim: boolean
onSplit: () => void
onResetTrim: () => void
onDelete: () => void
}
export const ContextMenu: React.FC<ContextMenuProps> = ({
x,
y,
menuRef,
hasTrim,
onSplit,
onResetTrim,
onDelete,
}) => {
return (
<div
ref={menuRef}
className="ep-context-menu"
style={{
position: "fixed",
left: x,
top: y,
}}
>
<div className="ep-context-menu-item" onClick={onSplit}>
<span className="ep-context-menu-icon"></span>
<span></span>
</div>
{hasTrim && (
<div className="ep-context-menu-item" onClick={onResetTrim}>
<span className="ep-context-menu-icon"></span>
<span></span>
</div>
)}
<div className="ep-context-menu-divider" />
<div className="ep-context-menu-item ep-context-menu-item-danger" onClick={onDelete}>
<span className="ep-context-menu-icon">🗑</span>
<span></span>
</div>
</div>
)
}
@@ -1,27 +0,0 @@
import React from "react"
import { getRulerStep, MIN_TRACK_WIDTH } from "../../constants/timeline"
import { generateRulerMarks } from "../../utils/timeline"
interface TimeRulerProps {
totalDuration: number
pps: number
onClick: (e: React.MouseEvent<HTMLDivElement>) => void
}
export const TimeRuler: React.FC<TimeRulerProps> = ({ totalDuration, pps, onClick }) => {
const trackWidth = Math.max(totalDuration * pps, MIN_TRACK_WIDTH)
const step = getRulerStep(totalDuration)
const marks = generateRulerMarks(totalDuration, step)
return (
<div className="ep-time-ruler" onClick={onClick}>
<div className="ep-time-ruler-inner" style={{ width: trackWidth }}>
{marks.map((t) => (
<span key={t} className="ep-time-mark" style={{ left: `${t * pps}px` }}>
{t}s
</span>
))}
</div>
</div>
)
}
@@ -1,76 +0,0 @@
import React from "react"
import { formatTime } from "../../utils/timeline"
interface TimelineHeaderProps {
/** 总时长(秒) */
totalDuration: number
/** 缩放:每秒像素数 */
pps: number
/** 缩放变更回调 */
onZoomChange?: (pps: number) => void
/** 撤销最后一个片段 */
onUndoClip?: () => void
/** 片段数量 */
clipsCount: number
}
/**
* 时间线头部组件
* 包含标题、总时长、缩放控件和操作按钮
*/
export const TimelineHeader: React.FC<TimelineHeaderProps> = ({
totalDuration,
pps,
onZoomChange,
onUndoClip,
clipsCount,
}) => {
return (
<div className="ep-timeline-header">
<div className="ep-timeline-title">
<span>🎬 线</span>
<span className="ep-timeline-duration">: {formatTime(totalDuration)}</span>
</div>
<div className="ep-timeline-actions">
{/* 缩放控件 */}
<div className="ep-timeline-zoom">
<button
className="ep-zoom-btn"
onClick={() => onZoomChange?.(Math.max(10, pps - 10))}
title="缩小"
>
</button>
<input
type="range"
className="ep-zoom-slider"
min={10}
max={120}
step={5}
value={pps}
onChange={(e) => onZoomChange?.(Number(e.target.value))}
/>
<button
className="ep-zoom-btn"
onClick={() => onZoomChange?.(Math.min(120, pps + 10))}
title="放大"
>
+
</button>
<span className="ep-zoom-label">{pps}px/s</span>
</div>
<button
className="ep-timeline-action-btn"
onClick={onUndoClip}
title="删除最后一个片段"
disabled={clipsCount <= 1}
>
</button>
<button className="ep-timeline-action-btn" title="重做">
</button>
</div>
</div>
)
}
@@ -1,36 +0,0 @@
import React from "react"
import { formatTrimTime } from "../../utils/timeline"
interface TrimPreviewProps {
startTime: number
endTime: number
duration: number
x: number
y: number
}
export const TrimPreview: React.FC<TrimPreviewProps> = ({ startTime, endTime, duration, x, y }) => {
return (
<div
className="ep-trim-preview"
style={{
position: "fixed",
left: x + 12,
top: y - 40,
}}
>
<div className="ep-trim-preview-row">
<span className="ep-trim-preview-label"></span>
<span className="ep-trim-preview-value">{formatTrimTime(startTime)}</span>
</div>
<div className="ep-trim-preview-row">
<span className="ep-trim-preview-label"></span>
<span className="ep-trim-preview-value">{formatTrimTime(endTime)}</span>
</div>
<div className="ep-trim-preview-row ep-trim-preview-duration">
<span className="ep-trim-preview-label"></span>
<span className="ep-trim-preview-value">{formatTrimTime(duration)}</span>
</div>
</div>
)
}
@@ -1,71 +0,0 @@
import { useState, useCallback, useEffect, useRef } from "react"
interface UsePlayheadDragOptions {
/** 当前播放时间(秒) */
currentTime: number
/** 缩放:每秒像素数 */
pps: number
/** 总时长(秒) */
totalDuration: number
/** 播放头跳转回调 */
onSeek?: (time: number) => void
}
/**
* 播放头拖拽 Hook
* 封装播放头的 mousedown/mousemove/mouseup 拖拽逻辑
*/
export const usePlayheadDrag = ({ pps, totalDuration, onSeek }: UsePlayheadDragOptions) => {
const [playheadDragging, setPlayheadDragging] = useState(false)
const trackRef = useRef<HTMLDivElement>(null)
/* ── 播放头拖拽全局 mousemove/mouseup ── */
useEffect(() => {
if (!playheadDragging) return
const handleMouseMove = (e: MouseEvent) => {
const trackEl = trackRef.current
if (!trackEl) return
const rect = trackEl.getBoundingClientRect()
const x = e.clientX - rect.left + trackEl.scrollLeft
const time = Math.max(0, Math.min(x / pps, totalDuration))
onSeek?.(Math.round(time * 10) / 10)
}
const handleMouseUp = () => {
setPlayheadDragging(false)
}
document.addEventListener("mousemove", handleMouseMove)
document.addEventListener("mouseup", handleMouseUp)
return () => {
document.removeEventListener("mousemove", handleMouseMove)
document.removeEventListener("mouseup", handleMouseUp)
}
}, [playheadDragging, pps, totalDuration, onSeek])
/* ── 播放头拖拽开始 ── */
const handlePlayheadMouseDown = useCallback((e: React.MouseEvent) => {
e.preventDefault()
e.stopPropagation()
setPlayheadDragging(true)
}, [])
/* ── 标尺点击跳转播放头 ── */
const handleRulerClick = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
const rect = e.currentTarget.getBoundingClientRect()
const x = e.clientX - rect.left
const time = Math.max(0, Math.min(x / pps, totalDuration))
onSeek?.(Math.round(time * 10) / 10)
},
[pps, totalDuration, onSeek],
)
return {
trackRef,
playheadDragging,
handlePlayheadMouseDown,
handleRulerClick,
}
}
@@ -1,78 +0,0 @@
/**
* 图片水印配置组件
*/
import React from "react"
interface ImageWatermarkSectionProps {
imageUrl: string
localImageUrl: string
width: number | undefined
height: number | undefined
onImageUrlChange: (url: string) => void
onWidthChange: (val: number) => void
onHeightChange: (val: number) => void
}
const ImageWatermarkSection: React.FC<ImageWatermarkSectionProps> = ({
imageUrl,
localImageUrl,
width,
height,
onImageUrlChange,
onWidthChange,
onHeightChange,
}) => {
const displayUrl = localImageUrl || imageUrl || ""
return (
<div className="wp-section">
<div className="wp-field">
<label className="wp-field-label"> URL</label>
<input
className="wp-input"
type="text"
placeholder="https://example.com/logo.png"
value={displayUrl}
onChange={(e) => onImageUrlChange(e.target.value)}
/>
{displayUrl && (
<div className="wp-image-preview">
<img
src={displayUrl}
alt="水印预览"
onError={(e) => {
;(e.target as HTMLImageElement).style.display = "none"
}}
/>
</div>
)}
</div>
<div className="wp-field">
<label className="wp-field-label">0 </label>
<input
className="wp-input"
type="number"
min={0}
max={2000}
value={width ?? 0}
onChange={(e) => onWidthChange(Number(e.target.value))}
/>
</div>
<div className="wp-field">
<label className="wp-field-label">0 </label>
<input
className="wp-input"
type="number"
min={0}
max={2000}
value={height ?? 0}
onChange={(e) => onHeightChange(Number(e.target.value))}
/>
</div>
</div>
)
}
export default ImageWatermarkSection
@@ -1,107 +0,0 @@
/**
* 滚动水印配置组件
*/
import React from "react"
import type { ScrollDirection } from "../../types"
import { SCROLL_DIRECTION_OPTIONS } from "../../constants/watermark"
interface ScrollWatermarkSectionProps {
text: string | undefined
scrollDirection: ScrollDirection | undefined
scrollSpeed: number | undefined
fontSize: number | undefined
color: string | undefined
onTextChange: (text: string) => void
onScrollDirectionChange: (dir: ScrollDirection) => void
onScrollSpeedChange: (speed: number) => void
onFontSizeChange: (size: number) => void
onColorChange: (color: string) => void
}
const ScrollWatermarkSection: React.FC<ScrollWatermarkSectionProps> = ({
text,
scrollDirection,
scrollSpeed,
fontSize,
color,
onTextChange,
onScrollDirectionChange,
onScrollSpeedChange,
onFontSizeChange,
onColorChange,
}) => {
return (
<div className="wp-section">
<div className="wp-field">
<label className="wp-field-label"></label>
<textarea
className="wp-textarea"
placeholder="输入滚动水印文字"
rows={2}
value={text ?? ""}
onChange={(e) => onTextChange(e.target.value)}
/>
</div>
<div className="wp-field">
<label className="wp-field-label"></label>
<select
className="wp-select"
value={scrollDirection ?? "horizontal"}
onChange={(e) => onScrollDirectionChange(e.target.value as ScrollDirection)}
>
{SCROLL_DIRECTION_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
<div className="wp-field">
<label className="wp-field-label"></label>
<div className="wp-slider-row">
<input
className="wp-slider"
type="range"
min={10}
max={200}
value={scrollSpeed ?? 50}
onChange={(e) => onScrollSpeedChange(Number(e.target.value))}
/>
<span className="wp-slider-value">{scrollSpeed ?? 50}px/s</span>
</div>
</div>
<div className="wp-field">
<label className="wp-field-label"></label>
<div className="wp-slider-row">
<input
className="wp-slider"
type="range"
min={12}
max={72}
value={fontSize ?? 24}
onChange={(e) => onFontSizeChange(Number(e.target.value))}
/>
<span className="wp-slider-value">{fontSize ?? 24}px</span>
</div>
</div>
<div className="wp-field">
<label className="wp-field-label"></label>
<div className="wp-color-row">
<input
className="wp-color-input"
type="color"
value={color ?? "#ffffff"}
onChange={(e) => onColorChange(e.target.value)}
/>
<span className="wp-color-value">{color ?? "#ffffff"}</span>
</div>
</div>
</div>
)
}
export default ScrollWatermarkSection
@@ -1,67 +0,0 @@
/**
* 文字水印配置组件
*/
import React from "react"
interface TextWatermarkSectionProps {
text: string | undefined
fontSize: number | undefined
color: string | undefined
onTextChange: (text: string) => void
onFontSizeChange: (size: number) => void
onColorChange: (color: string) => void
}
const TextWatermarkSection: React.FC<TextWatermarkSectionProps> = ({
text,
fontSize,
color,
onTextChange,
onFontSizeChange,
onColorChange,
}) => {
return (
<div className="wp-section">
<div className="wp-field">
<label className="wp-field-label"></label>
<textarea
className="wp-textarea"
placeholder="输入水印文字内容"
rows={3}
value={text ?? ""}
onChange={(e) => onTextChange(e.target.value)}
/>
</div>
<div className="wp-field">
<label className="wp-field-label"></label>
<div className="wp-slider-row">
<input
className="wp-slider"
type="range"
min={12}
max={72}
value={fontSize ?? 24}
onChange={(e) => onFontSizeChange(Number(e.target.value))}
/>
<span className="wp-slider-value">{fontSize ?? 24}px</span>
</div>
</div>
<div className="wp-field">
<label className="wp-field-label"></label>
<div className="wp-color-row">
<input
className="wp-color-input"
type="color"
value={color ?? "#ffffff"}
onChange={(e) => onColorChange(e.target.value)}
/>
<span className="wp-color-value">{color ?? "#ffffff"}</span>
</div>
</div>
</div>
)
}
export default TextWatermarkSection
@@ -1,60 +0,0 @@
/**
* 水印通用设置组件(位置 + 透明度)
*/
import React from "react"
import type { WatermarkPosition } from "../../types"
import { POSITION_OPTIONS } from "../../constants/watermark"
interface WatermarkCommonSectionProps {
position: WatermarkPosition
opacity: number
onPositionChange: (pos: WatermarkPosition) => void
onOpacityChange: (opacity: number) => void
}
const WatermarkCommonSection: React.FC<WatermarkCommonSectionProps> = ({
position,
opacity,
onPositionChange,
onOpacityChange,
}) => {
return (
<div className="wp-section wp-common-section">
<div className="wp-section-divider" />
<div className="wp-common-title"></div>
<div className="wp-field">
<label className="wp-field-label"></label>
<select
className="wp-select"
value={position}
onChange={(e) => onPositionChange(e.target.value as WatermarkPosition)}
>
{POSITION_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
<div className="wp-field">
<label className="wp-field-label"></label>
<div className="wp-slider-row">
<input
className="wp-slider"
type="range"
min={0}
max={1}
step={0.05}
value={opacity}
onChange={(e) => onOpacityChange(Number(e.target.value))}
/>
<span className="wp-slider-value">{Math.round(opacity * 100)}%</span>
</div>
</div>
</div>
)
}
export default WatermarkCommonSection
@@ -1,30 +0,0 @@
/**
* 水印类型 Tab 组件
*/
import React from "react"
import type { WatermarkType } from "../../types"
import { WATERMARK_TABS } from "../../constants/watermark"
interface WatermarkTypeTabsProps {
activeTab: WatermarkType
onTypeChange: (type: WatermarkType) => void
}
const WatermarkTypeTabs: React.FC<WatermarkTypeTabsProps> = ({ activeTab, onTypeChange }) => {
return (
<div className="wp-tabs">
{WATERMARK_TABS.map((tab) => (
<button
key={tab.key}
className={`wp-tab${activeTab === tab.key ? " active" : ""}`}
onClick={() => onTypeChange(tab.key)}
>
<span className="wp-tab-icon">{tab.icon}</span>
<span className="wp-tab-label">{tab.label}</span>
</button>
))}
</div>
)
}
export default WatermarkTypeTabs
@@ -1,12 +0,0 @@
import type { TemplateMode } from "@/api/editing-planner"
/* ──────────── 常量 ──────────── */
export const MODE_LIST: { key: TemplateMode; label: string; icon: string }[] = [
{ key: "pip", label: "混剪", icon: "🖼️" },
{ key: "voice_over", label: "人物口播", icon: "🎙️" },
{ key: "one_take", label: "一镜到底", icon: "🎥" },
{ key: "voice_pip", label: "口播+混剪", icon: "🎭" },
]
export const FILTER_CATEGORIES = ["全部", "种草", "知识", "日常", "推荐"]
@@ -1,34 +0,0 @@
/**
* ClipPropertiesPanel 常量定义
*/
import type { ClipType } from "@/pages/editing-planner/types"
export const POSITION_OPTIONS = [
{ value: "top", label: "顶部" },
{ value: "center", label: "居中" },
{ value: "bottom", label: "底部" },
]
export const FONT_OPTIONS = [
"思源黑体",
"思源宋体",
"苹方",
"PingFang",
"微软雅黑",
"楷体",
"华康俪金黑",
]
export const ANIMATION_OPTIONS = [
{ value: "none", label: "无" },
{ value: "fade", label: "淡入淡出" },
{ value: "slide", label: "滑动" },
{ value: "typewriter", label: "打字机" },
]
/** 片段类型图标/标签 */
export const CLIP_TYPE_ICONS: Record<ClipType, string> = { voice: "🎙️", pip: "🖼️" }
export const CLIP_TYPE_LABELS: Record<ClipType, string> = {
voice: "口播",
pip: "混剪",
}
@@ -1,42 +0,0 @@
/**
* FilterPanel 相关常量
*/
import type { FilterPreset } from "../types"
/** 所有预设列表 */
export const PRESET_LIST: FilterPreset[] = [
"none",
"original",
"fresh",
"warm",
"cool",
"vintage",
"cinema",
"bw",
"sunshine",
"film",
]
/** 预设对应的示例渐变色(用于视觉预览) */
export const PRESET_GRADIENTS: Record<FilterPreset, string> = {
none: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
original: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
fresh: "linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)",
warm: "linear-gradient(135deg, #f093fb 0%, #f5576c 100%)",
cool: "linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)",
vintage: "linear-gradient(135deg, #c79081 0%, #dfa579 100%)",
cinema: "linear-gradient(135deg, #2c3e50 0%, #4ca1af 100%)",
bw: "linear-gradient(135deg, #434343 0%, #000000 100%)",
sunshine: "linear-gradient(135deg, #f6d365 0%, #fda085 100%)",
film: "linear-gradient(135deg, #8e9eab 0%, #eef2f3 100%)",
}
/** 手动调节项配置 */
export const MANUAL_ADJUST_ITEMS = [
{ key: "brightness", label: "亮度", min: -100, max: 100 },
{ key: "contrast", label: "对比度", min: -100, max: 100 },
{ key: "saturation", label: "饱和度", min: -100, max: 100 },
{ key: "temperature", label: "色温", min: -100, max: 100 },
{ key: "tint", label: "色调", min: -100, max: 100 },
{ key: "sharpness", label: "锐度", min: 0, max: 100 },
] as const

Some files were not shown because too many files have changed in this diff Show More