Skip to content

Draw the macOS Azure tab as the scope tree it already describes - #198

Merged
FrodeHus merged 1 commit into
mainfrom
macos/azure-scope-tree-view
Sep 19, 2026
Merged

FrodeHus merged 1 commit into
mainfrom
macos/azure-scope-tree-view

Conversation

@FrodeHus

@FrodeHus FrodeHus commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Closes #194. The SwiftUI half of #186; the logic and its tests landed in #195.

The Azure tab listed its eligibilities flat, which does not scale: a platform engineer eligible for Contributor on sixty subscriptions read sixty sibling rows and clicked sixty times before the single activation Elevate promised.

The model

AppModel gains the tree and the state around it, mirroring AppModel.Panel.cs:

  • azureTree(for:) builds from the already-filtered rows, so a search narrows the tree instead of sitting beside it.
  • collapsedScopes is keyed per tenant — two accounts can reach the same scope — in memory, and scopes start open.
  • isScopeCollapsed answers false while filtering, so a match is never hidden behind a node closed earlier.
  • subtreeKeys / subtreeState / toggleSubtree, where a partly chosen subtree fills up rather than emptying: the checkbox is offering the rest.
  • canSelect is ported deliberately and is stricter than canActivate. The "is this row's checkbox enabled" rule lived in the view; without it a scope checkbox would select rows whose own checkbox is off.

The view

TenantRoles walks ScopeTree.flatten on the Azure tab and emits a ScopeRow or a RoleRow per entry. The two foldings the Core already performs are what keep the tree from costing more rows than it saves, so the view only has to respect them: a pass-through scope is drawn as a dimmed Alpha / ahead of the title it was folded into, and a scope leading to a single role gets no header at all.

Indent is 12 pt a step and stops after three, as on Windows — the panel is 380 pt wide, a management group path is longer than that, and the header's own path says where you are.

Management groups sit beside the subscriptions rather than above them, for the reason ArmScope documents: ARM writes a management group scope as its own flat path and never repeats it in a subscription's.

Verification

Built and tested on a Mac (Swift 6.4 / Xcode 26):

  • 444 ElevateCore tests and 158 ElevateApp tests pass; the app builds with no new warnings.
  • The twelve new AppModelScopeTreeTests mirror the C# AppModelScopeTreeTests case for case, and were watched failing before the accessors existed.
  • The panel was rendered offscreen in light and dark, with and without select mode, to check the folding, the elision and the three-state checkbox against realistic scopes — a management group beside two subscriptions, nested resource groups, a resource, a single-role subscription and a pass-through.

Worth a reviewer's judgment

A folded ancestor with no caption to borrow shows its raw subscription id, so a pass-through header can read 33333333-3333-… / sandbox-rg. That is what the data says — no eligibility sits on that subscription, so nothing named it — and it matches Windows. Truncating it differently would be a Core change and a divergence.

Separately, and not from this branch: a full local ElevateApp run can block indefinitely on an unanswered login-keychain prompt, which looks exactly like a hang in whatever is under test — the blamed test differs run to run and each passes alone. TestModel.swift:40 documents the path: ownAppViaLoopback is false by default, so a test whose settings pick up a client id reconciles own-app accounts against the real login keychain. Keeping the suite off the real keychain would be worth doing on its own.

Co-authored-by: Claude Opus 5 noreply@anthropic.com

The Azure tab listed its eligibilities flat, which does not scale: a platform
engineer eligible for Contributor on sixty subscriptions read sixty sibling
rows and clicked sixty times before the single activation Elevate promised.
Windows and the CLI got the tree in #186; the logic and its tests came across
to ElevateCore in #195. This is the SwiftUI layer that draws it.

AppModel gains the Azure tab's tree and the state around it, mirroring
AppModel.Panel.cs: azureTree(for:) builds from the already-filtered rows, so a
search narrows the tree rather than sitting beside it; collapsedScopes is keyed
per tenant, because two accounts can reach the same scope; and isScopeCollapsed
answers false while filtering, so a match is never hidden behind a node closed
earlier. The subtree checkbox carries its own canSelect, stricter than
canActivate: the rule for "is this row's checkbox enabled" lived in the view,
and without it a scope checkbox would select rows whose own checkbox is off. A
partly chosen subtree fills up rather than emptying — the checkbox is offering
the rest.

TenantRoles walks ScopeTree.flatten on the Azure tab and emits a ScopeRow or a
RoleRow per entry. The two foldings the Core already performs are what keep the
tree from costing more rows than it saves, so the view only has to respect
them: a scope that just passes through is drawn as a dimmed "Alpha /" ahead of
the title it was folded into, and a scope leading to a single role gets no
header at all. Indent is 12 pt a step and stops after three; the panel is 380 pt
wide and a management group path is longer than that, and the header's own path
says where you are. Management groups sit beside the subscriptions, not above
them, for the reason ArmScope documents.

Verified on a Mac: 444 ElevateCore tests and 158 ElevateApp tests pass, and the
app builds with no new warnings. The twelve new AppModelScopeTreeTests mirror
the C# suite case for case and were watched failing first. The panel was also
rendered offscreen in light and dark, with and without select mode, to check
the folding, the elision and the three-state checkbox against real scopes.

Closes #194

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@FrodeHus
FrodeHus enabled auto-merge (squash) September 19, 2026 12:23
@FrodeHus
FrodeHus merged commit cb3cf54 into main Sep 19, 2026
13 checks passed
@FrodeHus
FrodeHus deleted the macos/azure-scope-tree-view branch September 19, 2026 12:26
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.

macOS: draw the Azure tab's scope tree

1 participant