Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
f1b6664
feat: sync requests, per-state request component and a dedicated pane…
YozoraKurage Aug 8, 2026
c51e124
feat: port the remaining AAP math gadgets — reciprocal, divide, frame…
YozoraKurage Aug 8, 2026
125d75e
feat: curve-baked 1D LUT and atan2 gadgets; sub-assets now appear in …
YozoraKurage Aug 8, 2026
41d9e6a
feat: buffer gadget — copy a parameter N frames late to align pipelin…
YozoraKurage Aug 8, 2026
20d63fd
feat: DBT gadgets from C# recipes, and a two-half split for exported …
YozoraKurage Aug 8, 2026
579e927
docs: teach the recipe-refactor skill the two-half layout
YozoraKurage Aug 8, 2026
ee90a1b
fix: carry behaviours on state machines and synced-layer behaviour ov…
YozoraKurage Aug 8, 2026
eeb03f7
refactor: lift analyzer issue types out of ControllerAnalyzer
YozoraKurage Aug 8, 2026
1b0da34
refactor: extract tarjan scc into a reusable utility
YozoraKurage Aug 8, 2026
7dd6b58
test: cover the tarjan scc utility
YozoraKurage Aug 8, 2026
b6ff525
refactor: move state containment checks into AnimatorControllerExtens…
YozoraKurage Aug 8, 2026
3810b12
refactor: share BuildPathLabel between cleanup and usage finder
YozoraKurage Aug 8, 2026
0dd78c8
refactor: use DbtBuilder parameter helpers instead of private copies
YozoraKurage Aug 8, 2026
726b85c
refactor: reuse DbtBuilder.UniqueLayerName in LayersPanel
YozoraKurage Aug 8, 2026
2191239
refactor: split ControllerBuilder into one file per type
YozoraKurage Aug 8, 2026
48455d2
refactor: lift the export driver out of RecipeExporter
YozoraKurage Aug 8, 2026
e69348a
refactor: extract the fold planner from the export driver
YozoraKurage Aug 8, 2026
6ff07b5
refactor: extract async sync scheduling math into AsyncSyncSchedule
YozoraKurage Aug 8, 2026
e529a67
refactor: add GraphSelectionSet for typed selection buckets
YozoraKurage Aug 8, 2026
e441310
refactor: extract async sync cost model into AsyncSyncCost
YozoraKurage Aug 8, 2026
d50d057
refactor: extract async sync naming into AsyncSyncNaming
YozoraKurage Aug 8, 2026
7a8d427
refactor: extract node creation into NodeCommands
YozoraKurage Aug 8, 2026
f1cbb39
refactor: extract the mixed-value multi-edit skeleton into MultiEditGui
YozoraKurage Aug 9, 2026
02257ef
refactor: move Apply into AsyncSyncApplier
YozoraKurage Aug 9, 2026
4f8b9a5
refactor: extract shared panel gui helpers into PanelGui
YozoraKurage Aug 9, 2026
cb53d44
refactor: extract frame and note editing into FrameCommands
YozoraKurage Aug 9, 2026
8412c17
refactor: add NotifyLayerStructureChanged context extension
YozoraKurage Aug 9, 2026
cb7e793
refactor: collapse the six frame-data mutate bodies
YozoraKurage Aug 9, 2026
70a2ade
refactor: name the apply steps
YozoraKurage Aug 9, 2026
3b67a34
refactor: extract the cleanup section into CleanupInspector
YozoraKurage Aug 9, 2026
6899ded
refactor: extract the overview section into OverviewInspector
YozoraKurage Aug 9, 2026
d46a65c
refactor: extract the state machine section into StateMachineInspector
YozoraKurage Aug 9, 2026
db3e70c
refactor: extract the condition helpers into ConditionGui
YozoraKurage Aug 9, 2026
124a414
test: cover AsyncSyncSchedule directly
YozoraKurage Aug 9, 2026
b4cb577
refactor: extract transition commands into EdgeCommands
YozoraKurage Aug 9, 2026
3a5fe32
refactor: unify chain and fan pair enumeration
YozoraKurage Aug 9, 2026
0e9a3fd
refactor: extract the multi-transition editor into MultiTransitionIns…
YozoraKurage Aug 9, 2026
8e365eb
refactor: extract the transition sections into TransitionInspector
YozoraKurage Aug 9, 2026
ad23999
refactor: extract the vrc behaviour drawers into VrcBehaviourDrawers
YozoraKurage Aug 9, 2026
659d4a1
refactor: extract the state behaviour list into BehaviourInspector
YozoraKurage Aug 9, 2026
b94ec04
refactor: extract multi-state behaviours into MultiStateBehaviourInsp…
YozoraKurage Aug 9, 2026
d130ed9
refactor: extract the sync request box into SyncRequestInspector
YozoraKurage Aug 9, 2026
5abdf08
refactor: extract the state form into StateInspector
YozoraKurage Aug 9, 2026
8eb28c8
refactor: extract the multi-state editor into MultiStateInspector
YozoraKurage Aug 9, 2026
635c23e
refactor: extract the note section into NoteInspector
YozoraKurage Aug 9, 2026
7847716
refactor: extract the frame section into FrameInspector
YozoraKurage Aug 9, 2026
48ef202
refactor: share the transition capture-recreate core
YozoraKurage Aug 9, 2026
e678ac7
refactor: extract clipboard orchestration into GraphClipboard
YozoraKurage Aug 9, 2026
f9ba763
test: first coverage for graph command classes
YozoraKurage Aug 9, 2026
850dea8
refactor: share the behaviour box header and body between both lists
YozoraKurage Aug 9, 2026
377c54f
refactor: extract the tab strip from DaerDWindow
YozoraKurage Aug 9, 2026
8cebd36
refactor: say why the multi-state editor holds the other two inspectors
YozoraKurage Aug 9, 2026
b54c933
refactor: extract the graph context menu into GraphContextMenu
YozoraKurage Aug 9, 2026
4e53d9a
refactor: move EditorWindowOwner into its own file
YozoraKurage Aug 9, 2026
e875cb5
Merge branch 'refactor/p3-asyncsync' into refactor/0.10.0-integration
YozoraKurage Aug 9, 2026
8fea524
Merge branch 'refactor/p6-inspector' into refactor/0.10.0-integration
YozoraKurage Aug 9, 2026
30a5654
refactor: move the blend tree node classes into their own files
YozoraKurage Aug 9, 2026
b535113
refactor: use GraphSelectionSet for the view's selection accessors
YozoraKurage Aug 9, 2026
0c7ab5d
refactor: point the drag connect rule at TransitionEnd
YozoraKurage Aug 9, 2026
000e501
Merge branch 'refactor/p5-graph-views' into refactor/0.10.0-integration
YozoraKurage Aug 9, 2026
1f8a24b
fix: missing UnityEditor.Animations using in the split blend tree nodes
YozoraKurage Aug 9, 2026
931df8d
Merge branch 'refactor/p5-graph-views' into refactor/0.10.0-integration
YozoraKurage Aug 9, 2026
1f8d46a
Merge branch 'refactor/0.10.0-integration' into 0.10.0
YozoraKurage Aug 9, 2026
d57746a
refactor: single node-to-end conversion on GraphNodeBase
YozoraKurage Aug 9, 2026
97a93da
feat: localize the remaining inspector literals
YozoraKurage Aug 9, 2026
61cbd3a
refactor: context events and providers for graph visual refreshes
YozoraKurage Aug 9, 2026
7255401
refactor: route inspector visual refreshes through the context
YozoraKurage Aug 9, 2026
d148054
refactor: build the transition pool from context-provided model data
YozoraKurage Aug 9, 2026
eee2a1e
refactor: InspectorPanel takes a GraphSync, not the graph view
YozoraKurage Aug 9, 2026
32d51e8
Merge branch 'followup/transition-end' into 0.10.0
YozoraKurage Aug 9, 2026
e9eef5a
Merge branch 'followup/inspector-events' into 0.10.0
YozoraKurage Aug 9, 2026
76947b1
feat: trig gadgets as 1D lookup trees instead of layers
YozoraKurage Aug 9, 2026
b7a1050
feat: reciprocal without its supporting layer
YozoraKurage Aug 9, 2026
2721119
feat: wizard text for the layer-free trig and reciprocal gadgets
YozoraKurage Aug 9, 2026
fa08631
docs: recipe gadget docs catch up with the layer-free trig and recipr…
YozoraKurage Aug 9, 2026
45cd857
Merge branch 'feat/dbt-lut-gadgets' into 0.10.0
YozoraKurage Aug 9, 2026
60f4d59
feat: remember every DBT gadget's wizard inputs with the controller
YozoraKurage Aug 9, 2026
18feb8b
feat: re-edit, regenerate and delete saved DBT gadgets from the wizard
YozoraKurage Aug 9, 2026
5917dbb
feat: export gadget layers as the c.Gadgets calls that built them
YozoraKurage Aug 9, 2026
217741b
fix: write a Constant LUT tangent as float.PositiveInfinity, not Infi…
YozoraKurage Aug 9, 2026
4e7304f
Merge branch 'feat/aap-gadget-graphdata' into 0.10.0
YozoraKurage Aug 9, 2026
d382967
feat: a Home row in the layer list that takes over the centre pane
YozoraKurage Aug 9, 2026
01535b0
feat: list a controller's gadgets, syncs, recipes and tools on the ho…
YozoraKurage Aug 9, 2026
39d6634
fix: the async sync settings panel was unreachable, its path check of…
YozoraKurage Aug 9, 2026
04f3aa6
feat: lay the home screen out as one centred column of cards
YozoraKurage Aug 9, 2026
799a4ed
feat: split the home screen into two columns and fold the lists away
YozoraKurage Aug 9, 2026
887d1cc
refactor: the clip index moves into a form the home screen can host
YozoraKurage Aug 9, 2026
64a0373
refactor: the analysis report moves into a form the home screen can host
YozoraKurage Aug 9, 2026
2f785aa
refactor: the recipe exporter moves into a form the home screen can host
YozoraKurage Aug 9, 2026
e852c00
feat: the menu editor gets a card of its own, saying why it stays a w…
YozoraKurage Aug 9, 2026
90a5be8
Merge branch 'feat/home-screen' into 0.10.0
YozoraKurage Aug 9, 2026
8c9f20f
Merge branch 'main' into 0.10.0 (resolve 0.9.1 hotfix overlap)
YozoraKurage Aug 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 68 additions & 18 deletions .claude/skills/recipe-refactor/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,48 @@
---
name: recipe-refactor
description: DaerD が自動生成した C# Recipe (.cs) を、コントローラーの挙動を一切変えずに、人間と AI が読みやすい意味的な構造へリファクタリングする。ユーザーが「この Recipe を読みやすくして」「整理して」と言ったとき、または生成 cs の意味的再構成を頼まれたときに使う。
description: DaerD が自動生成した C# Recipe を、コントローラーの挙動を一切変えずに、人間と AI が読みやすい意味的な構造へリファクタリングする。ユーザーが「この Recipe を読みやすくして」「整理して」と言ったとき、生成 cs の意味的再構成を頼まれたとき、または再エクスポートした差分を整形済みの手編集側へ取り込むときに使う。
---

# DaerD Recipe の意味的リファクタリング

自動生成された `Build(ControllerBuilder c)` の呼び出し列を、レイヤーごとのメソッド・
パラメータクラス・データテーブル・ループへ整理する。**出力される AnimatorController が
元と完全に一致すること**が絶対条件で、読みやすさはその次。
自動生成された呼び出し列を、レイヤーごとのメソッド・パラメータクラス・データテーブル・
ループへ整理する。**出力される AnimatorController が元と完全に一致すること**が絶対条件で、
読みやすさはその次。

## どのファイルを触るか

エクスポートは partial class を 2 つに分けて出力する:

| ファイル | 持ち主 | 中身 |
|---|---|---|
| `<Name>.Generated.cs` | DaerD | `[SerializeField]` フィールドと `BuildGenerated(...)`。エクスポートのたびに丸ごと再生成される。**読む専用** — 編集しても次のエクスポートで消える |
| `<Name>.cs` | ユーザー / AI | 実際に実行される `Build(...)`。DaerD は二度と上書きしない。**リファクタはここへ書く** |

初期状態の手編集側は次の 1 行だけ。これを、`BuildGenerated` の呼び出し列を整理したコードで
置き換える:

```csharp
protected override void Build(ControllerBuilder c) => BuildGenerated(c);
```

`BuildGenerated` は消さない・改名しない・手編集側から再定義しない。比較の基準であり、
次のエクスポートで作り直される側だから。

`<Name>.Generated.cs` が存在しない Recipe は分割前の旧レイアウト(1 ファイルに
フィールドと `Build`)。その場合はそのファイルを直接整理するか、先に DaerD から
再エクスポートして 2 分割へ移行する(移行時は旧ファイルが `.cs.bak` に退避される)。

## 進め方

1. まず生成 cs 全体を読み、レイヤー構成と各レイヤーの役割を把握する(ステート名・
パラメーター名・遷移条件から推測する。分からない役割は推測をコメントに書かず、
構造の整理だけに留める)。
1. `<Name>.Generated.cs` を通しで読み、レイヤー構成と各レイヤーの役割を把握する
(ステート名・パラメーター名・遷移条件から推測する。分からない役割は推測をコメントに
書かず、構造の整理だけに留める)。冒頭に API チートシートがある。
2. リファクタ後の骨格を決める: `Build` はレイヤーごとのメソッド呼び出しだけにする。
複数レイヤーから使うパラメーターハンドルは `Params` クラスに集める。
3. 下の不変条件を守りながら書き換える。
4. 最後に検証手順をユーザーに案内する(Generate → Verify)。
3. 下の不変条件を守りながら、手編集側 `<Name>.cs` に書く。
4. 最後に検証手順をユーザーに案内する(Compare → Generate → Verify)。

## 不変条件(破ると挙動が変わる/Verify が乖離を報告する)
## 不変条件(破ると挙動が変わる/Compare・Verify が乖離を報告する)

- **生成順 = Animator の並び順**。以下の順序は元コードと同一に保つ:
- パラメーター宣言の順(パラメーター一覧の順になる)
Expand All @@ -29,13 +52,20 @@ description: DaerD が自動生成した C# Recipe (.cs) を、コントロー
- **同じ遷移元からの遷移の順**(評価優先度そのもの)。`AnyTransitionsTo` /
`EntryTransitionsTo` / `TransitionsTo` / `Exits` を張る順を元と揃える。
遷移元が異なる遷移同士の順序だけは自由に並べ替えてよい
- 1 ステート内のビヘイビアの順、1 遷移内の `When`/`And` 条件の順
- 1 ステート内のビヘイビアの順、1 遷移内の `When`/`And` 条件の順。
ステートマシンに付いた Behaviour(`main.BehaviourJson(...)` / `sub.BehaviourJson(...)`)は
**どのマシンに付いているか**を取り違えないこと。ステートの Behaviour とは別物
- テーブル化するときは行の並び = 元の生成順。並びが不規則でも「元データどおり」と
コメントして保存する
- **`[SerializeField]` フィールド名は変更禁止**(Recipe アセット側の参照キー)。
並べ替え・`[Header]` 追加は自由。
- **`[SerializeField]` フィールドは Generated 側にある**。手編集側で再宣言しない
(二重定義になる)。同じ partial class なので名前でそのまま参照できる。名前の変更も
不可(Recipe アセット側の参照キー)。手編集側で足す独自フィールドは自由。
- **`BehaviourJson` の JSON 文字列は 1 文字も変えない**(`m_ObjectHideFlags` 以外は
厳密比較される)。ヘルパー関数化するときも連結結果が元の文字列と一致すること。
- **後段ステップ(`c.Gadgets(...)` / `c.AsyncSync(...)` / `c.Raw(...)`)は呼び出しの
順序と引数をそのまま保つ**。これらは IR に載らないので Compare も Verify も見逃す。
特にガジェットは宣言順に適用され、後のガジェットが前の出力を入力にできる(並べ替えると
「入力が存在しない」で落ちる)。レイヤー名がそのガジェットレイヤーの同一性そのもの。
- 数値はそのまま保つ。`0.125f` を「約 0.1」に丸めるなどは不可。定数化は値が同一なら可。
- `.Default()`・`WithTransitionToSelf()`・`DrivingLocally()` などの有無を落とさない。
- パラメーターの既定値付き宣言 `c.FloatParameter("X", 1)` と無し宣言は区別を保つ
Expand All @@ -53,12 +83,32 @@ description: DaerD が自動生成した C# Recipe (.cs) を、コントロー
- マジックナンバーは `const` へ(ジェスチャー番号、ブレンド時間、スロット数など)。
- レイヤーごとに `/// <summary>` で役割を 1〜3 行説明。クラス先頭にレイヤー一覧の要約。
- 座標は行データに載せるか、元のまま layout 節に残す。どちらでも良いが混在させない。
- DBT ガジェットのレイヤーが生の巨大ツリーとして出力されている場合は、`c.Gadgets(...)` の
呼び出しへ書き換えられることがある(生成側にその旨のコメントが付く)。ただし**同じツリーに
なる保証は無く Compare も通らない**ので、ユーザーに確認してから行い、Generate 後の
実挙動で確かめる。

## 検証(必ず案内する)

1. Unity で Recipe アセットの **Generate** を実行(対象は複製したコントローラーが安全)。
2. 同アセットの **Verify** を実行し、乖離ゼロを確認する。
3. 乖離が出たら報告内容(レイヤー/ステート/フィールド名)から順序・値の取り違えを探す。
Verify は「宣言した IR」と「実物」の差分をフィールド単位で報告する。
1. Recipe アセットの **「エクスポート側と比較」(Compare)** — `Build` と `BuildGenerated` が
宣言する内容を IR レベルで突き合わせる。コントローラーには一切触らないので最初に回す。
ここが「一致」なら、整形は意味を変えていない(読み方ではなく出来上がる物の比較なので、
ループやテーブルへの書き換えでも通る)。
2. **Generate**(対象は複製したコントローラーが安全)→ **Verify** で実物との乖離ゼロを確認。
3. 後段ステップ(Raw / 巡回同期 / DBT ガジェット)は Compare にも Verify にも出ない。
そこだけは生成側と手編集側を目視で突き合わせる。
4. 乖離が出たら報告内容(レイヤー/ステート/フィールド名)から順序・値の取り違えを探す。

元ファイルのコピーを取る必要は無い — `<Name>.Generated.cs` が常に手つかずの基準として
残る(旧レイアウトの 1 ファイル Recipe を直接整理する場合のみ、事前にコピーを残すこと)。

## 再エクスポートへの追従

コントローラー側を編集して再エクスポートすると、変わるのは `<Name>.Generated.cs` だけで、
整形済みの手編集側はそのまま残る。差分の取り込みはこの順で:

リファクタ前に元ファイルのコピーを残しておくこと(Verify で乖離が出たときの比較用)。
1. `git diff -- <Name>.Generated.cs` を読む(これが「前回エクスポート以降にコントローラーで
何が変わったか」のレポートになる)。
2. 同じ変更を手編集側の構造へ反映する — テーブル化してあれば行の追加・修正で済むことが多い。
構造を作り直す必要は無い。
3. Compare で一致を確認する。
41 changes: 40 additions & 1 deletion Editor/Authoring/AsyncSyncRecipeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ namespace Yozolab.DaerD.Authoring
/// Async Sync (巡回同期) from a recipe, with everything the wizard offers plus the one
/// thing it doesn't: an explicit schedule. Runs after the declared layers are applied;
/// regenerates its own layer in place on every Generate (matched by base name through
/// the saved setup, exactly like the wizard's layer choice).
/// the saved setup, exactly like the wizard's layer choice). Unnamed, it runs under the
/// controller's derived default base name — a flat "Async" would collide with the next
/// distribution that multiplexes on the same avatar.
///
/// c.AsyncSync()
/// .Targets("Hue", "Outfit", "TailState")
Expand All @@ -29,6 +31,11 @@ internal AsyncSyncRecipeBuilder(ControllerBuilder root, string baseName)
_request.store = ParameterStore.Of(controller);
_request.emptyClip = GraphFrameData.GetEmptyClip(controller);

// The controller is only known here, and everything below — the saved setup,
// the layer name, the error message — keys on the base name, so resolve it first.
if (string.IsNullOrEmpty(_request.baseName))
_request.baseName = ResolveDefaultBaseName(controller);

// Regenerate in place when a setup with this base name already owns a layer —
// a recipe must be repeatable without stacking sync layers.
_request.layerIndex = -1;
Expand Down Expand Up @@ -60,11 +67,31 @@ internal AsyncSyncRecipeBuilder(ControllerBuilder root, string baseName)
return warnings;
}
AsyncSyncBuilder.Apply(_request);
// The generated layer is the recipe's: the next Generate rebuilds it, which
// is what the layer list's ownership badge and the panels' "add it in the
// recipe instead" hints are there to say.
string layer = string.IsNullOrEmpty(_request.layerName)
? _request.baseName : _request.layerName;
if (!root.PostLayers.Contains(layer)) root.PostLayers.Add(layer);

warnings.AddRange(AsyncSyncBuilder.Warnings(_request));
return warnings;
});
}

/// <summary>
/// The base name an unnamed <c>AsyncSync()</c> runs under. A setup already generated
/// under the old flat "Async" default keeps it: renaming it on the next Generate would
/// leave the previous layer and its synced parameters behind, unowned. Everything else
/// gets the controller's own default, which no other distribution can collide with.
/// </summary>
static string ResolveDefaultBaseName(AnimatorController controller)
{
foreach (var config in GraphFrameData.GetAsyncSyncs(controller))
if (config.baseName == "Async") return "Async";
return AsyncSyncBuilder.DefaultBaseName(controller);
}

/// <summary>The parameters to multiplex, in cycle order.</summary>
public AsyncSyncRecipeBuilder Targets(params string[] parameters)
{
Expand All @@ -80,6 +107,18 @@ public AsyncSyncRecipeBuilder Rate(string parameter, int timesPerPass)
return this;
}

/// <summary>
/// Accept on-demand sync requests for these targets: each gets a local, unsynced
/// Bool ("base/Req/target"), and the cycle jumps to a requested target's slot at the
/// next step boundary instead of waiting out the pass. Raise the flag from a state's
/// Sync Request (or any Parameter Driver); the send cycle clears it on service.
/// </summary>
public AsyncSyncRecipeBuilder Requestable(params string[] targets)
{
_request.requestTargets.AddRange(targets);
return this;
}

/// <summary>
/// Spell the cycle out step by step — the control the wizard doesn't expose. Every
/// multiplexed parameter must appear at least once and no slot may occupy adjacent
Expand Down
18 changes: 18 additions & 0 deletions Editor/Authoring/BoolParam.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using UnityEditor.Animations;
using UnityEngine;

namespace Yozolab.DaerD.Authoring
{
public sealed class BoolParam : ParamHandle
{
internal BoolParam(ControllerBuilder root, string name) : base(root, name) { }

public Condition IsTrue() => Make(AnimatorConditionMode.If, 0f, "IsTrue()");

public Condition IsFalse() => Make(AnimatorConditionMode.IfNot, 0f, "IsFalse()");

public Condition IsEqualTo(bool value) =>
Make(value ? AnimatorConditionMode.If : AnimatorConditionMode.IfNot, 0f,
$"IsEqualTo({RecipeScript.B(value)})");
}
}
11 changes: 11 additions & 0 deletions Editor/Authoring/BoolParam.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions Editor/Authoring/Condition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using UnityEditor.Animations;
using UnityEngine;

namespace Yozolab.DaerD.Authoring
{
/// <summary>One transition condition, produced by a parameter handle
/// (go.IsTrue(), blend.IsGreaterThan(0.5f)) and consumed by When / And.</summary>
public sealed class Condition
{
internal AnimatorConditionMode Mode;
internal string Parameter;
internal float Threshold;
internal string Source;
}
}
11 changes: 11 additions & 0 deletions Editor/Authoring/Condition.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading