diff --git a/.gitignore b/.gitignore
index af7bdaf..79ec79b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -362,3 +362,10 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
/.claude
+
+# Release packages / build artifacts (not tracked in git)
+Releases/
+dist/
+*.zip
+*.rar
+*.7z
diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
index 0000000..0bbc980
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,85 @@
+# 本機分支相對 GitHub (Babyhamsta/Yoable, origin/master) 的變更
+
+比較基準:`origin/master` @ `37ec380`("Update README installation requirements")
+本機領先 5 個已提交的 commit,另外工作區還有尚未提交的變更。
+
+## 一、已提交但尚未推送的 5 個 commit
+
+1. **Add automatic UI language detection based on system locale** (`5a5664d`)
+ - 首次啟動且尚無語言偏好設定時,自動偵測系統 UI 語系並套用最接近的支援語言
+ - 中文依文字系統分流:繁體(zh-Hant/zh-TW/zh-HK/zh-MO)→ zh-TW,簡體 → zh-CN
+ - 系統語言不支援時退回英文;使用者手動選擇的語言永遠優先,不會被自動偵測覆蓋
+
+2. **Ignore release packages (zip/rar/7z) in git** (`11029bb`)
+ - `.gitignore` 新增規則,避免發布用的壓縮檔被提交進版本庫
+
+3. **Use dist/ folder for release packages and ignore it** (`2fbe1e4`)
+ - 發布封裝統一輸出到 `dist/` 資料夾,並加入 `.gitignore`
+
+4. **Fix label exports and YouTube downloads** (`c2c5fdd`)
+ - 修正標籤匯出流程的問題
+ - 修正 YouTube 下載器(`YoutubeDownloader.cs`)的相關錯誤
+ - 調整 `NewProjectDialog.xaml` 版面
+ - 專案檔版本號更新
+
+5. **Update README installation requirements** (`37ec380`)
+ - 更新 README 的安裝需求說明
+
+## 二、工作區尚未提交的變更
+
+### 1. 圖片載入效能優化(大型專案 8000~10000 張圖片時的卡頓改善)
+
+**問題**:
+- 每次切換圖片時,在 **UI 執行緒同步解碼**整張圖片,且沒有任何快取,切回看過的圖也要重新解碼
+- 開啟大型專案時,即使 `.yoable` 專案檔的 `ImageReference` 早就有 `Width`/`Height` 欄位,存檔時卻從未寫入,導致每次開專案都要對上萬張圖逐一開檔解碼標頭來取得尺寸
+
+**解法**:
+- 新增 `Managers/ImageCacheManager.cs`:記憶體內的 LRU 點陣圖快取(預設上限 512MB / 512 張),並支援背景預載(prefetch)
+ - 快取的 `BitmapImage` 會 `Freeze()`,可在背景執行緒解碼、UI 執行緒直接顯示
+- `ImageManager.cs`:
+ - 新增 `Cache` 屬性(`ImageCacheManager`)
+ - 新增 `AddImageWithKnownSize(path, dimensions)`:跳過解碼標頭,直接用已知尺寸登記圖片
+ - `LoadImagesFromPathsAsync` 新增 `clearExisting` 參數,支援與"已知尺寸快速路徑"混合載入
+- `UI/DrawingCanvas.cs`:`LoadImage` 拆出一個接受已解碼 `ImageSource` 的多載,避免重複解碼
+- `MainWindow.xaml.cs`:
+ - `ImageListBox_SelectionChanged` 改為 `async`:快取命中則瞬間顯示;未命中則在背景執行緒解碼,不卡 UI
+ - 加入 generation 計數器,快速連續切圖時會捨棄過時的解碼結果,避免標籤存錯檔案
+ - 新增 `PrefetchNeighboringImages()`:每次切圖後自動背景預載後續 12 張、前面 4 張圖片,讓連續標註時下一張幾乎都是瞬間顯示
+- `ProjectManager.cs`:
+ - 匯出專案資料時,`ImageReference` 現在會寫入圖片的 `Width`/`Height`
+ - 匯入專案資料時,優先使用已存的尺寸直接註冊圖片(完全跳過解碼),只有舊專案檔(尺寸為 0x0)才會退回原本的解碼路徑
+ - `SaveProjectAs` 改為 `SaveProjectAsAsync`:資料夾建立與標籤檔複製移到背景執行緒,避免另存新檔時 UI 卡住;並修正失敗時專案路徑未回滾的問題
+
+### 2. 標籤「變更類別」功能
+
+- `MainWindow.xaml`:標籤清單(`LabelListBox`)的每個項目新增一顆「變更類別」圖示按鈕,並支援雙擊清單項目開啟類別選單
+- `MainWindow.xaml.cs`:
+ - 新增 `ChangeLabelClass_Click` / `LabelListBox_MouseDoubleClick` / `ShowChangeClassMenu`
+ - 以 `ContextMenu` 列出專案所有類別(含顏色色塊),點選後即時套用到該標籤並刷新畫布與清單
+- 多語系字串新增 `Main_ChangeClass`(English/日本語/Русский/简体中文/繁體中文皆已補上)
+
+### 3. 手動標註進度標記(Manual Labeling Progress)
+
+- `Models/ProjectData.cs`:新增 `ManualProgressImageFile` 欄位,記錄使用者手動標註進度的檔名檢查點
+- `MainWindow.xaml`:選單新增「設定目前為手動進度」/「跳至手動進度」兩個選項
+- `MainWindow.xaml.cs`:
+ - `SetManualProgress_Click`:將目前選取圖片設為進度檢查點,並重新計算所有圖片狀態
+ - `JumpToManualProgress_Click`:跳轉並選取到檢查點對應的圖片(會先清除篩選以確保可見)
+ - `IsAtOrBeforeManualProgress`:判斷某檔名是否在檢查點(依檔名排序)之前或等於檢查點
+ - 狀態判定邏輯(`DetermineImageStatus`)納入此檢查點:已在檢查點之前的 AI/匯入標籤,即使重新匯入也維持「已驗證」狀態,不會被誤判為需要複審
+- `ProjectManager.cs` 的狀態校正邏輯同步套用此規則
+- 新增對應多語系字串:`Menu_SetManualProgress`、`Menu_JumpManualProgress`、`Msg_ManualProgress_*` 系列(5 支語言檔皆已同步)
+
+### 4. 匯入標籤去重合併(Idempotent Import)
+
+- `Managers/LabelManager.cs`:
+ - 新增 `IsSameImportedLabel`:以 0.01 的座標容差判斷兩個標籤是否視為同一個框(因為 YOLO 匯出座標常有四捨五入誤差)
+ - 新增 `MergeImportedLabels`:合併既有標籤與匯入標籤時,先去除既有重複,再避免加入與既有標籤相同的匯入標籤
+ - 套用到兩處標籤匯入流程,確保重複匯入同一份已匯出的資料集不會造成標籤重複堆疊
+
+### 5. 其他小修正
+
+- `MainWindow.xaml.cs`:
+ - 「另存新檔」對話框補上 `DefaultExt`/`AddExtension`,避免使用者輸入檔名時漏副檔名
+ - 修正 `IsDescendantOf`(或同類的視覺樹判斷輔助方法)在 `OriginalSource` 為 `Run`(TextBlock 內的行內文字,非 Visual 節點)時,呼叫 `VisualTreeHelper.GetParent` 會拋例外的問題;改為先判斷是否為 `Visual`/`Visual3D` 節點,否則退回邏輯樹(`LogicalTreeHelper`)向上查找
+- `SettingsWindow.xaml`:語言下拉選單的顯示樣板,將原本用多個 `Run` 拼接語言名稱+國際化名稱的寫法,改為 `StackPanel` + `StringFormat` 綁定,避免留白/格式問題
diff --git a/README.md b/README.md
index 8fc5b07..9491e62 100644
--- a/README.md
+++ b/README.md
@@ -31,8 +31,12 @@ For non-WPF version you can build the legacy source or use v1.2.0 from releases
### 📥 Installation
+**Requirement:** Yoable needs the **[.NET 9 Desktop Runtime (x64)](https://dotnet.microsoft.com/download/dotnet/9.0/runtime?cid=getdotnetcore&runtime=desktop)** installed. If the app does not launch, install it first.
+
+需求:Yoable 需要先安裝 **[.NET 9 Desktop Runtime (x64)](https://dotnet.microsoft.com/download/dotnet/9.0/runtime?cid=getdotnetcore&runtime=desktop)**。如果程式無法開啟,請先安裝它。
+
1. Download the latest release from our [GitHub Releases](https://github.com/Babyhamsta/Yoable/releases).
-2. Download and run Yoable (No install required!).
+2. Extract the zip and run `YoableWPF.exe`.
3. (Optional) Load a **YOLO v5/v8/v11 (ONNX)** model for AI-assisted labeling.
### 🛠️ How to Use
diff --git a/YoableWPF/App.xaml b/YoableWPF/App.xaml
index 3674560..406af3a 100644
--- a/YoableWPF/App.xaml
+++ b/YoableWPF/App.xaml
@@ -8,6 +8,11 @@
+
+
+
+
+
diff --git a/YoableWPF/App.xaml.cs b/YoableWPF/App.xaml.cs
index b943519..7d77989 100644
--- a/YoableWPF/App.xaml.cs
+++ b/YoableWPF/App.xaml.cs
@@ -26,6 +26,19 @@ protected override void OnStartup(StartupEventArgs e)
// The LanguageManager constructor already loads the language from settings
_ = LanguageManager.Instance;
+ // Apply saved theme + accent before any window shows so the
+ // StartupWindow (and everything after) respects them
+ ModernWpf.ThemeManager.Current.ApplicationTheme = YoableWPF.Properties.Settings.Default.DarkTheme
+ ? ModernWpf.ApplicationTheme.Dark
+ : ModernWpf.ApplicationTheme.Light;
+ try
+ {
+ ModernWpf.ThemeManager.Current.AccentColor =
+ (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(
+ YoableWPF.Properties.Settings.Default.FormAccent);
+ }
+ catch { /* invalid stored hex — keep default accent */ }
+
// Show the startup window (combined splash screen and project selector)
var startupWindow = new StartupWindow();
startupWindow.Show();
diff --git a/YoableWPF/AugmentationDialog.xaml b/YoableWPF/AugmentationDialog.xaml
new file mode 100644
index 0000000..b114045
--- /dev/null
+++ b/YoableWPF/AugmentationDialog.xaml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/YoableWPF/AugmentationDialog.xaml.cs b/YoableWPF/AugmentationDialog.xaml.cs
new file mode 100644
index 0000000..00a1a5c
--- /dev/null
+++ b/YoableWPF/AugmentationDialog.xaml.cs
@@ -0,0 +1,110 @@
+using System;
+using System.IO;
+using System.Windows;
+using Microsoft.Win32;
+using YoableWPF.Managers;
+
+namespace YoableWPF
+{
+ public partial class AugmentationDialog : Window
+ {
+ public AugmentationOptions Options { get; private set; }
+
+ public AugmentationDialog(string defaultOutputDir, int variants, int seed, bool allowCurrentScope)
+ {
+ InitializeComponent();
+
+ OutputPathBox.Text = defaultOutputDir ?? string.Empty;
+ VariantsInput.Value = Math.Clamp(variants, 1, 50);
+ SeedInput.Value = Math.Max(0, seed);
+
+ if (!allowCurrentScope)
+ {
+ ScopeCurrent.IsEnabled = false;
+ ScopeAll.IsChecked = true;
+ }
+
+ UpdateValidationState();
+ }
+
+ private void Browse_Click(object sender, RoutedEventArgs e)
+ {
+ var dialog = new OpenFileDialog
+ {
+ ValidateNames = false,
+ CheckFileExists = false,
+ CheckPathExists = true,
+ FileName = "Select Folder"
+ };
+
+ if (!string.IsNullOrEmpty(OutputPathBox.Text) && Directory.Exists(OutputPathBox.Text))
+ dialog.InitialDirectory = OutputPathBox.Text;
+
+ if (dialog.ShowDialog() == true)
+ {
+ string folderPath = Path.GetDirectoryName(dialog.FileName);
+ if (!string.IsNullOrEmpty(folderPath))
+ OutputPathBox.Text = folderPath;
+ }
+ }
+
+ private void Options_Changed(object sender, RoutedPropertyChangedEventArgs