Skip to content

Compute visualizer graph layout in the language server (MSAGL)#19884

Merged
shenglol merged 3 commits into
mainfrom
shenglol/layout-engine-3
Jun 18, 2026
Merged

Compute visualizer graph layout in the language server (MSAGL)#19884
shenglol merged 3 commits into
mainfrom
shenglol/layout-engine-3

Conversation

@shenglol

@shenglol shenglol commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Description

Computes the Bicep visualizer's graph layout in the language server with MSAGL instead of in the webview with ELK, behind the off-by-default bicep.visualizer.serverLayout.enabled flag. This covers Phase 5 (server-side layout engine, shadow mode) and Phase 6 (applying server layout in the webview) of the visualizer MSAGL layout migration. Default behavior is unchanged while the flag is off.

The two commits map to the two phases:

  • Add server-side MSAGL visual graph layout engineIVisualGraphLayoutEngine / MsaglVisualGraphLayoutEngine run a layered (Sugiyama) top-to-bottom layout. Nested module scopes are laid out independently and composed into parent boxes (MSAGL clusters proved unreliable at runtime). Shared defaults live in VisualGraphLayoutOptions.Default. Engine + differ unit tests included, with a performance smoke test.
  • Apply server-driven visual graph layout in the webview — splits the protocol into a topology/metadata update (textDocument/visualGraphUpdate) and a measured layout request (textDocument/visualGraphLayout) that carries client-measured node sizes; applies server positions in the webview and gates the legacy ELK auto-layout behind serverLayoutActiveAtom.

Notable details for reviewers:

  • Layout invalidation is derived on the client by comparing updateNode field values (not presence), so range-only edits (e.g. deleting a blank line) never reflow. The rule is centralized in layout-invalidation.ts.
  • Animations preserved: nodes spring to their server positions (motion), and the layout fit uses the same bounds as the Fit View button, so re-layout / add / remove animate and the two controls agree to the pixel. Measured node sizes are rounded to integers so module boxes stay stable and re-layout is idempotent.
  • Single in-flight request loop with a dirty flag; Reset Layout is routed through the same slot so it cannot race a document-change update.
  • The server stays stateless per request, validating each measured layout request against the live compilation. Rationale and the full message flow are documented in src/vscode-bicep-ui/apps/visual-designer/visual-graph-protocol.md.

Example Usage

Enable the flag and open the visualizer:

"bicep.visualizer.serverLayout.enabled": true

Then run Open Bicep Visualizer on a .bicep file with resources/modules. Editing the file animates incremental layout changes; the Reset Layout button re-runs the server layout. With the flag off (default), the existing ELK path is used and behavior is unchanged.

Checklist

Microsoft Reviewers: Open in CodeFlow

shenglol added 2 commits June 15, 2026 21:54
Phase 5 of the visualizer MSAGL layout migration: compute graph layout in the Bicep language server instead of the webview.

- Add Msagl to central package management and reference it from Bicep.LangServer.

- Introduce IVisualGraphLayoutEngine / MsaglVisualGraphLayoutEngine running a layered (Sugiyama) top-to-bottom layout via LayoutHelpers.CalculateLayout, with straight-line routing and y-flipped, origin-normalized positions.

- Lay out nested module scopes independently and compose them into parent module boxes (MSAGL clusters proved unreliable at runtime).

- Keep shared defaults in VisualGraphLayoutOptions.Default so future callers (e.g. CLI image generation) do not duplicate React constants.

- Register the engine in DI and add engine + differ unit tests, including a performance smoke test.

- Update the migration plan (Phase 5).
Phase 6 of the visualizer MSAGL layout migration: split the protocol into a topology/metadata update and a measured layout request, then apply the server's MSAGL positions in the React webview behind the bicep.visualizer.serverLayout.enabled flag.

- Add textDocument/visualGraphLayout (and the getGraphUpdate/getGraphLayout webview bridge); the graph update is now topology/metadata only, and the layout request carries client-measured node sizes.

- Apply server positions in the webview and gate the legacy ELK auto-layout behind serverLayoutActiveAtom; route Reset Layout through the single in-flight slot so it cannot race a document-change update.

- Derive layout invalidation by comparing updateNode field values (not presence) so range-only edits never reflow; centralize the rule in layout-invalidation.ts next to the rendered-graph equality check.

- Spring nodes to their server positions via motion and fit the viewport to the same bounds as Fit View, so re-layout, additions, and removals animate and the two controls agree to the pixel; round measured node sizes to integers so module boxes stay stable and re-layout is idempotent.

- Add Vitest to the visual-designer app with layout-invalidation unit tests; add visual-graph-protocol.md and update the migration plan (Phase 6 + known follow-ups).
Resolves import-ordering conflicts in fake-message-channel.ts, use-graph-update.ts, and view.ts (both sides added overlapping visual-graph imports); reconciled via prettier import sort.
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Test this change out locally with the following install scripts (Action run 27788009723)

VSCode
  • Mac/Linux
    bash <(curl -Ls https://aka.ms/bicep/nightly-vsix.sh) --run-id 27788009723
  • Windows
    iex "& { $(irm https://aka.ms/bicep/nightly-vsix.ps1) } -RunId 27788009723"
Azure CLI
  • Mac/Linux
    bash <(curl -Ls https://aka.ms/bicep/nightly-cli.sh) --run-id 27788009723
  • Windows
    iex "& { $(irm https://aka.ms/bicep/nightly-cli.ps1) } -RunId 27788009723"

@shenglol shenglol merged commit 76e87ce into main Jun 18, 2026
41 checks passed
@shenglol shenglol deleted the shenglol/layout-engine-3 branch June 18, 2026 20:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants