Skip to content

Add common module management interface for labelers (add/rename/remove/duplicate subprojects) - #114

Merged
sdercolin merged 2 commits into
devfrom
feature/module-management
Jul 14, 2026
Merged

sdercolin merged 2 commits into
devfrom
feature/module-management

Conversation

@sdercolin

Copy link
Copy Markdown
Owner

Closes the Trello ticket Commonized module management (design discussion recorded as a comment on the card).

What

Adds a common, labeler-driven interface for managing modules (subprojects) from the main menu, replacing the need for labeler-specific macro plugins (e.g. utau-add-folder, textgrid-manage-module).

Interface (LabelerConf.moduleManagement, serialVersion 2 → 3)

"moduleManagement": {
  "add":       { "parameters": [...], "scripts": "addModule.js" },
  "rename":    { "parameters": [...], "scripts": "renameModule.js" },
  "remove":    { "scripts": "removeModule.js" },
  "duplicate": null
}
  • No default implementation: module semantics differ per labeler (e.g. vlabeler-textgrid encodes the tier index/name inside the module name and its writer parses it), so an operation that is not declared is not available, and the whole File > Manage Subprojects submenu is hidden when nothing is declared. Existing labelers are unaffected.
  • Each operation optionally declares displayedName, description, and plugin-style parameters, rendered by the existing plugin dialog machinery (BasePlugin / BasePluginDialogState).
  • Scripts run in the Project-scope macro environment (modules, currentModuleIndex, params, labelerParams, resources, projectRootDirectory, error(), report()), targeting the current module. Results go through validate() and the undo/redo history like macro plugins.

Core fixes required by variable module counts

  • ProjectHistory.contentEquals zipped module lists under the assumption // modules size will not change — removing trailing modules was treated as an index-only change and never recorded. Now a size change is never squashed.
  • ProjectStoreImpl.withCurrentIndexesKept matched modules by position across history snapshots; when the module count differs it now matches by name (behavior unchanged for the normal fixed-count path).

Bundled labeler adoption

nnsvs-singer-labeler, diffsinger-labeler, and utau-singer-labeler now declare add/rename/remove (duplicate intentionally omitted — duplicating a module sharing the same raw file is unsafe with Entry-scope writers). The UTAU add script is a port of the utau-add-folder plugin, which is kept for users on older app versions. vlabeler-textgrid will adopt the interface (including scripted rename validation and duplicate) in a separate PR in its own repo.

UI

  • File > Manage Subprojects submenu with one item per declared operation (hidden otherwise), with key actions available for keymap customization.
  • New strings in en/zh-CN/ja/ko; READMEs (×4) updated.

Docs

  • docs/labeler-development.md: new "Module Management" definition section and "Managing Modules" scripting section.
  • docs/labeler-structure-updates.md: serialVersion 3.

Tests

  • ModuleOperationTest: runs the real nnsvs scripts — rename (incl. collision/empty errors), remove (incl. last-module guard, index adjustment), add (lab parsed / default entry / outside-folder and duplicate errors), plus serialization round trip.
  • UtauSingerProjectFixtureTest: add-folder operation end to end.
  • ProjectHistoryTest / ProjectStoreTest: history recording and undo/redo across module-count changes.

Compatibility notes

  • Older release builds ignore the unknown moduleManagement key (ignoreUnknownKeys), so serialVersion-3 labelers and projects saved by this version load fine there (without the feature). Nothing rejects a higher serialVersion on load.
  • Project file version is unchanged (still 4).

🤖 Generated with Claude Code

Add a `moduleManagement` field to LabelerConf (serialVersion 3) that lets
labelers define add/rename/remove/duplicate operations for modules
(subprojects), surfaced as menu items under File > Manage Subprojects.
Operations run in the Project-scope macro environment with labeler-declared
parameters; there is no default implementation, so labelers must opt in
explicitly.

- Fix ProjectHistory.contentEquals and withCurrentIndexesKept, which
  assumed the module count never changes
- Adopt the interface in the bundled nnsvs/diffsinger/utau labelers
  (add/rename/remove), replacing the need for labeler-specific plugins
  such as utau-add-folder
- Docs, strings (en/zh/ja/ko), READMEs, and integration tests running
  the real labeler scripts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread src/jvmMain/kotlin/com/sdercolin/vlabeler/ui/ProjectStore.kt Outdated
… matching

- Clear moduleOperationShownInDialog in closeAllDialogs, and add a
  synchronous openModuleOperationDialog overload for testability
- Rework withCurrentIndexesKept to match modules by name, pairing the
  names that exist on only one side by order, so undo/redo tracks the
  current module across renames that reorder the list (e.g. the TextGrid
  labeler sorts modules by name after renaming)
- Tests for the new dialog state in AppDialogStateTest and the
  rename-with-reordering undo/redo path in ProjectStoreTest

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sdercolin
sdercolin merged commit efa32fe into dev Jul 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants