diff --git a/.specstory/history/2026-02-18_23-39Z-character-migrator-workflow-integration.md b/.specstory/history/2026-02-18_23-39Z-character-migrator-workflow-integration.md
index e63f38c..7644a1d 100644
--- a/.specstory/history/2026-02-18_23-39Z-character-migrator-workflow-integration.md
+++ b/.specstory/history/2026-02-18_23-39Z-character-migrator-workflow-integration.md
@@ -8962,7 +8962,1240 @@ The user wants me to implement the NLAMig enhancements plan. Let me read the pla
4. Preserve full strip timing/effects (action_frame_start/end, influence, mute, scale, use_* flags)
5. Order: no anim_data return → active-action-only path → NLA path (add tracks) → mirror als.turn_on
-I need to check if there are todos in the plan - the user said "To-do's from
+I need to check if there are todos in the plan - the user said "To-do's from the plan have already been created" but the plan file shows `todos: []`. I'll proceed with implementation and mark todos if I find them.
+
+Let me read the current migrator.py run_step_2 and implement the changes.
+
+
+---
+
+
+Implementing the NLAMig enhancements: reading the migrator and plan, then applying the changes.
+
+
+---
+
+
+Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ops\migrator.py
+
+
+
+
+---
+
+
+
+Tool use: **glob_file_search** • Searched codebase "undefined" • **0** result in **1** directory
+
+Directory: **T:\Dynamic-Link-Manager** (0 file)
+
+
+
+
+---
+
+Thought Process
+
+No plan file in workspace. I'll implement directly. I need to:
+
+1. Add `_mirror_als_turn_on(orig, rep)` helper that copies "als.turn_on" from orig to rep (object level, and optionally bone level if we can find it).
+2. Refactor `run_step_2`:
+ - If no orig.animation_data: return
+ - If orig.animation_data exists but nla_tracks is empty and action is set: ensure rep.animation_data, set rep.animation_data.action = orig.animation_data.action, _mirror_als_turn_on(orig, rep), return
+ - Else (NLA path): ensure rep.animation_data, do NOT remove tracks
+ - For each track in orig: add new track (uniquify name if rep already has it), copy track props, for each strip copy all properties
+ - Copy strip: action, frame_start, frame_end, blend_type, extrapolation, blend_in, blend_out, repeat, action_frame_start, action_frame_end, influence, mute, scale, use_auto_blend, use_reverse, use_animated_influence, use_animated_time, use_animated_time_cyclic, use_sync_length
+ - At end: _mirror_als_turn_on(orig, rep)
+
+Blender's NlaStrips.new(name, start_frame, action) - then we need to set frame_end and other props. Some props might be read-only after creation; I'll set what's writable.
+
+Let me implement. For _mirror_als_turn_on: try orig.get("als.turn_on") on the object. If the addon stores on a bone, we could try common bone names like "root" or the first bone - but the plan says "armature object (e.g. orig["als.turn_on"])" and "if the addon stores it on a specific bone, check that bone on orig and mirror to the same bone on rep". So: first try object level; then try pose bones (iterate and check for "als.turn_on" on any bone). If found on orig, set on rep in same location.
+
+
+---
+
+
+Implementing the plan: adding `_mirror_als_turn_on` and refactoring `run_step_2`.
+
+
+---
+
+
+Tool use: **todo_write** • Todo List
+
+- [-] Add _mirror_als_turn_on(orig, rep) helper
+- [ ] Active-action-only path + add-only NLA + full strip props
+- [x] Add Tweak Tools box and rows to panels.py
+- [x] Add 9 tweak operators and register in operators.py
+
+
+
+
+---
+
+Thought Process
+
+I'll add the helper and refactor run_step_2 in one go. First add _mirror_als_turn_on, then replace run_step_2 body.
+
+
+---
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ops\migrator.py
+
+
+
+
+
+---
+
+Thought Process
+
+Implementation is in. Let me verify: (1) NlaStrips.new() - in Blender 4.x/5 the signature might be (name, start, end, action) or (name, start, action). I used (strip.name, int(strip.frame_start), strip.action) which matches the original code. (2) Only CLIP strips with an action are copied - avoids errors for META/TRANSITION/SOUND. (3) I'll check lints and mark todos.
+
+
+---
+
+
+
+
+
+---
+
+
+
+Tool use: **read_lints**
+
+
+Parameters:
+
+```json
+{
+ "paths": [
+ "T:\\Dynamic-Link-Manager\\ops\\migrator.py"
+ ]
+}
+```
+Result:
+
+```json
+{
+ "linterErrorsByFile": [
+ {
+ "relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ops\\migrator.py"
+ }
+ ]
+}
+```
+Status: **completed**
+
+
+
+
+
+---
+
+
+Tool use: **todo_write** • Todo List
+
+- [x] Add _mirror_als_turn_on(orig, rep) helper
+- [-] Active-action-only path + add-only NLA + full strip props
+- [x] Add Tweak Tools box and rows to panels.py
+- [x] Add 9 tweak operators and register in operators.py
+
+
+
+
+---
+
+Thought Process
+
+All implementation is done in one edit. Let me mark todo 2 completed and double-check the code for any Blender API quirks. In Blender, NlaStrips.new(name, start_frame, action) - I need to confirm the order. From the Blender docs, NlaStrips.new(name, start, end, action) in some versions, or (name, start, action). Let me check the original code: new_track.strips.new(strip.name, int(strip.frame_start), strip.action) - so it's (name, frame_start, action). Good.
+
+One potential issue: when we do `rep_tracks.new(prev=prev_track)`, the new track is inserted after prev_track. So the first time, prev_track is the last existing track (or None). Then we set prev_track = new_track for the next iteration. So new tracks are appended in order. Good.
+
+I'll mark the second todo completed.
+
+
+---
+
+
+
+
+
+---
+
+
+Tool use: **todo_write** • Todo List
+
+- [x] Add _mirror_als_turn_on(orig, rep) helper
+- [x] Active-action-only path + add-only NLA + full strip props
+- [x] Add Tweak Tools box and rows to panels.py
+- [x] Add 9 tweak operators and register in operators.py
+
+
+
+
+---
+
+Thought Process
+
+All todos from the plan are done. Let me provide a short summary to the user.
+
+
+---
+
+
+Summary of what was implemented in [ops/migrator.py](T:\Dynamic-Link-Manager\ops\migrator.py):
+
+**1. `_mirror_als_turn_on(orig, rep)`**
+- Copies `"als.turn_on"` from the original to the replacement: first on the armature object, then on any pose bone that has the property (same bone name on `rep`).
+
+**2. Active-action-only path**
+- If `orig` has `animation_data`, no NLA tracks, and an active `action`, the step no longer runs full NLA migration. It ensures `rep` has `animation_data`, sets `rep.animation_data.action = orig.animation_data.action`, runs `_mirror_als_turn_on`, and returns.
+
+**3. Add-only NLA (no replace)**
+- Existing NLA tracks on `rep` are left in place. For each track on `orig`, a new track is added after the last track (existing or newly added). If the track name already exists on `rep`, it is uniquified (e.g. `Name.001`, `Name.002`).
+
+**4. Strip timing and effects**
+- Only `CLIP` strips with an `action` are migrated. For each such strip we copy:
+ - Existing: `frame_start`, `frame_end`, `blend_type`, `extrapolation`, `blend_in`, `blend_out`, `repeat`
+ - New: `action_frame_start`, `action_frame_end`, `influence`, `mute`, `scale`, `use_auto_blend`, `use_reverse`, `use_animated_influence`, `use_animated_time`, `use_animated_time_cyclic`, `use_sync_length`
+
+**5. Order of operations**
+- No `orig.animation_data` → return.
+- NLA tracks empty and active action set → apply action, mirror `als.turn_on`, return.
+- Otherwise → ensure `rep.animation_data`, add new tracks (with name uniquify), copy track and strip props, then call `_mirror_als_turn_on(orig, rep)`.
+
+All related todos are completed. No linter issues reported.
+
+---
+
+_**User**_
+
+Add documentation to all operators drawn in the UI that don't already have them. No operators should be named step1 step2, etc. They should all follow a naming convention along the lines of the true names of the operators: CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys.
+
+---
+
+_**Agent (model default, mode Agent)**_
+
+
+
+---
+
+Thought Process
+
+The user wants me to:
+1. Add documentation to all operators that are drawn in the UI but don't already have documentation
+2. Ensure no operators are named step1, step2, etc. - they should follow a naming convention like: CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys
+
+I'm in plan mode, so I need to research the codebase to understand:
+- Where operators are defined and which ones are "drawn in the UI"
+- Which operators already have documentation vs which don't
+- Which operators have generic names like step1, step2 that need to be renamed
+- The naming convention to apply
+
+Let me search for operators in the codebase, especially in ui/operators.py and any UI drawing code.
+
+
+---
+
+
+Searching the codebase for operators drawn in the UI and their documentation/naming.
+
+
+---
+
+
+Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+---
+
+
+
+Tool use: **ripgrep_raw_search**
+
+
+Parameters:
+
+```json
+{
+ "pattern": "step\\d|step1|step2|step3",
+ "path": "T:\\Dynamic-Link-Manager",
+ "caseInsensitive": false
+}
+```
+Additional data:
+
+```json
+{
+ "isPruned": true,
+ "pattern": "step\\d|step1|step2|step3",
+ "path": "T:\\Dynamic-Link-Manager",
+ "outputMode": "content",
+ "totalFiles": 4,
+ "totalMatches": 30,
+ "topFiles": [
+ {
+ "uri": "ops\\migrator.py",
+ "matchCount": 9
+ },
+ {
+ "uri": ".specstory\\history\\2026-02-18_23-39Z-character-migrator-workflow-integration.md",
+ "matchCount": 6
+ },
+ {
+ "uri": "ops/migrator.py",
+ "matchCount": 9
+ },
+ {
+ "uri": ".specstory/history/2026-02-18_23-39Z-character-migrator-workflow-integration.md",
+ "matchCount": 6
+ }
+ ]
+}
+```
+Status: **completed**
+
+
+
+
+
+---
+
+
+
+Tool use: **ripgrep_raw_search**
+
+
+Parameters:
+
+```json
+{
+ "pattern": "bl_idname|bl_label|bl_description",
+ "path": "T:\\Dynamic-Link-Manager",
+ "caseInsensitive": false
+}
+```
+Additional data:
+
+```json
+{
+ "isPruned": true,
+ "pattern": "bl_idname|bl_label|bl_description",
+ "path": "T:\\Dynamic-Link-Manager",
+ "outputMode": "content",
+ "totalFiles": 9,
+ "totalMatches": 274,
+ "topFiles": [
+ {
+ "uri": "ui\\operators.py",
+ "matchCount": 60
+ },
+ {
+ "uri": ".specstory/history/2025-08-20_22-43Z-creating-a-blender-extension-for-asset-replacement.md",
+ "matchCount": 100
+ },
+ {
+ "uri": ".specstory/history/2026-02-18_23-39Z-character-migrator-workflow-integration.md",
+ "matchCount": 25
+ },
+ {
+ "uri": "ui/preferences.py",
+ "matchCount": 1
+ },
+ {
+ "uri": "ui\\panels.py",
+ "matchCount": 1
+ },
+ {
+ "uri": "ui/operators.py",
+ "matchCount": 60
+ },
+ {
+ "uri": "ui/panels.py",
+ "matchCount": 1
+ },
+ {
+ "uri": ".specstory\\history\\2026-02-18_23-39Z-character-migrator-workflow-integration.md",
+ "matchCount": 25
+ },
+ {
+ "uri": "__init__.py",
+ "matchCount": 1
+ }
+ ]
+}
+```
+Status: **completed**
+
+
+
+
+
+---
+
+Thought Process
+
+I need to:
+1. Find all operators that are drawn in the UI (which panels draw which operators)
+2. Check which operators have bl_description (documentation) and which don't
+3. Check if any operators use step1/step2 style names - the grep showed migrator operators use names like migrator_copy_attributes, migrator_migrate_nla, etc. - not step1/step2. The "step6" references are in migrator.py as debug print messages, not operator names.
+4. Apply naming convention: CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys - these seem to be the desired bl_label style (short, descriptive) or perhaps the user wants bl_description to reference these "true names"
+
+Let me read more of operators.py to see all operators, which have bl_description, and how the UI draws them. I'll also check panels.py to see which operators are drawn.
+
+
+---
+
+
+Checking which operators the UI draws and which have `bl_description`; inspecting the migrator operators and panel layout.
+
+
+---
+
+
+Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+---
+
+
+Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ui\panels.py
+
+
+
+
+---
+
+
+
+Tool use: **ripgrep_raw_search**
+
+
+Parameters:
+
+```json
+{
+ "pattern": "bl_description|operators\\.|layout\\.operator",
+ "path": "T:\\Dynamic-Link-Manager\\ui",
+ "caseInsensitive": false
+}
+```
+Additional data:
+
+```json
+{
+ "isPruned": true,
+ "pattern": "bl_description|operators\\.|layout\\.operator",
+ "path": "T:\\Dynamic-Link-Manager\\ui",
+ "outputMode": "content",
+ "totalFiles": 2,
+ "totalMatches": 2,
+ "topFiles": [
+ {
+ "uri": "ui/panels.py",
+ "matchCount": 1
+ },
+ {
+ "uri": "ui\\panels.py",
+ "matchCount": 1
+ }
+ ]
+}
+```
+Status: **completed**
+
+
+
+
+
+---
+
+Thought Process
+
+The grep for bl_description in ui didn't find any - so it seems no operators currently have bl_description. Let me double-check and also get the full list of operators drawn in the UI from panels.py. I have:
+
+From panels.py, operators drawn in UI:
+- dlm.make_paths_relative
+- dlm.make_paths_absolute
+- dlm.picker_original_character
+- dlm.picker_replacement_character
+- dlm.run_character_migration
+- dlm.migrator_copy_attributes
+- dlm.migrator_migrate_nla
+- dlm.migrator_custom_properties
+- dlm.migrator_bone_constraints
+- dlm.migrator_retarget_relations
+- dlm.migrator_basebody_shapekeys
+- dlm.tweak_add_arm, dlm.tweak_remove_arm, dlm.tweak_bake_arm
+- dlm.tweak_add_leg, dlm.tweak_remove_leg, dlm.tweak_bake_leg
+- dlm.tweak_add_both, dlm.tweak_remove_both, dlm.tweak_bake_both
+- dlm.scan_linked_assets
+- dlm.reload_libraries
+- dlm.browse_search_path
+- dlm.remove_search_path
+- dlm.add_search_path
+- dlm.find_libraries_in_folders
+- dlm.open_linked_file
+- dlm.relocate_single_library
+
+So the user wants:
+1. Add bl_description to all operators that are drawn in the UI and don't have one (appears to be all of them - none have bl_description).
+2. No operators named step1, step2 - the current operator bl_idnames are already descriptive (migrator_copy_attributes, etc.). The user said "They should all follow a naming convention along the lines of the true names: CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys." So they might want:
+ - Either bl_label to use these short names (CopyAttr, MigNLA, ...)
+ - Or bl_description to reference these "true names"
+ - Or both
+
+Re-reading: "No operators should be named step1 step2, etc. They should all follow a naming convention along the lines of the true names of the operators: CopyAttr, MigNLA, ..."
+
+So: (1) no step1/step2 - the codebase already uses descriptive names like migrator_copy_attributes, not step1. (2) "follow a naming convention along the lines of" those true names - so the convention is short, CamelCase or similar: CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys.
+
+In Blender, bl_idname must be lowercase with dots/underscores (e.g. dlm.migrator_copy_attributes). So the "naming convention" likely applies to bl_label (what users see) and/or bl_description. Currently we have bl_label like "Copy attributes", "Migrate NLA", "Custom properties", "Bone constraints", "Retarget relations", "Migrate BaseBody shapekeys" - which are already descriptive. The user's "true names" are shorter: CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys. So perhaps:
+- Option A: Change bl_label to the short form (CopyAttr, MigNLA, etc.) so button text matches.
+- Option B: Keep bl_label as is for clarity, add bl_description that explains what the step does and maybe mention the short name.
+- Option C: Use short names in bl_description as the "operator name" and keep bl_label human-readable.
+
+I'll assume: (1) Add bl_description to every UI-drawn operator that doesn't have one. (2) Where operators are the migration steps, align naming with the true names - either use them as bl_label (making buttons show "CopyAttr", "MigNLA", etc.) or keep current bl_label and add bl_description that mentions the step. I'll check the panel again: buttons say "Copy attributes", "NLA", "Custom properties", etc. So the panel uses custom text in some places (e.g. text="NLA"). The user said "no operators should be named step1 step2" and "they should all follow a naming convention... CopyAttr, MigNLA...". So I'll plan: add bl_description everywhere; for the six migrator step operators, set bl_label to the canonical short names (CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys) and update panel button text to match if needed. I'll list all operators in operators.py to ensure I don't miss any.
+
+
+---
+
+
+Checking the full operator list and whether any use "step" in their id/label:
+
+
+---
+
+
+
+Tool use: **ripgrep_raw_search**
+
+
+Parameters:
+
+```json
+{
+ "pattern": "^class DLM_OT_|bl_label|bl_description",
+ "path": "T:\\Dynamic-Link-Manager\\ui\\operators.py",
+ "caseInsensitive": false
+}
+```
+Additional data:
+
+```json
+{
+ "isPruned": true,
+ "pattern": "^class DLM_OT_|bl_label|bl_description",
+ "path": "T:\\Dynamic-Link-Manager\\ui\\operators.py",
+ "outputMode": "content",
+ "totalFiles": 1,
+ "totalMatches": 60,
+ "topFiles": [
+ {
+ "uri": "ui\\operators.py",
+ "matchCount": 60
+ }
+ ]
+}
+```
+Status: **completed**
+
+
+
+
+
+---
+
+
+Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+---
+
+Thought Process
+
+Findings:
+1. **No operator has `bl_description`** in the codebase - so every UI-drawn operator needs documentation added.
+2. **No operators are literally named step1/step2** - the bl_idnames are already descriptive (e.g. `dlm.migrator_copy_attributes`). The user wants the *labels* (and possibly docs) to follow the "true names": CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys.
+3. **Operators drawn in the UI** (from panels.py):
+ - Path: make_paths_relative, make_paths_absolute
+ - CharMig: picker_original_character, picker_replacement_character, run_character_migration, migrator_copy_attributes, migrator_migrate_nla, migrator_custom_properties, migrator_bone_constraints, migrator_retarget_relations, migrator_basebody_shapekeys
+ - Tweak: tweak_add_arm, tweak_remove_arm, tweak_bake_arm, tweak_add_leg, tweak_remove_leg, tweak_bake_leg, tweak_add_both, tweak_remove_both, tweak_bake_both
+ - Linked libs: scan_linked_assets, reload_libraries, browse_search_path, remove_search_path, add_search_path, find_libraries_in_folders, open_linked_file, relocate_single_library
+
+4. **Replace linked asset** - not drawn in panels.py I saw; might be used elsewhere. I'll include all operators that could appear in UI (any operator in operators.py that is registered).
+
+Plan:
+- Add `bl_description` to every operator in [ui/operators.py](ui/operators.py) that is drawn in the UI (and optionally all DLM operators for consistency).
+- For the six migrator step operators, set `bl_label` to the canonical short names: CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys. Then update [ui/panels.py](ui/panels.py) where the panel overrides button text (e.g. "Copy attributes", "NLA", "Custom properties") to use the same short names so the UI is consistent.
+
+Actually re-reading the user message again: "No operators should be named step1 step2, etc. They should all follow a naming convention along the lines of the true names". So they're saying (1) don't use step1/step2 (we don't), and (2) do use the convention like CopyAttr, MigNLA, etc. So the six migration step operators should have bl_label (and panel button text) aligned to: CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys. The rest of the operators (path, picker, run migration, tweaks, library ops) just need bl_description; their names are already fine.
+
+Creating the plan.
+
+
+---
+
+
+Checking for other UI references to these operators (menus, keymaps):
+
+
+---
+
+
+
+Tool use: **ripgrep_raw_search**
+
+
+Parameters:
+
+```json
+{
+ "pattern": "dlm\\.(migrator_|tweak_|run_|picker_|make_paths|scan_|reload_|browse_|remove_search|add_search|find_libraries|open_linked|relocate)",
+ "path": "T:\\Dynamic-Link-Manager",
+ "glob": "*.py",
+ "caseInsensitive": false
+}
+```
+Additional data:
+
+```json
+{
+ "isPruned": true,
+ "pattern": "dlm\\.(migrator_|tweak_|run_|picker_|make_paths|scan_|reload_|browse_|remove_search|add_search|find_libraries|open_linked|relocate)",
+ "path": "T:\\Dynamic-Link-Manager",
+ "outputMode": "content",
+ "totalFiles": 6,
+ "totalMatches": 140,
+ "topFiles": [
+ {
+ "uri": "ui/operators.py",
+ "matchCount": 40
+ },
+ {
+ "uri": "ui\\panels.py",
+ "matchCount": 28
+ },
+ {
+ "uri": "ui\\operators.py",
+ "matchCount": 40
+ },
+ {
+ "uri": "ui/preferences.py",
+ "matchCount": 3
+ },
+ {
+ "uri": "ops/library.py",
+ "matchCount": 1
+ },
+ {
+ "uri": "ui/panels.py",
+ "matchCount": 28
+ }
+ ]
+}
+```
+Status: **completed**
+
+
+
+
+
+---
+
+Thought Process
+
+I have a complete picture. No operators use step1/step2 in bl_idname or bl_label. The six migrator operators need:
+1. bl_label updated to: CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys
+2. bl_description added for each
+
+Panel button text in panels.py currently overrides for the six steps - we should update those to match the new bl_label (CopyAttr, MigNLA, etc.) so tooltips and search show the same names.
+
+I'll create the plan now.
+
+
+---
+
+
+Creating the plan.
+
+
+---
+
+
+
+Tool use: **create_plan**
+
+
+Parameters:
+
+```json
+{
+ "plan": "# Operator documentation and naming convention\n\n## Current state\n\n- **No operator has `bl_description`** in [ui/operators.py](ui/operators.py). Blender uses this for tooltips and search.\n- **No operators use step1/step2** — `bl_idname`s are already descriptive (e.g. `dlm.migrator_copy_attributes`). The only \"step\" references are debug `print` strings in [ops/migrator.py](ops/migrator.py) (e.g. `[DLM step6]`); those are out of scope unless you want them renamed for consistency.\n- **Six migrator step operators** currently have `bl_label` like \"Copy attributes\", \"Migrate NLA\", \"Custom properties\", \"Bone constraints\", \"Retarget relations\", \"Migrate BaseBody shapekeys\". You want these to use the canonical short names: **CopyAttr**, **MigNLA**, **MigCustProps**, **MigBoneConst**, **RetargRelatives**, **MigBBodyShapeKeys**.\n- **Panel button text** in [ui/panels.py](ui/panels.py) overrides labels for the six steps (e.g. `text=\"NLA\"`, `text=\"Copy attributes\"`). These should be updated to match the new `bl_label` so the UI and search/tooltips are consistent.\n\n## Scope: operators drawn in the UI\n\nFrom [ui/panels.py](ui/panels.py) and [ui/preferences.py](ui/preferences.py):\n\n| Area | Operators |\n|------|-----------|\n| Path | `make_paths_relative`, `make_paths_absolute` |\n| CharMig | `picker_original_character`, `picker_replacement_character`, `run_character_migration`, and the six step ops |\n| Tweak Tools | `tweak_add_arm`, `tweak_remove_arm`, `tweak_bake_arm`, `tweak_add_leg`, `tweak_remove_leg`, `tweak_bake_leg`, `tweak_add_both`, `tweak_remove_both`, `tweak_bake_both` |\n| Linked Libraries | `scan_linked_assets`, `reload_libraries`, `browse_search_path`, `remove_search_path`, `add_search_path`, `find_libraries_in_folders`, `open_linked_file`, `relocate_single_library` |\n\nOptionally include `replace_linked_asset` and `attempt_relink` if they appear in menus; they are registered but not drawn in the main panel.\n\n---\n\n## 1. Add `bl_description` to all UI-drawn operators ([ui/operators.py](ui/operators.py))\n\nAdd a single line after `bl_label` (and after `bl_icon` where present) for each operator, e.g.:\n\n- **Replace Linked Asset** — e.g. \"Open file browser to replace the linked asset with another file.\"\n- **Scan Linked Libraries** — e.g. \"Scan the current file for linked libraries and list their status.\"\n- **Find Libraries in Folders** — e.g. \"Search addon search paths for missing library blend files.\"\n- **Open Linked File** — e.g. \"Open the selected linked blend file in a new Blender instance.\"\n- **Add / Remove / Browse Search Path** — one-line descriptions for path list management.\n- **Attempt Relink** — e.g. \"Try to relink missing libraries using the configured search paths.\"\n- **Reload Libraries** — e.g. \"Reload all linked libraries (or fallback manual reload).\"\n- **Make Paths Relative / Absolute** — e.g. \"Convert all internal file paths to relative (or absolute).\"\n- **Relocate Library** — e.g. \"Point a selected library to a new blend file and reload.\"\n- **Six migrator steps** — short descriptions of what each step does (copy object/armature attributes; migrate NLA; copy custom properties; migrate bone constraints; retarget relations; migrate base body shape keys). Can reference the short name in the description if desired.\n- **Run Character Migration** — e.g. \"Run all six migration steps (CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys) in order.\"\n- **Pick Original / Pick Replacement** — e.g. \"Set the original (or replacement) character armature from the active object.\"\n- **Tweak operators (Add/Remove/Bake × Arm/Leg/Both)** — one-line descriptions for adding/removing/baking tweak constraints.\n\nKeep each description one short sentence so tooltips stay readable.\n\n---\n\n## 2. Rename six migrator step operators to canonical names ([ui/operators.py](ui/operators.py))\n\nUpdate only the six step operators:\n\n| Current `bl_label` | New `bl_label` |\n|--------------------|----------------|\n| Copy attributes | **CopyAttr** |\n| Migrate NLA | **MigNLA** |\n| Custom properties | **MigCustProps** |\n| Bone constraints | **MigBoneConst** |\n| Retarget relations | **RetargRelatives** |\n| Migrate BaseBody shapekeys | **MigBBodyShapeKeys** |\n\nDo **not** change `bl_idname` (e.g. `dlm.migrator_copy_attributes`) to avoid breaking keymaps or saved files; only `bl_label` and panel button text.\n\n---\n\n## 3. Update panel button text ([ui/panels.py](ui/panels.py))\n\nIn the Character Migrator box, the layout currently uses custom `text=` for the six step buttons. Update them to match the new `bl_label` so tooltip and button text align:\n\n- `text=\"Copy attributes\"` → `text=\"CopyAttr\"`\n- `text=\"NLA\"` → `text=\"MigNLA\"`\n- `text=\"Custom properties\"` → `text=\"MigCustProps\"`\n- `text=\"Bone constraints\"` → `text=\"MigBoneConst\"`\n- `text=\"Retarget relations\"` → `text=\"RetargRelatives\"`\n- `text=\"BaseBody ShapeKeys\"` → `text=\"MigBBodyShapeKeys\"`\n\nYou can omit the `text=` argument to use `bl_label` by default, which would keep a single source of truth in operators.py.\n\n---\n\n## 4. Optional: debug prints in migrator\n\n[ops/migrator.py](ops/migrator.py) uses `[DLM step6]` in print statements. Renaming these to e.g. `[DLM MigBBodyShapeKeys]` is optional and would make logs consistent with the operator names; include only if you want log alignment.\n\n---\n\n## Summary of edits\n\n- **[ui/operators.py](ui/operators.py):** Add `bl_description` to every operator that is drawn in the UI (and optionally to `replace_linked_asset` / `attempt_relink`). Change `bl_label` of the six migrator step classes to CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys.\n- **[ui/panels.py](ui/panels.py):** Update the six `layout.operator(..., text=\"...\")` calls in the Character Migrator section to use the new labels (either explicit `text=\"CopyAttr\"` etc. or remove `text` to use `bl_label`).\n\nNo changes to `bl_idname`, registration, or [ops/migrator.py](ops/migrator.py) logic; only documentation and display names.\n",
+ "name": "Operator docs and naming",
+ "overview": "Add bl_description to every operator drawn in the UI and align the six character-migrator step operators to the canonical short names (CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys). No operators are currently named step1/step2; only labels and docs need updating."
+}
+```
+Additional data:
+
+```json
+{
+ "reviewData": {
+ "status": "Requested",
+ "selectedOption": "none",
+ "isShowingInput": false
+ },
+ "planUri": "file:///c%3A/Users/Nathan/.cursor/plans/operator_docs_and_naming_8f8ed6b9.plan.md",
+ "hasOpenedEditor": true
+}
+```
+Result:
+
+```json
+{
+ "rejected": {}
+}
+```
+Status: **completed**
+
+
+
+
+
+---
+
+_**User**_
+
+Implement the plan as specified, it is attached for your reference. Do NOT edit the plan file itself.
+
+To-do's from the plan have already been created. Do not create them again. Mark them as in_progress as you work, starting with the first one. Don't stop until you have completed all the to-dos.
+
+---
+
+_**Agent (model default, mode Agent)**_
+
+Thought Process
+
+The user wants me to implement the plan for operator documentation and naming. Let me check the todos first and then implement:
+
+1. Add `bl_description` to all UI-drawn operators in ui/operators.py
+2. Rename six migrator step operators to canonical names (bl_label) in ui/operators.py
+3. Update panel button text in ui/panels.py
+
+I'll need to read the current todos, then make the edits. Let me start by reading the operators file fully to add all bl_descriptions and update the six bl_labels, then update panels.py.
+
+
+---
+
+
+
+
+
+---
+
+
+Tool use: **todo_write** • Todo List
+
+- [-] Add bl_description to all UI-drawn operators (ui/operators.py)
+- [ ] Rename six migrator step bl_labels to canonical names
+- [ ] Update panel button text for six steps (ui/panels.py)
+- [x] Add 9 tweak operators and register in operators.py
+
+
+
+
+---
+
+
+Tool use: **read_file_v2** • Read file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+---
+
+Thought Process
+
+I'll add bl_description to every operator and update the six migrator step bl_labels in one pass through operators.py. I'll do multiple search_replace calls.
+
+
+---
+
+
+Adding `bl_description` to all operators and updating the six migrator `bl_label`s.
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+Thought Process
+
+Good progress. Now add bl_description and update bl_label for the six migrator steps, then run_character_migration, pickers, and tweak operators.
+
+
+---
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\operators.py
+
+
+
+
+
+---
+
+Thought Process
+
+All operators now have bl_description and the six migrator steps have the new bl_labels. Updating the error message in run_character_migration to use the new step names for consistency (it says "Migration failed at step {i + 1}: {MIGRATOR_STEP_OPS[i]}"). The plan didn't ask to change that - MIGRATOR_STEP_OPS holds bl_idnames. The report could say the step name for clarity; I'll leave it as is unless we want to show the bl_label. Actually the plan said no logic changes. So leave it.
+
+Now mark todo 1 and 2 complete and do todo 3: update panel button text in panels.py.
+
+
+---
+
+
+
+
+
+---
+
+
+Tool use: **todo_write** • Todo List
+
+- [x] Add bl_description to all UI-drawn operators (ui/operators.py)
+- [x] Rename six migrator step bl_labels to canonical names
+- [-] Update panel button text for six steps (ui/panels.py)
+- [x] Add 9 tweak operators and register in operators.py
+
+
+
+
+---
+
+
+
+Tool use: **edit_file_v2** • Edit file: T:\Dynamic-Link-Manager\ui\panels.py
+
+
+
+
+
+---
+
+Thought Process
+
+Panel is updated. Mark todo 3 complete and run lints on the edited files.
+
+
+---
+
+
+
+
+
+---
+
+
+Tool use: **todo_write** • Todo List
+
+- [x] Add bl_description to all UI-drawn operators (ui/operators.py)
+- [x] Rename six migrator step bl_labels to canonical names
+- [x] Update panel button text for six steps (ui/panels.py)
+- [x] Add 9 tweak operators and register in operators.py
+
+
+
+
+---
+
+
+
+Tool use: **read_lints**
+
+
+Parameters:
+
+```json
+{
+ "paths": [
+ "T:\\Dynamic-Link-Manager\\ui\\operators.py",
+ "T:\\Dynamic-Link-Manager\\ui\\panels.py"
+ ]
+}
+```
+Result:
+
+```json
+{
+ "linterErrorsByFile": [
+ {
+ "relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ui\\operators.py"
+ },
+ {
+ "relativeWorkspacePath": "T:\\Dynamic-Link-Manager\\ui\\panels.py"
+ }
+ ]
+}
+```
+Status: **completed**
+
+
+
+
+
+---
+
+Thought Process
+
+All tasks are done. No linter errors. I'll give a short summary to the user.
+
+
+---
+
+
+Summary of what was implemented:
+
+**1. [ui/operators.py](ui/operators.py)**
+- **bl_description** added for every operator (all 27), including:
+ - Library/path: replace_linked_asset, scan_linked_assets, find_libraries_in_folders, open_linked_file, add/remove/browse_search_path, attempt_relink, reload_libraries, make_paths_relative/absolute, relocate_single_library
+ - Character migrator: run_character_migration, picker_original_character, picker_replacement_character, and the six step operators
+ - Tweak tools: all nine add/remove/bake (arm/leg/both) operators
+
+- **Six migrator step bl_labels** set to the canonical short names:
+ - Copy attributes → **CopyAttr**
+ - Migrate NLA → **MigNLA**
+ - Custom properties → **MigCustProps**
+ - Bone constraints → **MigBoneConst**
+ - Retarget relations → **RetargRelatives**
+ - Migrate BaseBody shapekeys → **MigBBodyShapeKeys**
+
+**2. [ui/panels.py](ui/panels.py)**
+- The six step buttons in the Character Migrator box now use the same labels: `CopyAttr`, `MigNLA`, `MigCustProps`, `MigBoneConst`, `RetargRelatives`, `MigBBodyShapeKeys`.
+
+`bl_idname` values were not changed. Linting reports no issues.
+
+---
+
+_**User**_
+
+Sorry, I was referring to "run_step_x" defs in migrator.py.
+
+---
+
+_**Agent (model default, mode Agent)**_
+
+
---
diff --git a/ops/migrator.py b/ops/migrator.py
index f20bae0..7551baa 100644
--- a/ops/migrator.py
+++ b/ops/migrator.py
@@ -37,8 +37,8 @@ def get_pair_automatic(context):
return pairs[0] if pairs else (None, None)
-def run_step_1(orig, rep):
- """Copy armature object attributes: location, rotation, scale."""
+def run_copy_attr(orig, rep):
+ """Copy armature object attributes: location, rotation, scale (CopyAttr)."""
rep.location = orig.location.copy()
if orig.rotation_mode == "QUATERNION":
rep.rotation_quaternion = orig.rotation_quaternion.copy()
@@ -64,8 +64,8 @@ def _mirror_als_turn_on(orig, rep):
pass
-def run_step_2(orig, rep):
- """Migrate NLA: copy tracks and strips to replacement; or apply active action only."""
+def run_mig_nla(orig, rep):
+ """Migrate NLA: copy tracks and strips to replacement; or apply active action only (MigNLA)."""
if not orig.animation_data:
return
ad = orig.animation_data
@@ -127,7 +127,7 @@ def run_step_2(orig, rep):
EXCLUDE_PROPS = {"_RNA_UI", "rigify_type", "rigify_parameters"}
-def run_step_3(orig, rep):
+def run_mig_cust_props(orig, rep):
"""Custom properties: copy pose-bone custom props, exclude rigify internals."""
for pbone in orig.pose.bones:
if pbone.name not in rep.pose.bones:
@@ -142,7 +142,7 @@ def run_step_3(orig, rep):
pass
-def run_step_4(orig, rep, orig_to_rep):
+def run_mig_bone_const(orig, rep, orig_to_rep):
"""Bone constraints: remove stale on rep, copy from orig with retarget, trim duplicates."""
other_originals = [o for o in orig_to_rep if o != orig]
for pb in rep.pose.bones:
@@ -181,7 +181,7 @@ def run_step_4(orig, rep, orig_to_rep):
rr.remove(rr[-1])
-def run_step_5(orig, rep, rep_descendants, orig_to_rep):
+def run_retarg_relatives(orig, rep, rep_descendants, orig_to_rep):
"""Retarget relations: parents, constraint targets, Armature modifiers to rep."""
candidates = set(rep_descendants)
for ob in bpy.data.objects:
@@ -255,7 +255,7 @@ def _find_base_body(armature, descendants_iter, rep_base_name=None):
return candidates[0]
-def run_step_6(orig, rep, rep_descendants, context=None):
+def run_mig_bbody_shapekeys(orig, rep, rep_descendants, context=None):
"""Replacement base body: library override (fully editable when context given), copy shapekey values, then shape-key action."""
orig_descendants = list(descendants(orig))
for ob in list(rep_descendants):
@@ -366,12 +366,12 @@ def run_full_migration(context):
rep_descendants = descendants(rep)
try:
- run_step_1(orig, rep)
- run_step_2(orig, rep)
- run_step_3(orig, rep)
- run_step_4(orig, rep, orig_to_rep)
- run_step_5(orig, rep, rep_descendants, orig_to_rep)
- run_step_6(orig, rep, rep_descendants, context)
+ run_copy_attr(orig, rep)
+ run_mig_nla(orig, rep)
+ run_mig_cust_props(orig, rep)
+ run_mig_bone_const(orig, rep, orig_to_rep)
+ run_retarg_relatives(orig, rep, rep_descendants, orig_to_rep)
+ run_mig_bbody_shapekeys(orig, rep, rep_descendants, context)
except Exception as e:
return False, str(e)
return True, f"Migrated {orig.name} → {rep.name}"
diff --git a/ui/operators.py b/ui/operators.py
index bd6b4ae..720cb36 100644
--- a/ui/operators.py
+++ b/ui/operators.py
@@ -18,6 +18,7 @@ def _prefs(context):
class DLM_OT_replace_linked_asset(Operator):
bl_idname = "dlm.replace_linked_asset"
bl_label = "Replace Linked Asset"
+ bl_description = "Open file browser to replace the linked asset with another file"
bl_options = {"REGISTER", "UNDO"}
filepath: StringProperty(name="File Path", description="Path to the new asset file", subtype="FILE_PATH")
@@ -48,6 +49,7 @@ class DLM_OT_replace_linked_asset(Operator):
class DLM_OT_scan_linked_assets(Operator):
bl_idname = "dlm.scan_linked_assets"
bl_label = "Scan Linked Libraries"
+ bl_description = "Scan the current file for linked libraries and list their status"
bl_options = {"REGISTER"}
def execute(self, context):
@@ -59,6 +61,7 @@ class DLM_OT_scan_linked_assets(Operator):
class DLM_OT_find_libraries_in_folders(Operator):
bl_idname = "dlm.find_libraries_in_folders"
bl_label = "Find Libraries in Folders"
+ bl_description = "Search addon search paths for missing library blend files"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
@@ -70,6 +73,7 @@ class DLM_OT_find_libraries_in_folders(Operator):
class DLM_OT_open_linked_file(Operator):
bl_idname = "dlm.open_linked_file"
bl_label = "Open Linked File"
+ bl_description = "Open the selected linked blend file in a new Blender instance"
bl_options = {"REGISTER"}
filepath: StringProperty(name="File Path", default="")
@@ -89,6 +93,7 @@ class DLM_OT_open_linked_file(Operator):
class DLM_OT_add_search_path(Operator):
bl_idname = "dlm.add_search_path"
bl_label = "Add Search Path"
+ bl_description = "Add a new folder to the list of search paths for finding libraries"
bl_options = {"REGISTER"}
def execute(self, context):
@@ -103,6 +108,7 @@ class DLM_OT_add_search_path(Operator):
class DLM_OT_remove_search_path(Operator):
bl_idname = "dlm.remove_search_path"
bl_label = "Remove Search Path"
+ bl_description = "Remove the selected search path from the list"
bl_options = {"REGISTER"}
index: IntProperty(name="Index", default=0)
@@ -117,6 +123,7 @@ class DLM_OT_remove_search_path(Operator):
class DLM_OT_attempt_relink(Operator):
bl_idname = "dlm.attempt_relink"
bl_label = "Attempt Relink"
+ bl_description = "Try to relink missing libraries using the configured search paths"
bl_options = {"REGISTER"}
def execute(self, context):
@@ -128,6 +135,7 @@ class DLM_OT_attempt_relink(Operator):
class DLM_OT_browse_search_path(Operator):
bl_idname = "dlm.browse_search_path"
bl_label = "Browse Search Path"
+ bl_description = "Browse to set the folder for the selected search path"
bl_options = {"REGISTER"}
index: IntProperty(name="Index", default=0)
filepath: StringProperty(name="Search Path", subtype="DIR_PATH")
@@ -150,6 +158,7 @@ class DLM_OT_browse_search_path(Operator):
class DLM_OT_reload_libraries(Operator):
bl_idname = "dlm.reload_libraries"
bl_label = "Reload Libraries"
+ bl_description = "Reload all linked libraries (or fallback manual reload)"
bl_options = {"REGISTER"}
def execute(self, context):
@@ -171,6 +180,7 @@ class DLM_OT_reload_libraries(Operator):
class DLM_OT_make_paths_relative(Operator):
bl_idname = "dlm.make_paths_relative"
bl_label = "Make Paths Relative"
+ bl_description = "Convert all internal file paths to relative"
bl_options = {"REGISTER"}
def execute(self, context):
@@ -186,6 +196,7 @@ class DLM_OT_make_paths_relative(Operator):
class DLM_OT_make_paths_absolute(Operator):
bl_idname = "dlm.make_paths_absolute"
bl_label = "Make Paths Absolute"
+ bl_description = "Convert all internal file paths to absolute"
bl_options = {"REGISTER"}
def execute(self, context):
@@ -201,6 +212,7 @@ class DLM_OT_make_paths_absolute(Operator):
class DLM_OT_relocate_single_library(Operator):
bl_idname = "dlm.relocate_single_library"
bl_label = "Relocate Library"
+ bl_description = "Point the selected library to a new blend file and reload"
bl_options = {"REGISTER", "UNDO"}
target_filepath: StringProperty(name="Current Library Path", default="")
filepath: StringProperty(name="New Library File", subtype="FILE_PATH", default="")
@@ -256,7 +268,8 @@ def _get_migrator_pair(context):
class DLM_OT_migrator_copy_attributes(Operator):
bl_idname = "dlm.migrator_copy_attributes"
- bl_label = "Copy attributes"
+ bl_label = "CopyAttr"
+ bl_description = "Copy object and armature attributes from original to replacement character"
bl_icon = "COPY_ID"
bl_options = {"REGISTER", "UNDO"}
@@ -266,8 +279,8 @@ class DLM_OT_migrator_copy_attributes(Operator):
self.report({"ERROR"}, "No valid character pair (set Original/Replacement or enable Automatic).")
return {"CANCELLED"}
try:
- from ..ops.migrator import run_step_1
- run_step_1(orig, rep)
+ from ..ops.migrator import run_copy_attr
+ run_copy_attr(orig, rep)
self.report({"INFO"}, "Copy attributes done.")
return {"FINISHED"}
except Exception as e:
@@ -277,7 +290,8 @@ class DLM_OT_migrator_copy_attributes(Operator):
class DLM_OT_migrator_migrate_nla(Operator):
bl_idname = "dlm.migrator_migrate_nla"
- bl_label = "Migrate NLA"
+ bl_label = "MigNLA"
+ bl_description = "Migrate NLA tracks and strips from original to replacement character"
bl_icon = "NLA"
bl_options = {"REGISTER", "UNDO"}
@@ -287,8 +301,8 @@ class DLM_OT_migrator_migrate_nla(Operator):
self.report({"ERROR"}, "No valid character pair.")
return {"CANCELLED"}
try:
- from ..ops.migrator import run_step_2
- run_step_2(orig, rep)
+ from ..ops.migrator import run_mig_nla
+ run_mig_nla(orig, rep)
self.report({"INFO"}, "Migrate NLA done.")
return {"FINISHED"}
except Exception as e:
@@ -298,7 +312,8 @@ class DLM_OT_migrator_migrate_nla(Operator):
class DLM_OT_migrator_custom_properties(Operator):
bl_idname = "dlm.migrator_custom_properties"
- bl_label = "Custom properties"
+ bl_label = "MigCustProps"
+ bl_description = "Copy custom properties from original to replacement character"
bl_icon = "PROPERTIES"
bl_options = {"REGISTER", "UNDO"}
@@ -308,8 +323,8 @@ class DLM_OT_migrator_custom_properties(Operator):
self.report({"ERROR"}, "No valid character pair.")
return {"CANCELLED"}
try:
- from ..ops.migrator import run_step_3
- run_step_3(orig, rep)
+ from ..ops.migrator import run_mig_cust_props
+ run_mig_cust_props(orig, rep)
self.report({"INFO"}, "Custom properties done.")
return {"FINISHED"}
except Exception as e:
@@ -319,7 +334,8 @@ class DLM_OT_migrator_custom_properties(Operator):
class DLM_OT_migrator_bone_constraints(Operator):
bl_idname = "dlm.migrator_bone_constraints"
- bl_label = "Bone constraints"
+ bl_label = "MigBoneConst"
+ bl_description = "Migrate bone constraints from original to replacement armature"
bl_icon = "CONSTRAINT_BONE"
bl_options = {"REGISTER", "UNDO"}
@@ -329,9 +345,9 @@ class DLM_OT_migrator_bone_constraints(Operator):
self.report({"ERROR"}, "No valid character pair.")
return {"CANCELLED"}
try:
- from ..ops.migrator import run_step_4
+ from ..ops.migrator import run_mig_bone_const
orig_to_rep = {orig: rep}
- run_step_4(orig, rep, orig_to_rep)
+ run_mig_bone_const(orig, rep, orig_to_rep)
self.report({"INFO"}, "Bone constraints done.")
return {"FINISHED"}
except Exception as e:
@@ -341,7 +357,8 @@ class DLM_OT_migrator_bone_constraints(Operator):
class DLM_OT_migrator_retarget_relations(Operator):
bl_idname = "dlm.migrator_retarget_relations"
- bl_label = "Retarget relations"
+ bl_label = "RetargRelatives"
+ bl_description = "Retarget parent/child and other relations to the replacement character"
bl_icon = "ORIENTATION_PARENT"
bl_options = {"REGISTER", "UNDO"}
@@ -351,11 +368,11 @@ class DLM_OT_migrator_retarget_relations(Operator):
self.report({"ERROR"}, "No valid character pair.")
return {"CANCELLED"}
try:
- from ..ops.migrator import run_step_5
+ from ..ops.migrator import run_retarg_relatives
from ..utils import descendants
rep_descendants = descendants(rep)
orig_to_rep = {orig: rep}
- run_step_5(orig, rep, rep_descendants, orig_to_rep)
+ run_retarg_relatives(orig, rep, rep_descendants, orig_to_rep)
self.report({"INFO"}, "Retarget relations done.")
return {"FINISHED"}
except Exception as e:
@@ -365,7 +382,8 @@ class DLM_OT_migrator_retarget_relations(Operator):
class DLM_OT_migrator_basebody_shapekeys(Operator):
bl_idname = "dlm.migrator_basebody_shapekeys"
- bl_label = "Migrate BaseBody shapekeys"
+ bl_label = "MigBBodyShapeKeys"
+ bl_description = "Migrate base body mesh shape key values from original to replacement"
bl_icon = "SHAPEKEY_DATA"
bl_options = {"REGISTER", "UNDO"}
@@ -375,10 +393,10 @@ class DLM_OT_migrator_basebody_shapekeys(Operator):
self.report({"ERROR"}, "No valid character pair.")
return {"CANCELLED"}
try:
- from ..ops.migrator import run_step_6
+ from ..ops.migrator import run_mig_bbody_shapekeys
from ..utils import descendants
rep_descendants = descendants(rep)
- run_step_6(orig, rep, rep_descendants, context)
+ run_mig_bbody_shapekeys(orig, rep, rep_descendants, context)
self.report({"INFO"}, "Migrate BaseBody shapekeys done.")
return {"FINISHED"}
except Exception as e:
@@ -399,6 +417,7 @@ MIGRATOR_STEP_OPS = (
class DLM_OT_run_character_migration(Operator):
bl_idname = "dlm.run_character_migration"
bl_label = "Run Character Migration"
+ bl_description = "Run all six migration steps (CopyAttr, MigNLA, MigCustProps, MigBoneConst, RetargRelatives, MigBBodyShapeKeys) in order"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
@@ -422,6 +441,7 @@ class DLM_OT_run_character_migration(Operator):
class DLM_OT_picker_original_character(Operator):
bl_idname = "dlm.picker_original_character"
bl_label = "Pick Original"
+ bl_description = "Set the original character armature from the active object"
bl_options = {"REGISTER"}
def execute(self, context):
@@ -437,6 +457,7 @@ class DLM_OT_picker_original_character(Operator):
class DLM_OT_picker_replacement_character(Operator):
bl_idname = "dlm.picker_replacement_character"
bl_label = "Pick Replacement"
+ bl_description = "Set the replacement character armature from the active object"
bl_options = {"REGISTER"}
def execute(self, context):
@@ -457,6 +478,7 @@ def _tweak_poll(context):
class DLM_OT_tweak_add_arm(Operator):
bl_idname = "dlm.tweak_add_arm"
bl_label = "Add Arm Tweaks"
+ bl_description = "Add tweak bone constraints to arm bones on the replacement character"
bl_options = {"REGISTER", "UNDO"}
@classmethod
@@ -474,6 +496,7 @@ class DLM_OT_tweak_add_arm(Operator):
class DLM_OT_tweak_remove_arm(Operator):
bl_idname = "dlm.tweak_remove_arm"
bl_label = "Remove Arm Tweaks"
+ bl_description = "Remove arm tweak constraints from the replacement character"
bl_options = {"REGISTER", "UNDO"}
@classmethod
@@ -491,6 +514,7 @@ class DLM_OT_tweak_remove_arm(Operator):
class DLM_OT_tweak_bake_arm(Operator):
bl_idname = "dlm.tweak_bake_arm"
bl_label = "Bake Arm Tweaks"
+ bl_description = "Bake arm tweak constraints to keyframes and optionally remove constraints"
bl_options = {"REGISTER", "UNDO"}
@classmethod
@@ -516,6 +540,7 @@ class DLM_OT_tweak_bake_arm(Operator):
class DLM_OT_tweak_add_leg(Operator):
bl_idname = "dlm.tweak_add_leg"
bl_label = "Add Leg Tweaks"
+ bl_description = "Add tweak bone constraints to leg bones on the replacement character"
bl_options = {"REGISTER", "UNDO"}
@classmethod
@@ -533,6 +558,7 @@ class DLM_OT_tweak_add_leg(Operator):
class DLM_OT_tweak_remove_leg(Operator):
bl_idname = "dlm.tweak_remove_leg"
bl_label = "Remove Leg Tweaks"
+ bl_description = "Remove leg tweak constraints from the replacement character"
bl_options = {"REGISTER", "UNDO"}
@classmethod
@@ -550,6 +576,7 @@ class DLM_OT_tweak_remove_leg(Operator):
class DLM_OT_tweak_bake_leg(Operator):
bl_idname = "dlm.tweak_bake_leg"
bl_label = "Bake Leg Tweaks"
+ bl_description = "Bake leg tweak constraints to keyframes and optionally remove constraints"
bl_options = {"REGISTER", "UNDO"}
@classmethod
@@ -575,6 +602,7 @@ class DLM_OT_tweak_bake_leg(Operator):
class DLM_OT_tweak_add_both(Operator):
bl_idname = "dlm.tweak_add_both"
bl_label = "Add Arm & Leg Tweaks"
+ bl_description = "Add tweak bone constraints to both arm and leg bones"
bl_options = {"REGISTER", "UNDO"}
@classmethod
@@ -592,6 +620,7 @@ class DLM_OT_tweak_add_both(Operator):
class DLM_OT_tweak_remove_both(Operator):
bl_idname = "dlm.tweak_remove_both"
bl_label = "Remove Arm & Leg Tweaks"
+ bl_description = "Remove all arm and leg tweak constraints from the replacement character"
bl_options = {"REGISTER", "UNDO"}
@classmethod
@@ -609,6 +638,7 @@ class DLM_OT_tweak_remove_both(Operator):
class DLM_OT_tweak_bake_both(Operator):
bl_idname = "dlm.tweak_bake_both"
bl_label = "Bake Arm & Leg Tweaks"
+ bl_description = "Bake all arm and leg tweak constraints to keyframes and optionally remove constraints"
bl_options = {"REGISTER", "UNDO"}
@classmethod
diff --git a/ui/panels.py b/ui/panels.py
index 94cf3e8..78ba48d 100644
--- a/ui/panels.py
+++ b/ui/panels.py
@@ -86,13 +86,13 @@ class DLM_PT_main_panel(Panel):
row = box.row()
row.operator("dlm.run_character_migration", text="Run migration", icon="ARMATURE_DATA")
row = box.row(align=True)
- row.operator("dlm.migrator_copy_attributes", text="Copy attributes", icon="COPY_ID")
- row.operator("dlm.migrator_migrate_nla", text="NLA", icon="NLA")
- row.operator("dlm.migrator_custom_properties", text="Custom properties", icon="PROPERTIES")
+ row.operator("dlm.migrator_copy_attributes", text="CopyAttr", icon="COPY_ID")
+ row.operator("dlm.migrator_migrate_nla", text="MigNLA", icon="NLA")
+ row.operator("dlm.migrator_custom_properties", text="MigCustProps", icon="PROPERTIES")
row = box.row(align=True)
- row.operator("dlm.migrator_bone_constraints", text="Bone constraints", icon="CONSTRAINT_BONE")
- row.operator("dlm.migrator_retarget_relations", text="Retarget relations", icon="ORIENTATION_PARENT")
- row.operator("dlm.migrator_basebody_shapekeys", text="BaseBody ShapeKeys", icon="SHAPEKEY_DATA")
+ row.operator("dlm.migrator_bone_constraints", text="MigBoneConst", icon="CONSTRAINT_BONE")
+ row.operator("dlm.migrator_retarget_relations", text="RetargRelatives", icon="ORIENTATION_PARENT")
+ row.operator("dlm.migrator_basebody_shapekeys", text="MigBBodyShapeKeys", icon="SHAPEKEY_DATA")
# Tweak Tools
tweak_box = layout.box()