Skip to content

chore: Keep Settings and wizard presentation sources under 500 lines - #1787

Merged
hatayama merged 2 commits into
feature/god-class-split-integrationfrom
feat/split-ui-presentation-windows
Jul 14, 2026
Merged

chore: Keep Settings and wizard presentation sources under 500 lines#1787
hatayama merged 2 commits into
feature/god-class-split-integrationfrom
feat/split-ui-presentation-windows

Conversation

@hatayama

@hatayama hatayama commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • Keep Settings / Setup Wizard / Migration Wizard / Tool Settings presentation sources under the 500-line budget via Fowler Extract Class / Move Method.
  • No intentional end-user behavior change; UI shells stay in EditorWindows while workflow orchestration moves to presenters/controllers.

User Impact

  • Maintainability-only. Users should see the same Settings, Setup Wizard, Migration Wizard, and Tool Settings behavior.

Changes

  • ToolSettings: ListViewController + RowBinder + LayoutSignature (+ characterization tests for signature format)
  • Settings: expand CliSetupPresenter; add SkillsPresenter
  • Setup Wizard: WorkflowController composing CLI/Skills workflow controllers
  • Migration Wizard: WorkflowController for scan/migrate/skill operations
  • Resulting sizes all < 500 (windows ~424–433; extracted helpers 47–461)

Verification

  • uloop compile → 0/0
  • EditMode filter ToolSettings/SetupWizard/MigrationWizard/Settings related → 315 passed
  • dotnet test tests/UnityCliLoop.CodeComplexity.Tests → 12 passed

Merge gate

  • Do not merge until the author confirms manual UI smoke checks (動作チェックOK).

Made with Cursor

Review in cubic

hatayama and others added 2 commits July 14, 2026 20:45
…t signature

Extract Class per Fowler: separate foldout/section shell from ListView ownership,
row VisualElement binding, and the pure rebuild-signature algorithm. Pin the
signature format with characterization tests so list rebuild decisions stay stable.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ines

Move Method / Extract Class: Settings CLI and skills orchestration into presenters;
Setup Wizard CLI/skills workflow into controllers; Migration Wizard operations into
a workflow controller. Windows keep shell/DI/static facades. No intended behavior change.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7558d2e3-172c-4525-89e4-800981ea7b88

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR extracts Setup Wizard, migration-wizard, CLI Settings, and Tool Settings workflow logic into dedicated controllers and presenters. It also adds virtualized tool-list row infrastructure, deterministic layout signatures, expanded-details handling, and characterization tests.

Changes

Setup Wizard and Settings workflows

Layer / File(s) Summary
CLI and skills presenter coordination
Packages/src/Editor/Presentation/UnityCliLoopSettings*Presenter.cs
CLI and skills presenters coordinate asynchronous refresh, installation, path repair, cancellation, install-state tracking, and tool-toggle side effects.
Setup Wizard workflow controllers
Packages/src/Editor/Presentation/Setup/SetupWizard*WorkflowController.cs
Setup Wizard CLI and skills workflows are separated from the window and coordinated through refresh, installation, checking, and cancellation APIs.
Window wiring and lifecycle
Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs, Packages/src/Editor/Presentation/UnityCliLoopSettingsWindow.cs
Window bindings, refresh scheduling, teardown, and tool-toggle callbacks delegate to the new controllers and presenters.

Third-party migration workflow

Layer / File(s) Summary
Migration controller operations
Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardWorkflowController.cs
Migration preview, application, progress reporting, skill toggling, cancellation, main-thread completion, and UI state updates are implemented in a dedicated controller.
Migration window integration
Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardWindow.cs
The migration window constructs the controller, routes callbacks through it, and delegates refresh and cancellation lifecycle operations.

Tool Settings list rendering

Layer / File(s) Summary
Tool row data and layout contract
Packages/src/Editor/Presentation/UIToolkit/Components/ToolListRowData.cs, Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionLayoutSignature.cs, Assets/Tests/Editor/ToolSettingsSectionLayoutSignatureTests.cs
Row models and deterministic group signatures define list structure, with tests covering populated and empty groups.
Virtualized list controller
Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionListViewController.cs
List rebuilding, incremental toggle updates, details-row expansion, height calculation, visibility, and refresh behavior are centralized.
Row binding and section integration
Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionRowBinder.cs, Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSection.cs
Row interactions and UI binding are extracted, while the section delegates list state and updates to the controller.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SetupWizardWindow
  participant SetupWizardWorkflowController
  participant CliWorkflowController
  participant SkillsWorkflowController
  SetupWizardWindow->>SetupWizardWorkflowController: Bind UI and start refresh
  SetupWizardWorkflowController->>CliWorkflowController: Refresh CLI state
  SetupWizardWorkflowController->>SkillsWorkflowController: Refresh skills state
  CliWorkflowController-->>SetupWizardWorkflowController: Return CLI installed/path state
  SkillsWorkflowController-->>SetupWizardWorkflowController: Return target/install state
  SetupWizardWorkflowController-->>SetupWizardWindow: Update UI and resize
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main refactor goal of keeping Settings and wizard presentation sources under 500 lines.
Description check ✅ Passed The description matches the changeset and accurately summarizes the maintainability-focused controller/presenter extraction work.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/split-ui-presentation-windows

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hatayama

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@hatayama
hatayama merged commit 2552640 into feature/god-class-split-integration Jul 14, 2026
1 of 2 checks passed
@hatayama
hatayama deleted the feat/split-ui-presentation-windows branch July 14, 2026 12:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Packages/src/Editor/Presentation/UnityCliLoopSettingsWindow.cs (1)

352-359: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Capture _skillsPresenter before scheduling the delayed callback
OnDisable/OnDestroy clear _skillsPresenter, so the deferred lambda can hit a null field if the window closes before delayCall runs. Capture the presenter into a local before registering the callback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/src/Editor/Presentation/UnityCliLoopSettingsWindow.cs` around lines
352 - 359, Update HandleToolToggled to copy _skillsPresenter into a local
variable before registering the EditorApplication.delayCall callback, then have
the deferred lambda invoke ApplyToolToggleSideEffects on that captured presenter
instead of the mutable field. Preserve the existing deferred execution and
Forget behavior.
🧹 Nitpick comments (4)
Assets/Tests/Editor/ToolSettingsSectionLayoutSignatureTests.cs (1)

10-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test pinning the IsEnabled-invariant.

Consider a third test asserting that toggling IsEnabled on an item does NOT change the signature. This is the exact invariant ToolSettingsSectionListViewController.UpdateToolList relies on to decide "incremental toggle update" vs "full rebuild" — worth locking down explicitly since it's a critical/easy-to-break characterization test target.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Assets/Tests/Editor/ToolSettingsSectionLayoutSignatureTests.cs` around lines
10 - 48, Add a third test in ToolSettingsSectionLayoutSignatureTests that
creates equivalent data with an item’s IsEnabled value toggled and asserts
ToolSettingsSectionLayoutSignature.Create returns the same signature. Cover the
relevant tool group and preserve the existing signature fields, explicitly
locking the invariant used by
ToolSettingsSectionListViewController.UpdateToolList.
Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionListViewController.cs (1)

41-61: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Redundant full refresh after a direct toggle update.

UpdateSingleToggle sets toggle.SetValueWithoutNotify(enabled) directly, then immediately calls RefreshToolListView(), which clears _togglesByToolName and calls RefreshItems() — forcing all visible rows to unbind/rebind, which re-applies the same SetValueWithoutNotify(item.IsEnabled) via BindToolListRowElement. The manual direct-set on lines 55-58 is dead work; every single toggle click now pays for a full visible-row rebind it doesn't need.

♻️ Proposed fix
         public void UpdateSingleToggle(string toolName, bool enabled)
         {
             for (int i = 0; i < _toolListRows.Count; i++)
             {
                 ToolListRowData row = _toolListRows[i];
                 if (!row.IsTool || row.ToolName != toolName)
                 {
                     continue;
                 }

                 row.IsEnabled = enabled;
                 break;
             }

-            if (_togglesByToolName.TryGetValue(toolName, out Toggle toggle))
-            {
-                toggle.SetValueWithoutNotify(enabled);
-            }
-
-            RefreshToolListView();
+            if (_togglesByToolName.TryGetValue(toolName, out Toggle toggle))
+            {
+                toggle.SetValueWithoutNotify(enabled);
+            }
         }

Please confirm via Unity's ListView.RefreshItems() docs/behavior that visible items are indeed unbound/rebound on every call (as assumed here) before dropping the refresh.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionListViewController.cs`
around lines 41 - 61, Confirm Unity ListView.RefreshItems() behavior for the
current UI Toolkit version, specifically whether it unbinds and rebinds visible
rows on every call. If confirmed, remove the redundant RefreshToolListView()
call from UpdateSingleToggle while preserving the row-model update and direct
Toggle.SetValueWithoutNotify update; otherwise retain the refresh and adjust
only if needed based on the documented behavior.
Packages/src/Editor/Presentation/UnityCliLoopSettingsCliSetupPresenter.cs (1)

201-229: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate CLI-path-setup refresh logic.

RefreshCliPathSetupInBackground and RefreshCliPathSetupAsync implement the same shell-visibility check and _needsCliPathSetup assignment. Have the former delegate to the latter to avoid the two implementations drifting apart.

♻️ Proposed refactor
 internal async Task RefreshCliPathSetupInBackground()
 {
     if (_isRefreshingCliPathSetup)
     {
         return;
     }

-    if (!ShouldCheckCliPathSetup())
-    {
-        _needsCliPathSetup = false;
-        return;
-    }
-
     _isRefreshingCliPathSetup = true;
     RefreshSection();

     try
     {
-        bool isCliVisibleFromShell = await _cliSetupApplicationService.IsCliVisibleFromShellAsync(
-            UnityEngine.Application.platform,
-            CancellationToken.None);
-        _needsCliPathSetup = !isCliVisibleFromShell;
+        await RefreshCliPathSetupAsync(CancellationToken.None);
     }
     finally
     {
         _isRefreshingCliPathSetup = false;
         RefreshSection();
     }
 }

Also applies to: 335-347

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/src/Editor/Presentation/UnityCliLoopSettingsCliSetupPresenter.cs`
around lines 201 - 229, Update RefreshCliPathSetupInBackground to delegate the
CLI visibility check and _needsCliPathSetup update to RefreshCliPathSetupAsync
instead of duplicating that logic. Preserve the existing refresh-state guard and
UI refresh behavior, while ensuring the background method awaits the shared
implementation.
Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs (1)

87-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

New workflow controllers depend back on SetupWizardWindow for policy decisions and a policy constant. All five sites share one root cause: ShouldRepairCliPathFromPrimaryButton, ShouldCheckCliPathSetupForSetupWizard, CanManageSkills, ShouldUseFirstInstallSkillsUi, and ForceFlatSkillInstall are internal static members on the View (SetupWizardWindow), so the newly-extracted controllers must call back into the Window they're meant to be decoupled from — a dependency direction that didn't exist before this extraction, since the logic previously lived inside the Window itself. CanManageSkills is a pure identity function (return cliInstalled;), so two of the call sites can simply be inlined.

  • Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs#L87-L101: call CliSetupPrimaryActionPolicy.ShouldRepairCliPath(...) directly instead of SetupWizardWindow.ShouldRepairCliPathFromPrimaryButton(...).
  • Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs#L193-L208: call CliPathSetupCheckPolicy.ShouldCheck(...) directly instead of SetupWizardWindow.ShouldCheckCliPathSetupForSetupWizard(...).
  • Packages/src/Editor/Presentation/Setup/SetupWizardSkillsWorkflowController.cs#L128-L147: drop SetupWizardWindow.CanManageSkills(cliInstalled) and use cliInstalled directly at both call sites.
  • Packages/src/Editor/Presentation/Setup/SetupWizardSkillsWorkflowController.cs#L161-L165: inline the string.IsNullOrEmpty(lastSeenSetupWizardVersionBeforeOpen) check instead of calling SetupWizardWindow.ShouldUseFirstInstallSkillsUi(...).
  • Packages/src/Editor/Presentation/Setup/SetupWizardSkillsWorkflowController.cs#L278-L283: move the ForceFlatSkillInstall constant to a single shared location (e.g. a Domain/Policy class) instead of independently declaring/reading it from both SetupWizardWindow.cs and UnityCliLoopSettingsSkillsPresenter.cs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs`
around lines 87 - 101, Remove the extracted controllers’ policy dependencies on
SetupWizardWindow: in
Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs#L87-L101
call CliSetupPrimaryActionPolicy.ShouldRepairCli directly, and at `#L193-L208`
call CliPathSetupCheckPolicy.ShouldCheck directly; in
Packages/src/Editor/Presentation/Setup/SetupWizardSkillsWorkflowController.cs#L128-L147
use cliInstalled directly, at `#L161-L165` inline the
string.IsNullOrEmpty(lastSeenSetupWizardVersionBeforeOpen) check, and at
`#L278-L283` move ForceFlatSkillInstall to one shared Domain/Policy location used
by both SetupWizardWindow.cs and UnityCliLoopSettingsSkillsPresenter.cs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Packages/src/Editor/Presentation/Setup/SetupWizardWorkflowController.cs`:
- Around line 131-172: The RefreshUI flow allows ScheduleInitialRefresh and the
Refresh button to run RefreshUIAsync concurrently. Add an in-flight guard or
replaceable cancellation source used by RefreshUI, cancel or ignore any existing
refresh before starting a new one, and pass that cancellation token into
RefreshUIAsync instead of always using CancellationToken.None; ensure the guard
is cleared when the refresh completes.

In
`@Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardWorkflowController.cs`:
- Around line 138-146: Remove the controller’s dependency on
ThirdPartyToolMigrationWizardWindow by replacing its calls to
ConfirmMigrationApply, ShouldFinishMigrationOnMainThread,
ShouldRefreshAfterInterruptedMigration, ShouldRefreshAfterMigration, and
ShouldRemoveMigrationSkill with ThirdPartyToolMigrationWizardStateRules calls.
Move the inline ConfirmMigrationApply and ShouldRemoveMigrationSkill decision
logic into the rules class first if needed, preserving existing behavior and
signatures at the controller call sites.

In `@Packages/src/Editor/Presentation/UIToolkit/Components/ToolListRowData.cs`:
- Around line 15-16: Remove the UI back-reference and route details toggling
through a callback. In
Packages/src/Editor/Presentation/UIToolkit/Components/ToolListRowData.cs#L15-L16
remove Owner; in
Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionListViewController.cs#L26-L39
remove the owner constructor parameter and pass ToggleToolDetailsForTool as the
Action<string> callback; in
Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionRowBinder.cs#L79-L94
replace _owner and item.Owner usage with a non-null _onDetailsToggled invocation
and remove the assignment; in
Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSection.cs#L37-L41
stop passing this to the controller.

---

Outside diff comments:
In `@Packages/src/Editor/Presentation/UnityCliLoopSettingsWindow.cs`:
- Around line 352-359: Update HandleToolToggled to copy _skillsPresenter into a
local variable before registering the EditorApplication.delayCall callback, then
have the deferred lambda invoke ApplyToolToggleSideEffects on that captured
presenter instead of the mutable field. Preserve the existing deferred execution
and Forget behavior.

---

Nitpick comments:
In `@Assets/Tests/Editor/ToolSettingsSectionLayoutSignatureTests.cs`:
- Around line 10-48: Add a third test in ToolSettingsSectionLayoutSignatureTests
that creates equivalent data with an item’s IsEnabled value toggled and asserts
ToolSettingsSectionLayoutSignature.Create returns the same signature. Cover the
relevant tool group and preserve the existing signature fields, explicitly
locking the invariant used by
ToolSettingsSectionListViewController.UpdateToolList.

In `@Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs`:
- Around line 87-101: Remove the extracted controllers’ policy dependencies on
SetupWizardWindow: in
Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs#L87-L101
call CliSetupPrimaryActionPolicy.ShouldRepairCli directly, and at `#L193-L208`
call CliPathSetupCheckPolicy.ShouldCheck directly; in
Packages/src/Editor/Presentation/Setup/SetupWizardSkillsWorkflowController.cs#L128-L147
use cliInstalled directly, at `#L161-L165` inline the
string.IsNullOrEmpty(lastSeenSetupWizardVersionBeforeOpen) check, and at
`#L278-L283` move ForceFlatSkillInstall to one shared Domain/Policy location used
by both SetupWizardWindow.cs and UnityCliLoopSettingsSkillsPresenter.cs.

In
`@Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionListViewController.cs`:
- Around line 41-61: Confirm Unity ListView.RefreshItems() behavior for the
current UI Toolkit version, specifically whether it unbinds and rebinds visible
rows on every call. If confirmed, remove the redundant RefreshToolListView()
call from UpdateSingleToggle while preserving the row-model update and direct
Toggle.SetValueWithoutNotify update; otherwise retain the refresh and adjust
only if needed based on the documented behavior.

In `@Packages/src/Editor/Presentation/UnityCliLoopSettingsCliSetupPresenter.cs`:
- Around line 201-229: Update RefreshCliPathSetupInBackground to delegate the
CLI visibility check and _needsCliPathSetup update to RefreshCliPathSetupAsync
instead of duplicating that logic. Preserve the existing refresh-state guard and
UI refresh behavior, while ensuring the background method awaits the shared
implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8f05b6d3-1c4d-469e-aac7-7cf37561a592

📥 Commits

Reviewing files that changed from the base of the PR and between 1eafc03 and 3e430e2.

⛔ Files ignored due to path filters (10)
  • Assets/Tests/Editor/ToolSettingsSectionLayoutSignatureTests.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Presentation/Setup/SetupWizardSkillsWorkflowController.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Presentation/Setup/SetupWizardWorkflowController.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardWorkflowController.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolListRowData.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionLayoutSignature.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionListViewController.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionRowBinder.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Presentation/UnityCliLoopSettingsSkillsPresenter.cs.meta is excluded by none and included by none
📒 Files selected for processing (15)
  • Assets/Tests/Editor/ToolSettingsSectionLayoutSignatureTests.cs
  • Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs
  • Packages/src/Editor/Presentation/Setup/SetupWizardSkillsWorkflowController.cs
  • Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs
  • Packages/src/Editor/Presentation/Setup/SetupWizardWorkflowController.cs
  • Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardWindow.cs
  • Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardWorkflowController.cs
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolListRowData.cs
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSection.cs
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionLayoutSignature.cs
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionListViewController.cs
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionRowBinder.cs
  • Packages/src/Editor/Presentation/UnityCliLoopSettingsCliSetupPresenter.cs
  • Packages/src/Editor/Presentation/UnityCliLoopSettingsSkillsPresenter.cs
  • Packages/src/Editor/Presentation/UnityCliLoopSettingsWindow.cs

Comment on lines +131 to +172
internal void RefreshUI(bool refreshSkillsSection = true)
{
RefreshUIAsync(refreshSkillsSection, CancellationToken.None).Forget();
}

private void RefreshAutoShowToggle()
{
_suppressAutoShowToggle.SetValueWithoutNotify(_editorSettingsPort.GetSuppressSetupWizardAutoShow());
}

private async Task RefreshUIAsync(
bool refreshSkillsSection,
CancellationToken ct)
{
CancelSkillInstallStateRefresh();
ct.ThrowIfCancellationRequested();
RefreshAutoShowToggle();
ViewDataBinder.SetVisible(_nodejsWarning, false);
ViewDataBinder.SetVisible(_nodejsOk, false);
_cliWorkflow.ShowChecking();
if (refreshSkillsSection)
{
_skillsWorkflow.ShowChecking();
}

await Task.Yield();
ct.ThrowIfCancellationRequested();

ViewDataBinder.SetVisible(_nodejsWarning, false);
ViewDataBinder.SetVisible(_nodejsOk, false);

bool cliInstalled = await _cliWorkflow.RefreshAndUpdateAsync(ct);

if (!refreshSkillsSection)
{
_scheduleResizeToContent();
return;
}

_skillsWorkflow.ApplyFastSkillsState(cliInstalled);
_scheduleResizeToContent();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant files and inspect the surrounding code.
git ls-files 'Packages/src/Editor/Presentation/Setup/*' 'Packages/src/Editor/Presentation/*Settings*' 'Packages/src/Editor/Presentation/*' | sed -n '1,200p'

echo
echo '--- ast-grep outline: SetupWizardWorkflowController.cs ---'
ast-grep outline Packages/src/Editor/Presentation/Setup/SetupWizardWorkflowController.cs --view expanded || true

echo
echo '--- ast-grep outline: UnityCliLoopSettingsCliSetupPresenter ---'
fd -a 'UnityCliLoopSettingsCliSetupPresenter.*' Packages/src || true

echo
echo '--- rg references for RefreshUI / HandleRefreshCliVersion / _isRefreshingVersion ---'
rg -n "_isRefreshingVersion|HandleRefreshCliVersion|RefreshUIAsync|ScheduleInitialRefresh|ForceRefreshCliVersionAsync|CancelSkillInstallStateRefresh" Packages/src/Editor -S

Repository: hatayama/unity-cli-loop

Length of output: 14641


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the relevant code slices around the methods and any refresh guards.
for f in \
  Packages/src/Editor/Presentation/Setup/SetupWizardWorkflowController.cs \
  Packages/src/Editor/Presentation/Setup/UnityCliLoopSettingsCliSetupPresenter.cs
do
  if [ -f "$f" ]; then
    echo "=== $f ==="
    wc -l "$f"
    echo
  fi
done

echo '--- SetupWizardWorkflowController.cs (lines 1-260) ---'
sed -n '1,260p' Packages/src/Editor/Presentation/Setup/SetupWizardWorkflowController.cs

echo
echo '--- UnityCliLoopSettingsCliSetupPresenter.cs (relevant sections) ---'
sed -n '1,260p' Packages/src/Editor/Presentation/Setup/UnityCliLoopSettingsCliSetupPresenter.cs

Repository: hatayama/unity-cli-loop

Length of output: 7392


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find call sites for the controller refresh path and the initial refresh scheduling.
rg -n "RefreshUI\\(|RefreshUIAsync\\(|ScheduleInitialRefresh\\(|RefreshUI\\b" Packages/src/Editor/Presentation -S

echo
echo '--- controller file around refresh and scheduling methods ---'
sed -n '1,360p' Packages/src/Editor/Presentation/Setup/SetupWizardWorkflowController.cs

Repository: hatayama/unity-cli-loop

Length of output: 9974


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- SetupWizardCliWorkflowController.cs ---'
wc -l Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs
sed -n '1,260p' Packages/src/Editor/Presentation/Setup/SetupWizardCliWorkflowController.cs

echo
echo '--- SetupWizardSkillsWorkflowController.cs ---'
wc -l Packages/src/Editor/Presentation/Setup/SetupWizardSkillsWorkflowController.cs
sed -n '1,280p' Packages/src/Editor/Presentation/Setup/SetupWizardSkillsWorkflowController.cs

Repository: hatayama/unity-cli-loop

Length of output: 20760


Serialize RefreshUI ScheduleInitialRefresh() and the Refresh button can overlap, and RefreshUIAsync has no in-flight guard. That lets two refreshes race on _cliWorkflow / _skillsWorkflow and call ForceRefreshCliVersionAsync in parallel. RefreshUI also always passes CancellationToken.None, so the cancellation checks never suppress a prior run. Use a simple _isRefreshing flag or CTS swap to ignore/cancel the previous refresh before starting a new one.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/src/Editor/Presentation/Setup/SetupWizardWorkflowController.cs`
around lines 131 - 172, The RefreshUI flow allows ScheduleInitialRefresh and the
Refresh button to run RefreshUIAsync concurrently. Add an in-flight guard or
replaceable cancellation source used by RefreshUI, cancel or ignore any existing
refresh before starting a new one, and pass that cancellation token into
RefreshUIAsync instead of always using CancellationToken.None; ensure the guard
is cleared when the refresh completes.

Comment on lines +138 to +146
internal async Task HandleMigrateThirdPartyTools()
{
if (!ThirdPartyToolMigrationWizardWindow.ConfirmMigrationApply(
_pendingMigrationFilePaths.Length,
(title, message, ok, cancel) => EditorUtility.DisplayDialog(title, message, ok, cancel)))
{
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Controller depends back on the EditorWindow for pure decision logic.

ThirdPartyToolMigrationWizardWorkflowController calls ThirdPartyToolMigrationWizardWindow.ConfirmMigrationApply (140-142), .ShouldFinishMigrationOnMainThread (160-165, 168-173), .ShouldRefreshAfterInterruptedMigration (206-208), .ShouldRefreshAfterMigration (221), and .ShouldRemoveMigrationSkill (302-303). This inverts the dependency direction this refactor is meant to establish: the extracted controller now depends on the concrete EditorWindow subclass it was pulled out of, rather than the window depending on the controller. Most of these Window methods already just forward to ThirdPartyToolMigrationWizardStateRules, so the controller could call the rules class directly; ConfirmMigrationApply/ShouldRemoveMigrationSkill (implemented inline on the Window) would need to move to the rules class or the controller itself.

♻️ Example direction for the fix
-            bool shouldRemoveMigrationSkill =
-                ThirdPartyToolMigrationWizardWindow.ShouldRemoveMigrationSkill(currentInstallState);
+            bool shouldRemoveMigrationSkill =
+                ThirdPartyToolMigrationWizardStateRules.ShouldRemoveMigrationSkill(currentInstallState);

(and similarly route the other four call sites through ThirdPartyToolMigrationWizardStateRules, moving ConfirmMigrationApply/ShouldRemoveMigrationSkill there if they aren't already present.)

Also applies to: 160-173, 206-208, 221-221, 292-303

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardWorkflowController.cs`
around lines 138 - 146, Remove the controller’s dependency on
ThirdPartyToolMigrationWizardWindow by replacing its calls to
ConfirmMigrationApply, ShouldFinishMigrationOnMainThread,
ShouldRefreshAfterInterruptedMigration, ShouldRefreshAfterMigration, and
ShouldRemoveMigrationSkill with ThirdPartyToolMigrationWizardStateRules calls.
Move the inline ConfirmMigrationApply and ShouldRemoveMigrationSkill decision
logic into the rules class first if needed, preserving existing behavior and
signatures at the controller call sites.

Comment on lines +15 to +16
public bool IsEnabled;
public ToolSettingsSection Owner;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Row model shouldn't carry a UI-class back-reference — route details-toggle through a callback instead. ToolListRowData.Owner exists only so the details button can call back into ToolSettingsSection, which just forwards to the controller that already created the row binder. This produces an unnecessary 3-hop chain (RowBinder → row.Owner → ToolSettingsSection → controller) when the controller could hand the RowBinder a plain Action<string> callback wired to its own ToggleToolDetailsForTool, exactly like the existing onToolToggled callback.

  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolListRowData.cs#L15-L16: remove the Owner field entirely.
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionListViewController.cs#L26-L39: drop the owner ctor parameter; pass ToggleToolDetailsForTool as an Action<string> to the row binder instead.
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionRowBinder.cs#L79-L94: replace the owner/_owner field and item.Owner?.ToggleToolDetailsForTool(...) call with a stored Action<string> _onDetailsToggled invoked directly (no null-conditional, consistent with this file's Debug.Assert fail-fast convention); also drop the item.Owner = _owner; assignment in BindToolListRowElement.
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSection.cs#L37-L41: stop passing this into the controller constructor once the owner parameter is removed.
📍 Affects 4 files
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolListRowData.cs#L15-L16 (this comment)
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionListViewController.cs#L26-L39
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionRowBinder.cs#L79-L94
  • Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSection.cs#L37-L41
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/src/Editor/Presentation/UIToolkit/Components/ToolListRowData.cs`
around lines 15 - 16, Remove the UI back-reference and route details toggling
through a callback. In
Packages/src/Editor/Presentation/UIToolkit/Components/ToolListRowData.cs#L15-L16
remove Owner; in
Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionListViewController.cs#L26-L39
remove the owner constructor parameter and pass ToggleToolDetailsForTool as the
Action<string> callback; in
Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSectionRowBinder.cs#L79-L94
replace _owner and item.Owner usage with a non-null _onDetailsToggled invocation
and remove the assignment; in
Packages/src/Editor/Presentation/UIToolkit/Components/ToolSettingsSection.cs#L37-L41
stop passing this to the controller.

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.

1 participant