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.