MCDVoiceCooldown 后续结构优化路线图
版本记录：从 1.0.86 开始逐步整理，当前基准：1.0.116
用途：这个文件只作为开发/维护说明，不在 TOC 中加载，不影响游戏运行。

============================================================
一、已经完成的结构优化
============================================================

1. 第一阶段：基础拆分
   已新增：
   - Core/Constants.lua
   - Core/Utils.lua
   - Core/MediaCatalog.lua

   已完成内容：
   - 常量统一管理
   - 通用工具函数统一管理
   - 内置音效列表、SharedMedia 声音解析、音效路径规范化独立管理

2. 第二阶段：运行时主文件拆分
   已新增：
   - Core/Resolver.lua
   - Core/Notifier.lua
   - Core/Runtime.lua

   已完成内容：
   - 技能/物品/天赋解析拆到 Resolver
   - TTS、音效、SharedMedia 播放拆到 Notifier
   - 固定 CD 计时、OnUpdate 自动启停、延迟施法成功提醒队列拆到 Runtime

3. 第三阶段：数据与配置拆分
   已新增：
   - Core/CollectionStore.lua
   - Core/ImportExport.lua
   - Core/EntryStore.lua
   - Core/OptionsState.lua

   已完成内容：
   - 合集结构、合集引用、合集嵌套检测拆到 CollectionStore
   - 导入/导出、AceSerializer、LibDeflate 拆到 ImportExport
   - 语音条目保存/删除/读取/自动填充拆到 EntryStore
   - 当前编辑状态、职业/专精范围、编辑器字段同步拆到 OptionsState

4. UI 兼容修复
   已新增：
   - UI/ScrollBar.lua

   已完成内容：
   - 统一滚动条创建
   - 修复 UIPanelScrollBarTemplate 在自定义下拉菜单中 SetValue 报错
   - SavedList 与 Widgets 共用滚动条工具

5. 已修复的关键问题
   - 新增合集无法使用：补上 ResolveCollectionIcon
   - 自定义下拉菜单滚动条报错：新增 UI/ScrollBar.lua
   - 右键合集导出报错：修复 ImportExport 内 IsSameScopeGroupRef 转发
   - 合集收拢卡顿：收拢时优先使用缓存行隐藏子项，减少同步全量刷新
   - 编辑弹窗延时秒数丢失：切换声音来源前先 PullFromWidgets，避免刷新覆盖未保存输入
   - 新增时 CD提示/施法成功输入互相带入：为两个标签页加入独立临时草稿

6. SavedList 第一轮拆分
   已新增：
   - UI/SavedListRows.lua
   - UI/SavedListBuilder.lua

   已完成内容：
   - 行文本生成拆到 SavedListRows
   - 已保存列表数据构建、合集折叠缓存处理拆到 SavedListBuilder
   - SavedList:Refresh() 保留为旧入口，只负责调用 Builder 并渲染
   - 右键菜单中的展开/收拢改为走 SavedList:ToggleGroupCollapsed()，避免绕过缓存优化


7. SavedList 第二轮拆分
   已新增：
   - UI/SavedListDragDrop.lua
   - UI/SavedListContextMenu.lua

   已完成内容：
   - 拖拽预览、目标判定、幽灵框、拖放高亮拆到 SavedListDragDrop
   - 右键语音菜单、右键合集菜单、菜单遮罩与定位拆到 SavedListContextMenu
   - SavedList.lua 继续保留 SelectKey、FindDropTarget、Refresh 等兼容入口
   - SavedList.lua 进一步缩小为列表创建、选择、渲染与刷新入口



8. EditorFrame 第一轮拆分
   已新增：
   - UI/EditorLayout.lua
   - UI/EditorFields.lua

   已完成内容：
   - 编辑弹窗的标签、控件摆放、模块卡片、TTS 语速滑条创建拆到 EditorLayout
   - 职业/专精/音效下拉列表缓存、字段 Pull/Push、语言刷新拆到 EditorFields
   - EditorFrame.lua 继续保留 EnsureFrame、OpenForNew、OpenForEdit、Refresh、Close 等兼容入口
   - 后续新增字段时优先改 EditorFields / EditorLayout，减少直接修改 EditorFrame.lua



9. 导入刷新优化
   已新增：
   - Core/ImportCodec.lua
   - Core/ImportRefreshScheduler.lua

   已完成内容：
   - 导入字符串编解码、压缩、解压拆到 ImportCodec
   - 导入后物品补全、运行时重建、施法成功重建、嗜血重建、列表刷新改为分帧调度
   - 导入成功后不再由 MainFrame 立即重复刷新列表，减少完整配置导入时的同步卡顿

10. MainFrame / SavedList 轻量拆分
   已新增：
   - UI/MainFrameRefresh.lua
   - UI/SavedListSelection.lua

   已完成内容：
   - 主窗口刷新、按钮状态刷新、刷新请求合并拆到 MainFrameRefresh
   - SavedList 选择状态重绘拆到 SavedListSelection
   - 选择普通条目时继续只重绘旧选中行和新选中行，不触发全量列表重建
   - MainFrame.lua 和 SavedList.lua 进一步缩小为主体创建与兼容入口


11. Widgets / SavedListLayout 继续拆分
   已新增：
   - Core/SavedListLayout.lua
   - UI/WidgetDropdown.lua

   已完成内容：
   - 已保存列表布局构建从 Core/AceOptions.lua 拆到 SavedListLayout
   - 统一下拉菜单、滚动弹出层、下拉行选择/悬浮逻辑从 UI/Widgets.lua 拆到 WidgetDropdown
   - AceOptions.lua 和 Widgets.lua 均保留旧入口，外部调用不变

============================================================
二、后续优化必须遵守的原则
============================================================

1. 先修 bug，再继续拆结构。
   如果测试中出现报错、功能失效、卡顿，必须先修复，再继续下一步优化。

2. 每次只拆一个职责。
   不要一次性同时改 Runtime、SavedList、EditorFrame、导入导出。

3. 尽量保留旧入口。
   UI 层已经大量调用 NS.AceOptions:* 和 NS.API:*。
   拆分模块时，旧入口要保留为转发函数，避免 UI 大面积跟着改。

4. 不改变保存数据结构，除非明确做版本迁移。
   当前 DB 结构包含：
   - 普通语音条目
   - castSuccess 旧版兼容数据
   - root 顺序
   - groups 合集结构
   - collapsed 折叠状态

5. 不回头读取 12.x 容易触发秘密值/污染的 API。
   保持当前模型：
   - UNIT_SPELLCAST_SUCCEEDED
   - 固定 CD
   - 用户输入 CD
   - 不读真实冷却
   - 不读急速
   - 不依赖 IsUsableSpell

6. UI 优化优先使用 Blizzard 原生控件风格。
   不要重新引入自绘风格混乱的控件。

============================================================
三、下一步建议优化顺序
============================================================

【步骤 1】继续优化 SavedList 性能（进行中，已完成 Rows / Builder / DragDrop / ContextMenu 拆分）
目标文件：
- UI/SavedList.lua

建议新增文件：
- UI/SavedListBuilder.lua
- UI/SavedListRows.lua
- UI/SavedListContextMenu.lua
- UI/SavedListDragDrop.lua

优化目标：
1. 将列表数据构建与行显示分离。
2. 展开/收拢合集时尽量只更新受影响行，而不是每次全量重建。
3. 折叠合集时不要解析技能名称、物品名称、图标、声音文本。
4. 右键菜单逻辑从 SavedList.lua 拆出。
5. 拖拽排序逻辑从 SavedList.lua 拆出。
6. 保留 SavedList:Refresh() 作为旧入口。

测试重点：
- 合集展开/收拢是否流畅
- 合集里大量条目时是否还卡顿
- 拖入合集/拖出合集是否正常
- 右键导出/删除/重命名是否正常
- 折叠状态红/黄/绿状态是否正确

------------------------------------------------------------

【步骤 2】拆 EditorFrame 字段与布局（进行中，已完成 EditorLayout / EditorFields / EditorSoundFields 拆分）
目标文件：
- UI/EditorFrame.lua

建议新增文件：
- UI/EditorFields.lua
- UI/EditorSoundFields.lua
- UI/EditorLayout.lua

优化目标：
1. 把普通字段、TTS 字段、音效字段、施法成功字段分组。
2. 下拉菜单、输入框、滑条统一走 UI/Widgets.lua。
3. 减少 EditorFrame.lua 直接操作 state 的地方。
4. 让以后新增字段只需要改一个字段注册表，而不是到处找控件。

测试重点：
- 新增普通冷却语音
- 新增施法成功语音
- TTS 字段保存
- 内置音效字段保存
- SharedMedia 字段保存
- 自定义路径字段保存
- 关闭再打开弹窗字段是否正确回填

------------------------------------------------------------

【步骤 3】拆 OptionsController
目标文件：
- Core/AceOptions.lua

建议新增文件：
- Core/OptionsController.lua
- Core/TestController.lua

优化目标：
1. AceOptions.lua 最终只保留兼容入口。
2. 保存、删除、导入、导出、测试、刷新等入口统一走 OptionsController。
3. TTS 测试、音效测试、技能测试等按钮逻辑拆到 TestController。
4. AceOptions.lua 不再直接承载复杂业务逻辑。

测试重点：
- 所有按钮是否仍然有效
- 保存/删除/导入/导出是否有效
- TTS 测试是否有效
- 音效测试是否有效
- 技能 ID 自动填充是否有效

------------------------------------------------------------

【步骤 4】继续拆 Runtime 事件层
目标文件：
- MCDVoiceCooldown.lua
- Core/Runtime.lua

建议新增文件：
- Core/EventHub.lua
- Core/CastSuccess.lua
- Core/Bloodlust.lua
- Core/PublicAPI.lua

优化目标：
1. MCDVoiceCooldown.lua 最终只保留 AddOn 初始化。
2. 事件注册/分发放到 EventHub。
3. 施法成功提醒匹配逻辑放到 CastSuccess。
4. 嗜血/英勇/时间扭曲提醒放到 Bloodlust。
5. NS.API 对外接口集中到 PublicAPI。

测试重点：
- 固定 CD 到期提醒
- 施法成功提醒
- 延迟施法成功提醒
- 嗜血提醒
- 切专精后运行时配置重建
- 进入战斗/离开战斗后 OnUpdate 是否自动启停

------------------------------------------------------------

【步骤 5】建立统一测试清单
建议新增文件：
- Docs/TestChecklist_CN.txt

内容建议：
1. 基础启动测试
2. 新增/编辑/删除语音测试
3. 合集测试
4. 导入/导出测试
5. TTS/音效测试
6. 战斗触发测试
7. 专精切换测试
8. 大量数据性能测试
9. 旧版本数据库兼容测试

============================================================
四、后续重点风险点
============================================================

1. SavedList 全量刷新
   当前仍然是后续最容易产生卡顿的地方。
   如果合集很多、条目很多，展开/收拢、拖拽、导入后刷新都可能卡。

2. AceOptions.lua 剩余职责
   虽然已经瘦身，但仍然承担控制入口和 UI 刷新桥接。
   后续应继续向 OptionsController 迁移。

3. EditorFrame / EditorFields 后续维护
   EditorFrame 已经拆出 Layout / Fields / SoundFields。
   后续如果新增字段，优先放到对应字段模块，不要再把逻辑塞回 EditorFrame.lua。

4. 导入/导出与合集嵌套
   每次修改合集结构后，必须测试：
   - 单合集导出
   - 嵌套合集导出
   - 完整配置导出
   - 重新导入后的顺序和折叠状态

5. 12.x 秘密值限制
   任何新功能如果涉及：
   - 冷却
   - 急速
   - 技能是否可用
   - 单位状态比较
   都必须先评估是否会触发 secret value / taint。

============================================================
五、推荐的版本推进方式
============================================================

1.0.95：加入本路线图文件，不改功能。
1.0.96：SavedListBuilder / SavedListRows 第一轮拆分（已完成）。
1.0.97：SavedListContextMenu / SavedListDragDrop 拆分。
1.0.98：EditorLayout / EditorFields 第一轮拆分。
1.0.99：EditorSoundFields / TestController 拆分。
1.1.0：OptionsController 拆分。
1.1.0：EventHub / CastSuccess / Bloodlust / PublicAPI 拆分，并做完整集中测试。

============================================================
六、每次继续优化前要先检查
============================================================

1. 当前用户反馈的错误是否已修复。
2. 当前 zip 是否能打开插件主界面。
3. 新增语音、保存、删除是否正常。
4. 新增合集、展开/收拢、右键导出是否正常。
5. 导入/导出是否正常。
6. 本次优化是否只改一个职责。
7. 旧入口是否保留。
8. TOC 加载顺序是否正确。
9. 压缩包命名是否为：MCDVoiceCooldown_版本号.zip。



============================================================
七、最新进度记录
============================================================

1.0.99 已完成：EditorSoundFields / TestController 拆分
- 新增 UI/EditorSoundFields.lua。
- 音效来源、内置音效、SharedMedia、TTS 语速滑条逻辑从 EditorFrame / EditorFields 拆出。
- 新增 Core/TestController.lua。
- 普通测试按钮与嗜血测试按钮逻辑从 AceOptions.lua 拆出。
- 下一步建议：拆 Core/OptionsController.lua，把 AceOptions.lua 剩余控制入口继续迁移。

1.0.100 已完成：OptionsController 第一轮拆分
- 新增 Core/OptionsController.lua。
- 血嗜/英勇配置的加载、保存、摘要与测试入口桥接迁移到 OptionsController。
- 当前范围摘要、编辑弹窗打开、UI 刷新、已保存列表刷新等控制入口迁移到 OptionsController。
- 保存/删除/测试/字段同步等桥接入口迁移到 OptionsController，AceOptions.lua 继续保留旧入口兼容。
- AceOptions.lua 进一步瘦身。
- 下一步建议：继续拆 AceOptions.lua 中剩余的合集移动/列表排序逻辑，或开始拆 Runtime 的 EventHub / CastSuccess / Bloodlust。


9. OptionsController / AceOptions 继续瘦身
   已新增：
   - Core/SavedListOrder.lua

   已完成内容：
   - savedListOrder 的 loaded/unloaded 显示顺序管理从 AceOptions.lua 拆出
   - 拖拽显示顺序记录、移除顺序缓存、去除拖放后缀统一放到 SavedListOrder
   - AceOptions.lua 保留旧本地函数名作为转发，降低 MoveSavedListItem 的改动风险

10. PublicAPI 第一轮拆分
   已新增：
   - Core/PublicAPI.lua

   已完成内容：
   - NS.API 的批量安装逻辑集中到 PublicAPI
   - MCDVoiceCooldown.lua 不再逐行挂载大量 API 字段
   - 旧的 NS.API 名称全部保留，UI 和其它模块调用方式不变

1.0.103 已完成：Bloodlust / EventHub 两步拆分
- 新增 Core/Bloodlust.lua。
- 嗜血/英勇/时间扭曲的新鲜疲劳检测、最近触发时间记录、运行时自定义音效路径缓存，从 MCDVoiceCooldown.lua 拆出。
- RebuildBloodlustConfig 与 PlayBloodlustNotification 旧 API 保留，内部转发到 Bloodlust 模块。
- 新增 Core/EventHub.lua。
- PLAYER_LOGIN、PLAYER_ENTERING_WORLD、PLAYER_SPECIALIZATION_CHANGED、PLAYER_TALENT_UPDATE、SPELLS_CHANGED、ITEM_DATA_LOAD_RESULT、PLAYER_REGEN_ENABLED、UNIT_SPELLCAST_SUCCEEDED、UNIT_AURA 的注册与分发，从 MCDVoiceCooldown.lua 拆出。
- MCDVoiceCooldown.lua 继续保留事件回调的业务逻辑闭包，降低一次性迁移风险。
- 下一步建议：拆 Core/CastSuccess.lua，把施法成功配置构建与触发匹配从主文件继续迁移。



1.0.104 已完成：Scope / CastSuccess 两步拆分
- 新增 Core/Scope.lua。
- 当前职业/专精获取、作用域是否载入判断、职业名/专精名解析，从 MCDVoiceCooldown.lua 拆出。
- 新增 Core/CastSuccess.lua。
- 施法成功配置构建、旧版 castSuccessConfigs 兼容读取、统一 entryType=cast 读取、触发匹配，从 MCDVoiceCooldown.lua 拆出。
- MCDVoiceCooldown.lua 继续保留旧入口转发，NS.API 名称不变。
- 延迟施法成功提醒仍由 Runtime.lua 负责，没有改变延时播放机制。
- 下一步建议：继续拆 Core/ItemResolveQueue.lua，把物品触发法术 ID 的等待队列、ITEM_DATA_LOAD_RESULT、脱战补全逻辑从主文件迁移。


1.0.105 已完成：ItemResolveQueue / DeletedEntryStore 两步拆分
- 新增 Core/ItemResolveQueue.lua。
- 物品触发法术 ID 的等待队列、ITEM_DATA_LOAD_RESULT 补全、脱战补全逻辑，从 MCDVoiceCooldown.lua 拆出。
- ResolveAllStoredItemTriggers / ResolvePendingItems 旧 API 保留，内部转发到 ItemResolveQueue。
- 新增 Core/DeletedEntryStore.lua。
- 删除标记 exactKey / signature 构建、删除标记记录、清理旧配置逻辑，从 MCDVoiceCooldown.lua 拆出。
- MarkEntryDeleted / ClearEntryDeletedMarker / IsEntryMarkedDeleted / PurgeDeletedEntries 旧 API 保留。
- 下一步建议：继续拆 MCDVoiceCooldown.lua 中的 RuntimeConfigBuilder，把固定 CD 运行时配置构建从主文件迁移。


1.0.106 已完成：RuntimeConfigBuilder / EntryMap 两步拆分
- 新增 Core/RuntimeConfigBuilder.lua。
- 固定 CD 运行时配置构建逻辑从 MCDVoiceCooldown.lua 拆出。
- 全职业、当前职业全专精、当前职业当前专精三层优先级保持不变。
- 物品 triggerSpellID、天赋 CD 覆盖、充能、TTS/音效字段构建逻辑保持不变。
- 新增 Core/EntryMap.lua。
- EnsureSpecTable、GetSpecTable、GetEntry、GetOrderedEntryIndices、FindFirstFreeIndex、GetUsedEntryCount 等通用配置表辅助函数从主文件拆出。
- MCDVoiceCooldown.lua 保留旧本地入口转发，NS.API 名称不变。
- 下一步建议：继续拆 MCDVoiceCooldown.lua 中的 AddonLifecycle / ProfileController，把 PLAYER_LOGIN 初始化、切专精重建、刷新面板等生命周期桥接从主文件迁移。


1.0.107 已完成：ProfileController / Startup 两步拆分
- 新增 Core/ProfileController.lua。
- 当前编辑范围目标职业/专精判断、范围摘要、条目范围、面板刷新、切专精后的配置重建流程，从 MCDVoiceCooldown.lua 迁移。
- 新增 Core/Startup.lua。
- PLAYER_LOGIN 初始化、PLAYER_ENTERING_WORLD/SPELLS_CHANGED/PLAYER_TALENT_UPDATE 配置刷新、PLAYER_SPECIALIZATION_CHANGED 专精切换刷新、ITEM_DATA_LOAD_RESULT 物品补全、PLAYER_REGEN_ENABLED 脱战补全、UNIT_SPELLCAST_SUCCEEDED 施法事件分发、UNIT_AURA 嗜血检测业务流程，从主文件迁移。
- EventHub 继续只负责事件注册和低层分发；Startup 负责事件业务流程。
- NS.API 旧入口保留，保存数据结构、导入导出格式、运行时提醒模型不变。
- 下一步建议：继续拆 MCDVoiceCooldown.lua 中剩余的 ServiceBridge / ModuleBridge，把大量本地转发函数按 ResolverBridge、RuntimeBridge、NotifierBridge 等继续收口，或者暂停结构拆分先做完整集中测试。

[1.0.108 已完成]
- 新增 Core/ImportCodec.lua：导入/导出字符串编码、解码独立。
- 新增 Core/ImportRefreshScheduler.lua：导入后的物品补全、运行时重建、施法成功重建、嗜血重建、列表刷新改为分帧执行。
- Core/ItemResolveQueue.lua 增加 ResolveAllStoredBatched：完整配置导入后，物品触发法术 ID 补全按小批量分帧处理。
- UI/MainFrame.lua 导入成功后不再立刻同步 self:Refresh()，避免和导入模块重复刷新。
- Core/ImportExport.lua 导入物品时不再在清洗阶段立即解析触发法术 ID，统一交给导入后队列补全。

[下一步建议]
- 如果 1.0.108 导入测试稳定，下一步可以继续拆 Core/ImportEntryProcessor.lua / Core/ImportCollectionProcessor.lua，把 ImportExport.lua 内的单条、合集、完整配置导入处理继续拆开。
- 若导入仍有卡顿，优先检查 SavedListBuilder 的列表构建成本，考虑加入更细粒度的列表分页/批量渲染。



[1.0.110 已完成]
- 新增 UI/MainFrameDialogs.lua。
- 主窗口中的新增合集、重命名合集、导入、导出弹窗创建逻辑从 UI/MainFrame.lua 拆出。
- 新增 UI/MainFrameLocale.lua。
- 语言下拉菜单条目生成、主窗口文案刷新、按钮文案刷新、编辑弹窗语言刷新转发，从 UI/MainFrame.lua 拆出。
- UI/MainFrame.lua 继续保留 EnsureCollectionDialog、OpenImportDialog、OpenExportDialog、RefreshLocale 等旧入口，内部由新模块提供实现。
- 新增 Docs/TestChecklist_CN.txt，作为后续集中测试清单。

[下一步建议]
- 若 1.0.110 测试稳定，可以继续拆 UI/Widgets.lua：优先将下拉菜单逻辑拆到 UI/Dropdown.lua。
- 如果下拉菜单拆分风险过高，则继续拆 UI/MainFrameActions.lua，把新增、编辑、删除、导入、导出按钮回调从 MainFrame.lua 迁移出去。

[1.0.112 UI结构优化记录]
- 新增 UI/WidgetUtils.lua：统一 MakeSingleLine / SafeCreateFrame / Clamp 等 UI 工具函数。
- 新增 UI/WidgetScrollable.lua：拆出 Widgets:CreateScrollableContent，后续滚动容器问题优先检查这里。
- 新增 UI/WidgetDropdownPopup.lua：拆出统一下拉菜单弹出层、滚动、行渲染、悬浮、选择逻辑。
- UI/WidgetDropdown.lua 现在只保留下拉关闭状态控件、公共 Set/Get/Enable 接口。
- UI/Widgets.lua 继续瘦身，只保留基础控件：标签、按钮、输入框、勾选框、模块卡片。

[1.0.112 后续测试重点]
1. 主窗口能否打开。
2. 编辑弹窗能否打开。
3. 所有下拉菜单能否打开、滚动、选择、关闭。
4. 下拉菜单层级是否仍然高于编辑弹窗。
5. 编辑弹窗滚动区域是否正常。
6. 新增/编辑/保存普通CD、施法成功配置是否正常。
- 新增 UI/EditorDrafts.lua：拆出新增 CD提示/施法成功 时的临时草稿保存、恢复、空白字段初始化。


[1.0.113 UI结构优化记录]
- 新增 UI/SavedListRenderer.lua：从 UI/SavedList.lua 拆出列表行创建、分组标题创建、普通/合集/空行渲染、滚动范围更新、行点击/拖拽脚本绑定。
- UI/SavedList.lua 现在主要保留列表容器创建、刷新调度、缓存渲染入口、展开/收拢入口、选择入口。
- 新增 UI/EditorFrameBuilder.lua：从 UI/EditorFrame.lua 拆出编辑弹窗主体创建、模块布局、底部按钮、标签页按钮、字段控件创建、自动填充和点击事件绑定。
- UI/EditorFrame.lua 现在主要保留打开/关闭、切换 CD提示/施法成功、字段 Pull/Push/Refresh 兼容入口。
- 本次不改变保存数据结构、导入导出格式、运行时提醒模型。

[1.0.113 后续测试重点]
1. 已保存列表能否正常显示已载入/未载入分组。
2. 普通行、合集行、空合集提示行显示是否正常。
3. 列表滚动、选中、右键、拖拽是否正常。
4. 编辑弹窗能否正常打开，新建/编辑/关闭是否正常。
5. CD提示、施法成功、嗜血三个标签页切换是否正常。
6. 编辑弹窗内所有字段、按钮、自动填充、测试按钮是否正常。

[下一步建议]
- 若 1.0.113 测试稳定，可以继续拆 UI/EditorActions.lua，把保存、测试、切换标签、职业/专精下拉回调从 EditorFrameBuilder.lua 进一步拆出。
- 或继续拆 UI/SavedListScrollbar.lua / UI/SavedListHeaders.lua，把列表滚动和分组标题处理进一步独立。


[1.0.114 UI结构优化记录]
- 新增 UI/EditorActions.lua：从 UI/EditorFrameBuilder.lua 拆出编辑弹窗交互回调，包括标签切换、职业/专精下拉、保存、测试、检查天赋、延时播放。
- UI/EditorFrameBuilder.lua 现在更接近“只负责创建控件和布局”，后续按钮逻辑问题优先检查 UI/EditorActions.lua。
- 新增 UI/SavedListHeaders.lua：从 UI/SavedListRenderer.lua 拆出已载入/未载入分组标题创建与折叠逻辑。
- UI/SavedListRenderer.lua 继续负责行渲染，但标题折叠逻辑已独立，后续列表标题问题优先检查 UI/SavedListHeaders.lua。

[1.0.114 后续测试重点]
1. 编辑弹窗 CD提示/施法成功/嗜血标签切换。
2. 职业/专精下拉切换后字段是否保留。
3. 保存/测试按钮是否正常。
4. 检查天赋、延时播放勾选是否正常。
5. 已载入/未载入标题展开收拢是否正常。

[下一步建议]
- 若 1.0.114 稳定，可以继续拆 UI/SavedListRowFactory.lua，把普通行/合集行/空合集提示行的创建逻辑从 UI/SavedListRenderer.lua 拆出。
- 或继续拆 UI/DialogTextArea.lua，把 MainFrameDialogs.lua 中的多行导入/导出文本框和弹窗按钮布局继续独立。

[1.0.115 UI结构优化记录]
- 当前优化方向：插件 UI 结构维护性，不新增功能，不改变保存数据结构。
- 新增 UI/SavedListRowFactory.lua：从 UI/SavedListRenderer.lua 拆出已保存列表普通行/合集行的创建、点击、右键、拖拽脚本绑定。
- UI/SavedListRenderer.lua 现在更集中负责行数据渲染、位置摆放、滚动范围更新。
- 新增 UI/DialogTextArea.lua：从 UI/MainFrameDialogs.lua 拆出导入/导出多行文本框创建、滚动区域、文本高度自适应、滚动条皮肤。
- UI/MainFrameDialogs.lua 继续负责弹窗主体和按钮行为，导入/导出文本区域由 DialogTextArea 提供。

[1.0.115 后续测试重点]
1. 已保存列表普通行/合集行点击、右键、拖拽是否正常。
2. 合集展开/收拢后行显示是否正常。
3. 导入/导出弹窗能否打开。
4. 导入/导出多行文本框能否输入、滚动、全选。
5. 导入成功后刷新是否正常。

[下一步建议]
- 继续拆 UI/MainFrameActions.lua，把主窗口按钮回调从 UI/MainFrame.lua / UI/MainFrameRefresh.lua 进一步独立。
- 或继续拆 UI/DialogChrome.lua，把通用原生弹窗外框 PrepareNativeDialog 从 MainFrameDialogs.lua 迁移出去。



12. UI 列表行渲染与合集弹窗拆分（1.0.116）
   已新增：
   - UI/SavedListRowRenderer.lua
   - UI/MainFrameCollectionDialog.lua

   已完成内容：
   - 已保存列表单行的视觉渲染、状态点、图标、文字、选中颜色拆到 SavedListRowRenderer。
   - SavedListRenderer 更专注于列表流程、标题、行摆放、滚动范围。
   - 新增/重命名合集弹窗从 MainFrameDialogs 拆到 MainFrameCollectionDialog。
   - MainFrameDialogs 继续保留导入/导出弹窗与通用弹窗工具。


13. MainFrame 按钮回调拆分（1.0.116）
   已新增：
   - UI/MainFrameActions.lua

   已完成内容：
   - 主窗口新增、编辑、删除、刷新、导入、导出按钮回调拆出。
   - SavedList 选中变更回调拆出。
   - MainFrame.lua 更专注于窗口主体创建、控件声明、打开关闭和刷新转发。


[1.0.117] 已完成：
- 第 4 步 MainFrame 剩余逻辑拆分：新增 UI/MainFrameBuilder.lua。
- 第 5 步 AceOptions 兼容层清理：新增 Core/AceOptionsCompat.lua 与 Core/SavedListMoveController.lua。
- 下一步建议：进入集中测试与修复阶段，优先测试主界面打开、合集操作、列表拖拽、导入导出和所有提醒触发。

[1.0.118 修复与结构优化记录]
- 修复合集内普通语音无法拖拽到合集外的问题。
- 拖到合集标题中间 = 放入合集；拖到合集标题上/下边缘 = 移到合集外层并排在该合集前/后。
- 新增 UI/SavedListDropTarget.lua：从 UI/SavedListDragDrop.lua 拆出拖拽目标判断、放入/排序模式判断、dropKey 构建。
- Core/SavedListMoveController.lua 增加“普通语音相对合集标题前/后插入”的处理逻辑。
- 下一步建议：若 1.0.118 测试稳定，可以继续拆 UI/DialogChrome.lua 或 UI/MainFrameImportExportDialog.lua，把通用弹窗外框和导入/导出弹窗行为从 MainFrameDialogs 继续拆出。

[1.0.119 拖拽系统收尾记录]
- 新增 Core/SavedListDropKey.lua：统一处理拖拽 dropKey 的后缀解析与构建。
- 统一 :inside / :after / :before / :empty 后缀处理，避免 SavedListDropTarget 与 SavedListMoveController 各自手写解析导致行为不一致。
- UI/SavedListDropTarget.lua 改为通过 SavedListDropKey:Build() 构建目标 key。
- Core/SavedListMoveController.lua 改为通过 SavedListDropKey:Split() 解析目标 key，减少 targetKey 字符串清理重复逻辑。
- 本次不改变保存数据结构、不改变运行时提醒模型、不改变导入导出格式。
- 下一步建议：进入 1.0.120 SavedList 刷新性能优化，重点处理展开/收拢、导入后刷新、大列表行渲染缓存。

## 1.0.120 记录
- 完成 SavedList 刷新性能优化。
- 新增 UI/SavedListRefresh.lua，统一刷新、缓存渲染、展开/收拢逻辑。
- SavedListBuilder 增加单次刷新内的 scope layout 缓存。
- SavedListLayout 增加配置表、职业/专精名、载入状态、折叠计数缓存。
- SavedListRows 增加行文本缓存，减少缓存重绘时重复拼接文本。
- 后续测试重点：大合集展开/收拢、导入完整配置后列表刷新、拖拽后刷新、删除/新增后刷新。



[1.0.121]
- 修复合集内语音拖出到根列表。
- 修复空合集选中后按钮仍为灰色。
- 优化合集展开：局部展开当前合集所在 scope，并加入条目行缓存。

[1.0.122 修复与结构优化记录]
- 修复合集内普通语音拖出到外层仍不稳定的问题。
- 新增 UI/SavedListDragGeometry.lua：统一封装拖拽时的鼠标坐标、区域命中检测、行内上下边缘判断。
- UI/SavedListDropTarget.lua 不再只依赖 MouseIsOver/行命中；当合集内语音拖到列表空白区域时，会明确生成 root:<classID>:<specID>:after 目标，移动到当前 scope 根列表末尾。
- 普通语音拖到自己所在的父合集标题时，不再判定为“放回同一合集”，而是根据鼠标在标题上/下半区移动到父合集外层前/后。
- 合集内语音拖到其它合集标题时，边缘区域加宽，降低“拖出/排序”的操作难度。
- 本次继续保持保存数据结构、导入导出格式、运行时提醒模型不变。

[1.0.122 后续测试重点]
1. 合集内语音拖到列表空白处，是否能移出到外层根列表。
2. 合集内语音拖到父合集标题上半区/下半区，是否能移出并排在父合集前/后。
3. 合集内语音拖到其它合集标题中间，是否仍能放入目标合集。
4. 合集内语音拖到其它合集标题上/下边缘，是否能移到该合集同级前/后。
5. 普通根列表语音拖入合集是否仍正常。
6. 合集拖拽排序和合集嵌套是否仍正常。

[1.0.123 拆分收尾记录]
- 新增 Core/CollectionController.lua：从 Core/AceOptions.lua 拆出合集创建、重命名、删除、查询与合集图标解析。
- Core/AceOptions.lua 现在只保留声音状态小桥接与旧 NS.AceOptions:* 兼容入口。
- 新增 Core/ImportExportUtil.lua：拆出导入/导出共用的深拷贝与条目清洗。
- 新增 Core/ImportEntryProcessor.lua：拆出单条记录导入与导出记录构建。
- 新增 Core/ImportFullProcessor.lua：拆出完整配置导入。
- 新增 Core/ImportCollectionProcessor.lua：拆出合集导入。
- 新增 Core/ExportBuilder.lua：拆出单条、合集、完整配置导出构建。
- Core/ImportExport.lua 现在主要保留导入/导出字符串入口、payload 分发与 NS.AceOptions 旧入口转发。
- 新增 Core/ResolverBridge.lua：从 MCDVoiceCooldown.lua 拆出技能、物品、天赋、对象解析桥接。
- 本次继续保持保存数据结构、导入导出格式、运行时提醒模型不变。

[1.0.123 后续测试重点]
1. 新增、重命名、删除合集是否正常。
2. 单条语音导入/导出是否正常。
3. 合集导入/导出，特别是嵌套合集，是否保持顺序和折叠状态。
4. 完整配置导入/导出是否正常，导入后列表是否分帧刷新。
5. 物品触发法术 ID 补全是否仍在导入后执行。
6. 普通 CD、施法成功、嗜血提醒是否仍正常触发。

[1.0.124 拆分收尾记录]
- 新增 Core/SavedListMoveContext.lua：从 Core/SavedListMoveController.lua 抽出拖拽移动使用的集合 key、scope、顺序记录、UI 刷新等上下文辅助方法。
- Core/SavedListMoveController.lua 继续保留 MoveSavedListItem 作为唯一移动入口，复杂执行分支暂不硬拆，避免改变拖拽行为。
- 新增 Core/NotifierBridge.lua：从 MCDVoiceCooldown.lua 拆出声音路径规范化、条目声音路径解析、普通提醒与施法成功提醒播放桥接。
- 新增 Core/RuntimeBridge.lua：从 MCDVoiceCooldown.lua 拆出运行时冷却清理、延迟施法成功队列、运行时刷新与启动冷却桥接。
- MCDVoiceCooldown.lua 进一步向初始化/配置/旧 API 安装入口收敛。
- 本次继续保持保存数据结构、导入导出格式、运行时提醒模型不变。

[1.0.124 后续测试重点]
1. 已保存列表拖拽排序、拖入合集、拖出合集、跨载入/未载入分组拖动是否正常。
2. 删除合集后普通 CD 与施法成功旧兼容数据是否同步清理。
3. TTS、内置音效、自定义路径、SharedMedia 播放是否正常。
4. 固定 CD 到期提醒、施法成功提醒、延迟施法成功提醒是否正常。
5. 专精切换后运行时配置是否重建，OnUpdate 是否仍自动启停。
