Architecture and maintenance

Documentation index

Separation of responsibilities

The core document/session model is independent of the browser DOM. Commands mutate the model through validated operations and history. The surface adapter translates native browser input/selection to model commands and renders current state. The public editor coordinates policy, codecs, preview, imports and host notifications.

flowchart TD H["Host API and toolbar"] --> E["Editor coordinator"] N["Native browser input"] --> S["Surface adapter"] S --> M["Model, commands and history"] E --> M M --> S E --> C["Codecs and policy"] C --> P["Preview pipeline"] E --> A["Host asset callbacks"]

The DOM is a projection. Do not save element.innerHTML or use execCommand as the application command architecture. Native browser editing is intercepted/normalized, with DOM reconciliation as an input fallback; the model remains authoritative.

Runtime modules

File Responsibility
codecs.js Markdown parse/serialize, source metadata and safe URL helpers
core.js Sessions, source/rich documents, positions, selections, history, revisions, bookmarks
commands.js Editing operations, block/mark operations, input rules, search/comment helpers
documents.js Supported HTML import/export and structured document helpers
structures.js Lists, tables, cell navigation, image blocks, structural replacement
policy.js Presets, feature compatibility, diagnostics and import handling
markdown-commands.js Markdown Source bold/italic delimiter operations
preview.js Preview preprocessing/sanitization, iframe, CSS, mapping and divider
languages.js Lexical source decoration and bracket-matching helper
paste.js Clipboard import/input fallback helpers
surface.js contenteditable rendering/input, composition, native selection, measurement
editor.js Base component lifecycle, API, command routing and events
advanced.js Current extended editor: policy, structures, images, preview override
image-host.js Optional sample native/browser image transport
app.js, feature-ui.js Demonstration host UI and optional demo bridge
styles.css, features.css Surface and demonstration styling, structure/preview rules

Load order matters because modules augment shared globals/prototypes. advanced.js replaces CODEEditor.Editor/create; instantiate only after it loads. The globals are not an ES module dependency system.

Document shapes

A source document has a text string. A rich document contains ordered blocks with stable IDs and marked text runs. Supported block kinds include paragraph, headings, list/task items, quote, code, thematic break, raw fragment, cell and image. Structure metadata connects related list/table nodes and builds a normalized structural index.

Runs carry text plus marks such as bold/italic/code/link and supported style values. A table is represented through cell blocks with shared table/row/column metadata; it is not arbitrary nested HTML. An image is a block with reference/state metadata. Opaque raw content preserves unsupported source without pretending to offer editable semantics.

Rich selections use block IDs and offsets; source uses string offsets. Helpers flatten rich positions when needed for search, native transport and range operations. Source metadata and fingerprints allow unchanged source to be retained; edited structures can be serialized canonically.

Transactions and rendering

An edit captures model/selection state, validates the mutation and stores history. Source transaction batches validate all ranges against one revision. History is bounded and adjacent typing can be grouped. A rebuild can replace the surface without discarding this state.

The current surface uses browser contenteditable and browser selection/caret layout. It does not draw its own blinking caret. Some rendering paths replace substantial DOM content and measurement/colorizing can scan the document. There is no viewport virtualization, piece table optimized for huge files, worker parser, or semantic language engine.

Composition requires special handling: rerendering during an IME's intermediate state can destroy input. The adapter protects composition and reconciles the committed result. Operations needing committed values must respect that lifecycle.

Preview is separate from serialization

Preview hooks receive a serialized snapshot of content but return display-only substitutions or HTML. They do not mutate the document. The standard output path adds measurement anchors. Arbitrary host HTML cannot automatically retain source mapping, so following falls back to proportional scrolling.

Preview-only CSS lives in the iframe. Source/visual editor display CSS remains separate. Keeping these paths separate lets a blog preview imitate a site without leaking that site's CSS into the input surface.

Native wrapper layout

File Responsibility
TextEditor.cs UserControl, WebView2 setup, queue, state cache, method bridge, lifecycle
TextEditor.Properties.cs Typed forwarding properties
TextEditor.Preview.cs Preview properties/events and async reverse requests
EditorOptions.cs Settings, validation, notifications and wire values
EditorTypes.cs Public enums, records and event arguments
NativeImages.cs Bounded image transport, validation and callback routing
Assets/control.html Minimal editing shell
Assets/control-host.js Typed-wrapper bridge, state serialization and internal find UI
Assets/control.css Control-only layout

Assets are embedded resources under CODE.Editor.WinForms.Assets.*. The sample Form is a consumer, not the control implementation. Keep native API names, bridge operations, and JavaScript contracts aligned when adding a capability.

Keeping the two deliveries synchronized

The full project contains the canonical browser files at its root and a copy embedded in the WinForms class library. The standalone delivery contains the class library's embedded assets and corresponding tests. A runtime change must be propagated to both distributions before packing.

Recommended release sequence:

  1. Change the appropriate core/component source and targeted tests.
  2. Synchronize embedded assets and the two class-library copies.
  3. Run model tests and relevant browser/native acceptance checks.
  4. Rebuild the self-contained demo.
  5. Build and pack the WinForms library and test a package-only consumer.
  6. Update API/default/limitation documentation and test evidence.
  7. Create archives without transient build directories or obsolete loose editor copies.

This documentation-only update does not change runtime behavior or bump the package version.

A later custom-rendered source surface

The model boundary helps a future Monaco-like surface: it can retain document operations, history, serialization and host APIs. It still requires a new input/caret/selection strategy, line layout and hit testing, viewport rendering, scroll metrics, composition support, accessibility integration, and mobile behavior. Existing offset mapping and tests become useful constraints, not an implementation of those capabilities.

Keep Source and Visual views free to use different adapters. A highly optimized source surface need not force a rich document editor to abandon native contenteditable. Introduce an explicit adapter contract before replacing either, and keep host selection/save semantics stable.