Skip to content

VisualEditor

Edgar Mesquita edited this page Aug 14, 2026 · 14 revisions

Visual Editor (VS Code)

🌐 This page in: English · Português

A first-party VS Code extension that turns the SDK into a visual development environment: a screen renders beside the file that produces it, from the buffer you are typing in, and every rendered element knows which C# expression built it.

The preview is the real web realizer running the real compiled module. It is not a lookalike, and that is the point: a preview that renders is evidence the page renders. Nothing new draws pixels.

Status. The design host and the identity mechanism ship and are covered by tests. The extension itself is early — it is built from source, not installed from the Marketplace, and only the preview and the source-span identity are in place. Editing from the canvas is ahead. Track E in the Roadmap has the milestones.

The design host

Since 0.2.0-preview.30

eqdesign is a long-lived process that holds the project's Roslyn compilation and compiles the editor's buffer — not the file on disk. That distinction is the whole reason it exists: eqc reads files and the hot-reload service watches the filesystem, so neither can show you the text you are currently looking at, only the text you last saved.

It speaks newline-delimited JSON over stdio. No port to choose, nothing to authorise, nothing left listening if the window dies, and it works unchanged over Remote-SSH because the extension host runs on the remote machine anyway.

Measured on samples/DefaultUIDashboard (16 source files, 316 references):

Step Time
Build the compilation, once at activation 271 ms
Compile an edit (662-line PaymentsPage, warm) p50 293 ms
Diagnostics only (bind, no transpile) 36 ms

Hence two cadences: errors are re-checked after a 150 ms pause in typing, the preview recompiles after 400 ms. A C# error stops the compile before anything is emitted — Roslyn parses leniently, so a missing brace still yields a tree the transpiler would walk, producing a module that mounts and throws, which arrives on screen as a blank frame with nothing to explain it.

The host refuses to start if the reference list is empty, rather than compiling against an incomplete model. That failure is otherwise invisible: without the semantic model, named arguments are emitted in syntactic order, and the screen renders with its values in the wrong slots and no error anywhere.

Design mode: a node remembers the C# that built it

Since 0.2.0-preview.30

VisualNode carries an Origin — the source span of the expression that constructed it, or null, which is what every shipped build is. A design-mode compilation fills it in:

var compiler = new ComponentCompiler { DesignMode = true };
// Every node construction is emitted wrapped, so the built node remembers where it came from:
//   $eq.origin(UI.text(…), "…/PaymentsPage.cs|28:12|28:73")

Both realizers carry it into the DOM as data-eq-origin, attached at their single dispatch:

<div data-eq-origin="/src/Screens/PaymentsPage.cs|27:8|34:10">
  <span data-eq-origin="/src/Screens/PaymentsPage.cs|28:12|28:73">Count: 0</span>
</div>

The format is path|startLine:startColumn|endLine:endColumn, zero-based — the editor's own coordinates.

Why not source maps. The obvious answer does not work. The whole Build body is converted to one flat string and emitted through a single call, so the finest position a V3 map can name is the start of the method: one shipped module has a 942-character line. An origin is exact instead — and because it is a plain string on the abstract node, the native (Photon) track inherits the same mechanism the day it wants it.

Only constructions are stamped, never references. Stamping a variable where it is merely mentioned would overwrite the construction's origin with the span of a use, and select the wrong line. A node built inside a foreach carries the span of the one expression that builds it — which is the only editable thing there anyway.

Design mode is off by default and never on in an SDK build: the wrapper is real emitted code, and shipping it would put a design tool's concern in every user's bundle. A production render contains no data-eq-origin at all, and the SSR/hydration fingerprint test pins that.

Running it today

The extension is built from source and points at the design host in the same checkout:

cd extensions/vscode && npm install && npm run compile

Then open a C# page or component and run eQuantic UI: Open Preview. The project must have been built once — the preview stands on an ordinary build's output (the reference list the SDK writes, and wwwroot/_equantic/runtime.js), so "build the project once" is the honest instruction when a piece is missing.

What it does not do yet

  • No editing from the canvas. Selection and inspection come first; property write-back and the component palette are the phases after.
  • No native preview. Identity is already solved on Photon (LayoutNode carries the node, absolute bounds and a stable path); only getting frames into a webview is missing.
  • No [ServerAction] calls. Action ids come from a startup assembly scan, so an ad-hoc preview class is never in the registry.
  • The baseline theme. An app selects its theme at startup, and reading that back means running the app's composition root. Shapes and layout are exact; a rebranded palette is not yet reflected.

See also

Clone this wiki locally