The pack hosts two Monaco editors: the single code editor (MonacoEditorView, in MeshWeaver.Blazor) and the side-by-side diff editor (DiffEditorView, here). Both wrap a BlazorMonaco component, and both depend on a detail that is easy to miss and silent when missed.

The contract

BlazorMonaco renders its editor host as <div id="…" class="@CssClass"> and writes no style attribute. Verified against the shipped BlazorMonaco 3.5 assembly: the compiled markup carries the class and id attribute literals and no style at all. So the class the view passes as CssClass is the only handle on that element — if no stylesheet defines a rule for it, the element is a plain block div of height: auto containing nothing but Monaco's own absolutely-positioned children, i.e. zero pixels tall.

monaco.editor.create / createDiffEditor then lay the editor out at the container's measured size. With automaticLayout: true that measurement is repeated on every resize — so a zero-height container stays zero-height forever, and there is no error anywhere: no exception, no console warning, no failed request. The page renders, the host box occupies its declared height, and the editor inside it is invisible.

Wrapping the component in a sized <div> is not enough. height does not inherit, and a plain block child does not stretch to a parent's height. The rule has to name the container itself:

/* DiffEditorView.razor.css */
::deep .diff-editor-view {
    width: 100%;
    height: 100%;
}

::deep because the element belongs to the child component, not to this view's own markup — the scope attribute lands on the view's root <div>, and the rule compiles to [b-xxxxxxxxxx] .diff-editor-view.

What went wrong (MeshWeaver#3288)

DiffEditorView shipped from the day it was written with CssClass="diff-editor-view" and no stylesheet. Across both repos and every served CSS bundle, that class name appeared exactly once — in the markup that names it. The version comparison of a node whose content is structured data (the report was a Claims/Claim, whose developments array routes it to the JSON side-by-side editor rather than the markdown redline) therefore rendered as a 600px gap between the "Changes from v3 to v4" heading and the Restore button.

It survived that long because the common case never reaches this editor: a markdown node's version comparison renders the tracked-change redline, and the diff editor only appears for non-markdown content or behind ?view=source.

Measured in a headless browser against the shipped BlazorMonaco assets, on the exact DOM the view emits:

host box Monaco container original pane modified pane
without the stylesheet 1280×600 1280×0 625×0 625×0
with the stylesheet 1280×600 1280×600 625×600 625×600

Only the CONTAINER is sized. Inside a side-by-side diff Monaco positions .editor.original and .editor.modified itself from the split ratio, so forcing those to 100% — which the single-editor sheet legitimately does for its one editor — would break the split.

The second half: wait for Monaco

Blazor.start() is deliberately not gated on Monaco (gating it left Safari staring at an empty page while the 3.6MB AMD bundle loaded). The gate moved into the editor components instead: each one waits for window.monacoReady before rendering the BlazorMonaco component, because BlazorMonaco calls monaco.editor.create… on its first render and 3.5 console.errors and returns when monaco is still undefined — no editor, and nothing retries.

MonacoEditorView was given that gate; DiffEditorView was not, and now is. Both treat a failed or absent readiness promise as "ready", so the gate can only ever delay an editor, never withhold one.

The guard

MonacoEditorContainerSizingGuard (in MeshWeaver.Blazor.Views.Test) discovers every <Standalone…Editor> in both view assemblies' Monaco folders — from the sources, not a hand-written list — and fails when one names a CssClass its component stylesheet does not give a height. It also asserts that the scan found the known hosts, so a rename or a move cannot turn it into a test that checks an empty list and reports green.

A stylesheet contract is necessary, not sufficient: it cannot see an ancestor that collapses the host box. The pixels are confirmed by measuring the real Monaco layout in a browser, as above.

Reconnecting…
The server was updated. Reloading the page to pick up the latest version.