diff --git a/.agent/DECISIONS.md b/.agent/DECISIONS.md index 9834fda2..737eca41 100644 --- a/.agent/DECISIONS.md +++ b/.agent/DECISIONS.md @@ -12,6 +12,18 @@ Record meaningful technical decisions here. Use one entry per decision. ## Entries +- Date: 2026-09-02 +- Decision: Add a reusable mobile-first option confirmation popup that accepts a title, content, and typed display/value options, lays actions out as full-width touch targets, and returns the selected stable string value or null when dismissed. Require this confirmation before singer uninstall. +- Rationale: The existing exit-editor confirmation is fixed to editor-specific labels and a byte register. A generic string-valued popup supports localized labels without coupling callers to displayed text and keeps destructive operations explicit on narrow screens. +- Alternatives considered: Reuse the fixed exit-editor popup; add a singer-only confirmation popup; compare localized option labels as results. +- Impacted areas: Mobile popup controls/services/theme tokens, singer uninstall interaction, and localization. + +- Date: 2026-09-02 +- Decision: Centralize installed-singer removal in `SingerManager.UninstallSinger`. Remove directory-based voicebanks recursively and single-file Vogen packages directly, release singer memory first, and always rescan installed singers in `finally`. Run the operation from the singer detail page inside the shared loading popup. +- Rationale: Core owns singer location semantics and the installed-singer index, while Mobile owns progress and error presentation. Keeping the rescan in Core guarantees that success and failure paths expose the current filesystem state. +- Alternatives considered: Delete `USinger.Location` directly in the Mobile ViewModel; refresh only after successful deletion; route removal through a platform storage service. +- Impacted areas: Core singer lifecycle, Mobile singer detail interaction, and singer-detail localization. + - Date: 2026-09-01 - Decision: Add a dedicated batch-lyric popup to the Hand, Note, and MultiSelect piano-roll context menus. Initialize it from the earliest selected note through the end of the active voice part, and apply one undoable multi-note lyric command either sequentially or to the selected-note snapshot. - Rationale: A focused text editor supports fast paste-and-replace workflows while preserving the current note selection and Core's existing undo/validation behavior. Keeping parsing and scope selection in the Mobile ViewModel avoids changes to upstream-derived Core. diff --git a/OpenUtau.Core/SingerManager.cs b/OpenUtau.Core/SingerManager.cs index 37c94fa1..9a1d7919 100644 --- a/OpenUtau.Core/SingerManager.cs +++ b/OpenUtau.Core/SingerManager.cs @@ -52,6 +52,41 @@ public USinger GetSinger(string name) { return null; } + /// + /// 卸载歌手,并在操作结束后重新扫描已安装歌手。 + /// + /// 要卸载的已安装歌手。 + public void UninstallSinger(USinger singer) + { + try + { + ArgumentNullException.ThrowIfNull(singer); + string location = singer.Location; + if (string.IsNullOrWhiteSpace(location)) + { + throw new InvalidOperationException("Singer location is empty."); + } + + singer.FreeMemory(); + if (Directory.Exists(location)) + { + Directory.Delete(location, true); + } + else if (File.Exists(location)) + { + File.Delete(location); + } + else + { + throw new DirectoryNotFoundException($"Singer location does not exist: {location}"); + } + } + finally + { + SearchAllSingers(); + } + } + public void ScheduleReload(USinger singer) { reloadQueue.Enqueue(singer); ScheduleReload(); diff --git a/OpenUtauMobile/Assets/Lang/Strings.en.resx b/OpenUtauMobile/Assets/Lang/Strings.en.resx index 913ba85e..fe82f27e 100644 --- a/OpenUtauMobile/Assets/Lang/Strings.en.resx +++ b/OpenUtauMobile/Assets/Lang/Strings.en.resx @@ -612,6 +612,21 @@ Delete Singer + + Uninstalling singer… + + + Uninstalled {0} + + + Failed to uninstall {0}. + + + Uninstall singer? + + + Uninstall {0} and permanently remove all of its files? This action cannot be undone. + Undo diff --git a/OpenUtauMobile/Assets/Lang/Strings.ja.resx b/OpenUtauMobile/Assets/Lang/Strings.ja.resx index 3aaaa136..a10bd56c 100644 --- a/OpenUtauMobile/Assets/Lang/Strings.ja.resx +++ b/OpenUtauMobile/Assets/Lang/Strings.ja.resx @@ -609,6 +609,21 @@ シンガーを削除 + + シンガーをアンインストールしています… + + + {0} をアンインストールしました + + + {0} のアンインストールに失敗しました。 + + + シンガーをアンインストールしますか? + + + {0} をアンインストールし、すべてのファイルを完全に削除しますか?この操作は元に戻せません。 + 元に戻す diff --git a/OpenUtauMobile/Assets/Lang/Strings.ru.resx b/OpenUtauMobile/Assets/Lang/Strings.ru.resx index 962913d1..e45fcfb5 100644 --- a/OpenUtauMobile/Assets/Lang/Strings.ru.resx +++ b/OpenUtauMobile/Assets/Lang/Strings.ru.resx @@ -535,6 +535,21 @@ Удалить банк + + Удаление банка… + + + Банк {0} удалён + + + Не удалось удалить банк {0}. + + + Удалить банк? + + + Удалить банк {0} и навсегда стереть все его файлы? Это действие нельзя отменить. + Отменить diff --git a/OpenUtauMobile/Assets/Lang/Strings.uk.resx b/OpenUtauMobile/Assets/Lang/Strings.uk.resx index 04e488ec..97e82bfa 100644 --- a/OpenUtauMobile/Assets/Lang/Strings.uk.resx +++ b/OpenUtauMobile/Assets/Lang/Strings.uk.resx @@ -535,6 +535,21 @@ Видалити банк + + Видалення банку… + + + Банк {0} видалено + + + Не вдалося видалити банк {0}. + + + Видалити банк? + + + Видалити банк {0} і назавжди стерти всі його файли? Цю дію неможливо скасувати. + Скасувати diff --git a/OpenUtauMobile/Assets/Lang/Strings.zh-Hans.resx b/OpenUtauMobile/Assets/Lang/Strings.zh-Hans.resx index 604b2729..d2b48582 100644 --- a/OpenUtauMobile/Assets/Lang/Strings.zh-Hans.resx +++ b/OpenUtauMobile/Assets/Lang/Strings.zh-Hans.resx @@ -612,6 +612,21 @@ 删除歌手 + + 正在卸载歌手… + + + 已卸载 {0} + + + 卸载 {0} 失败。 + + + 确认卸载歌手? + + + 确定卸载 {0} 并永久移除其全部文件吗?此操作不可撤销。 + 撤销 diff --git a/OpenUtauMobile/Controls/ErrorDialogPopup.axaml b/OpenUtauMobile/Controls/ErrorDialogPopup.axaml index 7da6d96c..a6a6f517 100644 --- a/OpenUtauMobile/Controls/ErrorDialogPopup.axaml +++ b/OpenUtauMobile/Controls/ErrorDialogPopup.axaml @@ -12,9 +12,7 @@ Classes="PopupDialogRoot"> - M12,2 C6.48,2 2,6.48 2,12 C2,17.52 6.48,22 12,22 C17.52,22 22,17.52 22,12 C22,6.48 17.52,2 12,2 Z M13,17 L11,17 L11,15 L13,15 L13,17 Z M13,13 L11,13 L11,7 L13,7 L13,13 Z M7.41,8.59 L12,13.17 L16.59,8.59 L18,10 L12,16 L6,10 L7.41,8.59 Z - M7.41,15.41 L12,10.83 L16.59,15.41 L18,14 L12,8 L6,14 L7.41,15.41 Z diff --git a/OpenUtauMobile/Controls/NotesCanvas.cs b/OpenUtauMobile/Controls/NotesCanvas.cs index a5cf7b7f..5d9a8de5 100644 --- a/OpenUtauMobile/Controls/NotesCanvas.cs +++ b/OpenUtauMobile/Controls/NotesCanvas.cs @@ -395,7 +395,7 @@ private static void DrawNoteResizeHandle(DrawingContext context, Rect noteRect) private void RenderFinalPitch(int leftTick, int rightTick, DrawingContext context) { if (ViewModel == null || Part == null) return; - IPen pen = ThemeResources.GetPen("Sem.Color.Outline", 2); + IPen pen = ViewModel.EditMode == PianoRollEditMode.PitchPen ? ThemeResources.GetPen("Sem.Color.Primary", 2) : ThemeResources.GetPen("Sem.Color.Outline", 2); StreamGeometry geometry = new(); bool hasVisibleSegment = false; lock (Part) diff --git a/OpenUtauMobile/Controls/OptionConfirmPopup.axaml b/OpenUtauMobile/Controls/OptionConfirmPopup.axaml new file mode 100644 index 00000000..adbaec02 --- /dev/null +++ b/OpenUtauMobile/Controls/OptionConfirmPopup.axaml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -235,4 +235,4 @@ - \ No newline at end of file +