Images and clipboard

Documentation index · Security

Image references versus file imports

An existing durable image URL can be inserted directly. A pasted bitmap, dropped file, or file-picker selection must first be stored somewhere. The editor handles the insertion, progress/error state and URL replacement; the host owns the storage operation.

The image insertion command creates block objects in Visual mode. Supported inline HTML images can also remain within prose; this does not imply support for arbitrary nested image layouts. References serialize as Markdown image syntax or supported HTML. No image bytes are automatically embedded as base64 in the saved text.

editor.execute('insertImage', {
  src: '/editor-assets/example.png', alt: 'System diagram'
});
await editor.InsertImageAsync("https://assets.example.com/diagram.png", "System diagram");

The URL must be accepted by the image URL rules. Loading it also depends on the origin policy; a valid URL can be stored yet blocked from display. Prefer application-controlled HTTPS URLs or appropriate relative URLs. Do not insert local machine file paths as portable asset references.

Linked images and image controls

Selected linked image with independent destination, source and title controls

Select an image in Visual mode, or place the cursor in recognized image syntax in Source mode. Its contextual toolbar appears near the top-left edge, clamped to the viewport. Only the active object's controls are shown; interacting with its controls keeps it active.

Control What it changes
Link URL The destination opened by the linked picture; an empty value removes the link wrapper
Image Source The image resource URL, preserving an existing destination and link title
Link title The optional title on an existing link; distinct from image alternative text
Alt text The image's textual alternative
Size Display dimensions; the original image file is unchanged
Actions Host-registered image operations, when supplied

Markdown documents support HTML anchors around either an HTML image or Markdown image syntax, including supported multiline forms:

<a href="https://example.com/article" title="Read the article">
<img src="/___FileProxy___?mode=vstsgit&path=DevProcess/TextEditor/https://example.com/cover.png" alt="Article cover">
</a>

The linked picture is one image object in Visual mode. Preview preserves the link around the image. Source decorations mark the image syntax itself, and the image toolbar handles the wrapper without adding a competing text-link toolbar. Code fences keep this syntax literal. Embedded HTML remains subject to the document policy.

Image Size menu with reset, percentage presets, pixel dimensions and aspect-ratio lock

Open Size to choose a percentage of the image's natural dimensions, enter width/height in pixels, or reset explicit dimensions. Lock aspect ratio keeps proportional resizing. Percentage presets require a loaded image with known natural dimensions. Dimensions affect the document representation; toolbar appearance and editor pane widths do not. See image editing details for drag/move behavior and serialization limits.

If a resource cannot load, use Image Source to correct its URL; copying its markup can still work. A valid stored URL may be blocked by host resource policy or unavailable on the network.

Source image definition with contextual controls and an incomplete image definition below it

In Source mode, keep the caret in the recognized image definition to use its controls while editing the literal Markdown. The incomplete definition below illustrates why unfinished syntax cannot provide the same image operations as a recognized object.

Browser import callback

Provide onImageImport at creation. It receives a File, source label, cancellation signal and image ID, and must return an object with a durable src. The endpoint below is illustrative host code; the included ASP.NET sample supplies an /editor-assets endpoint with this general role.

const editor = CODEEditor.create(element, {
  format: 'markdown', mode: 'rich',
  imageOrigins: [location.origin],
  async onImageImport({ file, signal }) {
    const response = await fetch('/editor-assets', {
      method: 'POST', body: file,
      headers: { 'Content-Type': file.type }, signal
    });
    if (!response.ok) throw new Error('The image could not be stored.');
    const result = await response.json();
    return { src: result.src, assetId: result.assetId, alt: file.name };
  }
});
// With a File obtained from an <input type="file">:
// await editor.importImage(file, 'picker');

source distinguishes picker, drop, paste and retry paths. The callback may upload, write through a native bridge, or use another host service. Do not return a temporary browser object URL as durable storage.

WinForms import event

Assign e.Result to your storage Task. The control transports and validates file bytes; it does not require a storage service interface or DI container.

editor.ImageImportRequested += (_, e) =>
{
    e.Result = SaveImageAsync(e.Bytes, e.MimeType, e.CancellationToken);
};

Here SaveImageAsync is an application method returning Task<ImageImportResult>. It should store the bytes under an application-generated name, honor cancellation, and return new ImageImportResult(src, assetId, alt). File names supplied by a browser are display metadata; do not use them directly as trusted filesystem paths.

A local-folder example, configured before initialization:

string imageFolder = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
    "MyApplication", "EditorImages");
Directory.CreateDirectory(imageFolder);
editor.LocalImageFolder = imageFolder;
editor.Options.ImageOrigins = [TextEditor.LocalImageOrigin];
editor.ImageImportRequested += (_, e) =>
    e.Result = StoreLocalAsync(e.Bytes, e.MimeType, e.CancellationToken);

async Task<ImageImportResult> StoreLocalAsync(
    ReadOnlyMemory<byte> bytes, string mime, CancellationToken token)
{
    string extension = mime switch
    {
        "image/png" => ".png", "image/jpeg" => ".jpg",
        "image/gif" => ".gif", "image/webp" => ".webp",
        _ => throw new InvalidOperationException("Unsupported image type.")
    };
    string name = Guid.NewGuid().ToString("N") + extension;
    await File.WriteAllBytesAsync(Path.Combine(imageFolder, name), bytes.ToArray(), token);
    return new ImageImportResult($"{TextEditor.LocalImageOrigin}/{name}", name);
}

That URL works only where the same virtual origin maps the appropriate asset folder. A Markdown file copied to another machine or displayed on a website will not automatically carry its assets. For Olympus content shared across hosts, an application asset service with stable IDs/URLs is usually the better integration. Save As is not an asset-bundling operation.

There is no public .NET method that accepts an arbitrary file's bytes for import. Native paste/drop uses the event. For a host-owned file picker, store/upload the selected file through your application and insert its returned URL with InsertImageAsync.

External images in pasted HTML

externalImages / ExternalImages governs images referenced by pasted HTML:

Value Behavior
Reference (default) Keep a permitted external URL reference
Reject Omit external image references from that import path
Import Ask onExternalImageImport / ExternalImageImportRequested to obtain a durable replacement URL

The external callback receives a URL and cancellation, not file bytes. The host must decide which remote resources it is willing to fetch and how to authenticate. This setting is separate from allowRemoteImages / AllowRemoteImages, which controls display resource permission. An external reference can therefore remain in the document while its display is blocked.

Lifecycle and limits

The default file size limit is 10 MiB; allowed types are PNG, JPEG, GIF and WebP. JavaScript permits up to eight concurrent imports, with a bounded retry-file cache. WinForms validates bounded transport and file signatures; PNG/JPEG/GIF decoding also enforces a 40-million-pixel limit. WebP receives signature validation, not equivalent decoding. Native MaxImageBytes permits at most 100 MiB and native pending transport has an aggregate bound.

An insertion starts pending. Success resolves it to a ready URL without creating a separate user-facing upload undo step. Failure leaves a failed image that can be retried or removed. Pending and failed images block saveable-value retrieval and validated snapshots. They also block view switching until resolved or removed.

Undo, removal, load and disposal can cancel work. Late results do not reinsert removed content. The JavaScript assetAbandoned event tells a host that an uploaded result no longer belongs to the current document; the host decides whether and when to garbage-collect it. Never immediately delete a shared asset merely because a user undoes a reference. Retry can fail if its retained original file has been evicted; the user must choose the file again.

Clipboard behavior

Source copy exports the exact selected source text without fetching image binaries. Visual copy can export supported HTML and plain text. Complete tables can retain structure; partial table selections are flattened for clipboard output. HTML paste passes through the supported importer and feature policy. Binary-only native image paste invokes the host image-import handler in either view; picker/drop support depends on the host integration.

CopyAsync and CutAsync publish Unicode text and supported Windows HTML. Selected images can also publish bitmap/PNG and original image data when available; an unavailable image does not prevent copying its markup. Cut deletes only after successful copying and checks that the document revision still matches. PasteAsync prefers recognized image markup, then supported binary image data, then HTML/text. Binary imports require the host image-storage handler. Keyboard copy/paste also carries supported rich HTML and images. Use PasteHtmlAsync for host-supplied HTML and PasteTsvAsync for explicitly prepared tabular text. PasteHtmlAsync is intended for Visual mode; it is not an HTML-source insertion helper.

PasteAsync does nothing when the native clipboard has no supported image, HTML, or nonempty text, preserving the selection and document. An empty JavaScript pasteContent payload likewise leaves both Source and Visual documents untouched. CopyAsync with an empty selection does not overwrite the clipboard. Real clipboard combinations and ordering from Word, Outlook and browser applications remain an acceptance test area.