Editing images, Source review and large documents

The follow-up features apply to the shared browser/ASP.NET runtime and the embedded WinForms control. Use Editorial demo and Source review in the examples to try the workflow.

Code and Markdown

Code controls use the image toolbar's appearance and float above the active snippet's top-left edge in both modes, with a nearby fallback when space is limited. Controls disappear when the cursor leaves the object or focus moves outside the editor and its controls. Language changes edit the fence info string; contextual action results replace its contents as one guarded, undoable operation. See code snippets for indentation defaults and formatting events.

editor.configure({
  maxDocumentLength: 0,
  historyLimitBytes: 32 * 1024 * 1024
});

Legacy toolbarPlacement/toolbarPlacements values remain accepted for compatibility; floating object toolbars follow the shared active-object positioning convention. Controls are presentation only and never enter saved/copied content. Unknown fence languages remain valid code. c#, C#, cs and csharp share the C# colorizer. Lexical coloring covers all 193 bundled highlight.js 11.12.0 languages plus Visual FoxPro, with separate HTML and XML selections (195 catalog entries), including standalone Source documents and fenced code in Markdown Source and Visual editing. The default code-language picker groups common languages first and then lists the remaining catalog by display name; codeLanguages can restrict it. CodeEditorLanguages.catalog exposes IDs, names and aliases. Standard highlight.js aliases are accepted along with vfp/foxpro/visualfoxpro, jsx, tsx and tsql. The browser sample also offers the full catalog. Highlighting is bundled locally and works offline. Unknown languages have plain code text. decorated: false disables coloring in both views. This is lexical decoration, not semantic analysis or IntelliSense.

Markdown recognizes <br>, <br/> and <br />, case-insensitively, as hard breaks. Lists retain their structure, including an image following a break. Untouched source keeps its spelling. Edited supported content can normalize to Markdown hard-break notation. Repeated view changes preserve source exactly; after editing, serialization becomes stable without accumulating blank lines.

Image objects

Selected image with size controls and a resize handle

Click an image to select the object and show its outline/resize handle. This works for URL references and imported attachments, including unavailable images. Copy offers HTML with the reference, alt text and display dimensions; the plain-text fallback contains image source syntax. Cut/Delete remove the object and undo restores it. Visual-mode WinForms copy includes the full image tag, HTML, PNG bytes and a bitmap when the image bytes are available. Source-mode copy always contains only the exact selected source text; it never fetches or copies image binaries. Visual copy commits markup first and silently keeps it if the image is missing, blocked or invalid. Valid raster image signatures are recognized even when the server labels them application/octet-stream. Browser copy also attempts PNG plus HTML and text through the Clipboard API; browser permissions and cross-origin image access can prevent the binary representation, in which case status reports the limitation. Paste prefers a recognized HTML or Markdown image tag when both markup and binary data are available. Binary-only paste invokes the existing image-import handler in either editing view.

Drag an image before/after another block to move its existing identity, reference, size and attached comments. The blue insertion line shows the target; dragging near the viewport edge scrolls. Alt+Up/Down moves a selected image by one block. Dropping at its original location does nothing. Source view retains literal editable image syntax.

Selecting an image shows a floating image toolbar, an eight-handle selection frame around the bitmap, and a size badge below it. Unselected images show no editing controls. The badge reads Native size until explicit dimensions are set, then shows width × height in CSS pixels. The toolbar and badge reserve space only while selected. Drag a corner or side handle to preview a resize; releasing commits one undoable/tracked display-size change. Arrow keys on a focused handle resize by one pixel (Shift uses ten). Escape or pointer cancellation restores the previous size. Aspect ratio stays locked by default. A host may set unlockImageAspectRatio: true to allow Shift-dragging with independent dimensions. The Size menu provides native size, 25%/50%/75%/100% presets based on intrinsic image dimensions, Width/Height inputs that resize immediately as valid values are typed, an aspect-ratio checkbox and Reset Size to clear explicit dimensions. Empty or invalid intermediate values do not change the image. Continuous typing groups into one undo step. Replace changes the URL and Alt text edits the alternative text; both are undoable. Resizing does not modify the underlying file. Cropping remains deferred.

editor.resizeImage(imageBlockId, 640, 360);
editor.resizeImage(imageBlockId); // clear explicit sizing
editor.moveImage(imageBlockId, targetBlockId, true); // after target

Ordinary unsized images use Markdown image syntax. Sized or linked images use escaped safe HTML, such as <img src="/___FileProxy___?mode=vstsgit&path=DevProcess/TextEditor/diagram.png" alt="Workflow" width="640" height="360">, optionally inside a safe link. Width-only/height-only imports retain their aspect semantics. Sizes are positive integer pixels up to 100,000. Strict Markdown/HTML-disabled policies reject newly introduced dimensions rather than losing them; enable embedded HTML or clear sizing to save such content. Only supported attributes are imported; active HTML is never enabled by image support.

Review in either view

Source comments, tracked edits and C# code controls beside an editor-relative review margin

Comment highlights, point markers for hidden syntax, inserted text and deleted-text decorations work in Source and Visual views. Hover a highlight to read all overlapping discussions without moving the caret. Focus or click it to pin the card for reply/resolve actions. Escape, an outside click or navigation closes the transient discussion. Scrolls reposition the review margin; reply text is not part of the article.

The default review margin lists comments and changes in document order and scrolls independently of the article. Clicking a card reveals its anchored text. A dashed line connects a visible anchor to its visible card while either has hover/focus. Persistent anchor pins identify comments even without a line. Navigation lists offscreen, article-level, resolved and detached threads; selecting a live annotation reveals its text. Detached threads offer Attach to selection. Pending changes appear in Markup, with Accept/Reject controls. A narrow editor falls back to the panel. Closing review releases the wide margin; comment icons remain in a narrow strip and reopen their discussion when clicked. Documents without anchored comments reserve no icon strip. Original remains a separate read-only source projection.

editor.configureReview({ presentation: 'margin', marginWidth: 330 });
editor.showReview(true);
const annotations = editor.getReviewAnnotations();
const rectangles = editor.getReviewAnchorRects(annotations[0].id);
editor.selectReviewAnnotation(annotations[0].id);

presentation is margin or panel; marginWidth is 240–600 pixels. Margin mode needs at least its width plus 420 pixels for article text. It measures visible anchors on demand; offscreen cards are represented in navigation. Resolved discussions remain navigable in the panel. These APIs use logical annotation IDs and current viewport rectangles; clients must not persist DOM nodes or pixel coordinates.

Canonical anchor.sourceRange coordinates persist alongside the logical/block anchor. Shared model/mapping.js projects them into each editing view; review.annotations() supplies decorations and the Surface supplies current geometry. Syntax absent from Visual text becomes a point anchor, while its exact Source range remains in the sidecar. Projected ranges expose projection (source, text, point or unmapped); an unsupported exact projection stays at its related block point rather than guessing a text occurrence. Stable block identities keep image comments attached through moves. Quote/context recovery is explicit on external edits; ambiguous content needs reattachment. Highlight text, deleted-text UI, cards and connectors are excluded from native text scans, clipboard and serialization.

Resource policy and measured performance

There is no default document-length ceiling. maxDocumentLength: 0 means unlimited; a host may set a positive limit in UTF-16 model units. Rejections retain content/selection and report status. History keeps up to 100 operations and defaults to a 32 MiB estimated snapshot budget, always retaining the newest operation; historyLimitBytes: 0 removes the byte budget. This estimate is not a process-memory limit. Review metadata retains its separate documented bounds.

Atomic ranges are indexed once per model revision, unchanged flat blocks reuse their DOM, serialization is cached, inactive review skips diffing, sample inspector updates are batched, and the gutter measures visible lines. Mapping and annotation caches are separate from geometry. Full model snapshots/parsing and full-document DOM are still used; viewport virtualization is future work.

tests/web/long-document-benchmark.js exposes await CodeEditorBenchmark.run(disposableEditor). It replaces the supplied document. The October 20 WebView2 155 run on this development machine, using the shared sample with a wide review margin and 100 comments, recorded:

Fixture Load One edit Edit with tracking and 100 comments
1,000 paragraphs, 64,888 characters 76 ms 19 ms 65 ms
10,000 paragraphs, 658,888 characters 581 ms 187 ms 367 ms

An 819,998-character Source fixture (10,000 lines, CRLF) loaded in 254 ms; pasting a second copy took 75 ms and retained 1,639,996 characters. Undo restored the exact input and redo restored the paste. The gutter rendered 15 visible labels. Approximate browser heap samples were 32/219 MB during the successive paragraph runs; these include live DOM, histories and uncollected allocations, not isolated document sizes. Scroll-to-frame observations were 33/30 ms. These are observations, not timing guarantees. The original 1,000-paragraph edit measured about 686 ms. Long Visual documents with active review can still have noticeable latency; measure real article structures and host callbacks on target hardware.

WinForms equivalents

editor.Options.MaxDocumentLength = 0;
editor.Options.HistoryLimitBytes = 32 * 1024 * 1024;
editor.Options.UnlockImageAspectRatio = false;
await editor.ConfigureExtensionsAsync(new(ToolbarPlacement: "top",
    ToolbarPlacements: new() { ["image"] = "bottom" }));
await editor.ConfigureReviewAsync(new(Presentation: "margin", MarginWidth: 330));
await editor.ResizeImageAsync(imageBlockId, 640, 360);
await editor.MoveImageAsync(imageBlockId, targetBlockId, after: true);
var annotations = await editor.GetReviewAnnotationsAsync();
ReviewAnchorRect[] rects = await editor.GetReviewAnchorRectsAsync(commentId);
await editor.SelectReviewAnnotationAsync(commentId);

EditorLanguage exposes every bundled language, including Rust, Go, Java and VisualFoxPro; existing enum values remain stable. Use editor.Options.Language = EditorLanguage.VisualFoxPro for a FoxPro Source document. CopyAsync now publishes Unicode plain text and a UTF-8-offset Windows HTML clipboard envelope; PasteAsync offers both representations to the editor. Source mode uses the literal text representation. Native clipboard availability still depends on the Windows session; simulated DOM clipboard checks cannot prove external application interoperability.

Review cards now have polished defaults, author/time hierarchy and gutter bubble toggles. Connectors route above text through clear whitespace with a gutter-only fallback. The component exposes per-instance themes for the surface and its portals. See UI styling and comment bubbles for CSS variables/classes, browser/WinForms APIs and the two-editor showcase.

Updating syntax grammars

The bundled highlighter comes from the BSD-3-Clause licensed @highlightjs/cdn-assets 11.12.0 package. Extract that package and run node build/vendor-highlight.cjs path/to/package, then node build/assets.cjs. The vendor bundle includes the full upstream license. When upgrading versions, update the pinned version in the vendor script, reconcile the WinForms enum with CodeEditorLanguages.catalog, and run the language tests; the token adapter uses the pinned highlighter’s token tree to preserve UTF-16 source offsets without inserting generated HTML. Visual FoxPro is maintained in languages/languages.js. Line comment shortcuts cover languages with configured line delimiters; grammars without one still support highlighting and bracket matching.

Configuring the image toolbar

JavaScript accepts imageToolbar: { size: true, replace: true, alternativeText: true, actions: true, inlineActions: false } at creation or through configure. Omitted flags use these defaults; an empty object resets them. Set individual flags to false to hide built-in controls. Image contextActions appear only when registered, either in the same toolbar's Actions menu or as individual buttons with inlineActions: true. Action callbacks and native ContextActionRequested events keep the existing image context and guarded result workflow. All-false flags remove the toolbar while retaining the selection frame and badge.

editor.ImageToolbar = new EditorImageToolbarOptions(
    Replace: false, AlternativeText: true, InlineActions: true);
// Also available as editor.Options.ImageToolbar.

Built-in URL/alt-text editors use WebView2/browser prompts. Hosts that supply their own media picker can hide Replace and register an image context action instead. Source view shows the same toolbar when the caret is inside a recognized image definition anywhere in the text (Markdown image syntax or embedded HTML, including inline images). The toolbar anchors above the start of the definition when space permits. A larger image icon marks recognized syntax; hovering or focusing it previews the image, subject to the configured image-loading policy. Visual mode renders surrounding prose and the inline image in one paragraph. Appearance and toolbar configuration never enter saved document content. Explicit sizes render without the former 480px height cap or editor-width cap; the editor scrolls for large images. Unsized images still fit the available editor width while preserving their natural aspect ratio. Stored dimensions must be positive integers up to 100,000 CSS pixels per axis.

Image definitions in Markdown Source

With syntax decoration enabled, recognized image definitions receive a distinct color, a small image icon and a recognition tooltip. An incomplete or unsupported ![...] definition receives warning styling instead. These indicators reflect the editor parser and safe URL rules; they do not prove that the referenced file exists. Definitions inside fenced code or comments are not treated as document images.

Move the caret inside a recognized standalone image definition to use the same configurable Size, Replace, Alt text and optional host actions as Visual view. Source edits remain undoable and do not switch views. Dimensions require embedded HTML, so sizing a Markdown image emits an <img> definition. Surrounding source and line endings remain intact. Percentage presets require intrinsic dimensions; a hidden image is loaded only when the host's existing image-loading policy allows that URL. Otherwise presets remain disabled and numeric dimensions still work. Source image action contexts include sourceImage: true, source range, raw definition and normal image metadata. Results use the existing stale-target protection.

Use Appearance.ImageSyntaxColor and ImageSyntaxWarningColor (JavaScript imageSyntaxColor / imageSyntaxWarningColor, CSS --ce-syntax-image / --ce-syntax-image-warning) for indicator colors.

Simple generated lists serialize with adjacent bullet lines. Explicitly loose lists retain their blank lines; a Source/Visual view switch alone never normalizes stored source. Visual images support arrow-key object selection and caret positions on either side. Left/Right leave the selection at the corresponding image edge; a further arrow moves into adjacent text. Up/Down enter images from the first/last visible line of neighboring text. Shift extends selection. Delete and Backspace remove selected images atomically; deletion at an adjacent paragraph boundary also removes the image. Cut, copy and undo preserve image metadata and surrounding text. Clipboard data includes an HTML image reference and Markdown plain text. Typing at an image edge creates a neighboring paragraph. Read-only mode permits navigation and copying while blocking mutation. Image Actions menus share the Size menu palette and use background focus highlighting without a bordered outline.

Shift+Up/Down extends or shrinks text selections by rendered lines in both views, including wrapped paragraphs and backward selections. The stationary anchor and preferred horizontal position are retained while reversing direction.