Editor extensions

An editor extension may have an associated editor extension hosted in the Github Pages section of the repo.

Editor extension URLs must now be registered in targetconfig.json under packages.approvedEditorExtensionUrls:

{
    ...
    packages: {
        ...
        approvedEditorExtensionUrls: [
            "url to extension"
        ]
    }
}

Configuration

The editor extension is configured in the pxt.json file by adding an extension field:

{
    ...
    extension: {
        url: "url to extension"
    }
}

The editor will automatically add an “Editor” button for the editor extension in the extensions category.

To debug a local extension, from the local dev server, add localeditorextensions=1 to the url and add the a localUrl field.

Urls must be registered in the targetconfig.json package configuration section under packages.approvedEditorExtensions.

Protocol

The editor and the editor extension <iframe> communicate using a protocol of IFrame messages.

  • Messages have a unique id to correlate responses to requests.
  • A response message can be requested. The id identifer can be used to correlate a receive response to the original query.
  • All messages sent by the editor extension must contain the extension id, extId. This identifier is passed when loading the <iframe> (see Initialization).
// sending message
var msg = {
    id: Math.random().toString(),
    type: "pxtpkgext",
    action: "extinit",
    extId: extId,
    response: true
}
window.parent.postMessage(msg, "*");

// handle the response
function receivedResponse(resp) {
  if (resp.action === "extinit")
    console.log('initialized!')
}
window.addEventListener("message", function(ev) {
  var resp = ev.data;
  if (resp && resp.type === "pxtpkgext")
    receivedResponse(resp);
}, false);

Initialization

When the user presses the editor extension button:

Store the extension id since it’s needed in every message.

var extId = window.location.hash.substr(1);
  • Once fully loaded, the extension sends a extinit message to the parent window.
var msg = {
    id: Math.random().toString(),
    type: "pxtpkgext",
    action: "extinit",
    extId: extId
}
...

Shown / Hidden events

The editor sends a extshown message when showing the editor frame, and a exthidden message after hiding the editor.

Read and Write code

The editor extension can read (extreadcode) and write (extwritecode) a dedicated TypeScript and JSON file in the project. The JSON file is designed to store rich metadata while the TypeScript is the “code behind” that gets executed. This feature does not require permissions.

Write code

var msg = {
    id: Math.random().toString(),
    type: "pxtpkgext",
    action: "extwritecode",
    extId: extId,
    body: {
        code: "// generated TypeScript code",
        json: "serialized JSON here"
    }
}
...

Read code

var id = Math.random().toString();
var msg = {
    id: id,
    type: "pxtpkgext",
    action: "extreadcode",
    extId: extId,
    response: true
}
...

function receivedResponse(resp) {
  if (resp.action === "extreadcode" && resp.id === id && resp.body) {
      var ts = resp.body.code;
      var json = JSON.parse(resp.body.json);
      ...
  }
}
...

Read user code

The extusercode message requests to read the entire set of files in the project. If successfull, the response contains a resp field with a map of the file names to file contents.

export interface UserCodeResponse extends ExtensionResponse {
    /* A mapping of file names to their contents */
    resp?: { [index: string]: string };
}

Data streams

When available, the editor may stream data coming from the devices. The extdatastream message requests to stream data. The following message sets a request for serial messages:

var msg {
    ...
    action: "extdatastream",
    body: {
        serial: true
    }
}
...

If successful, the editor will proxy serial messages to the editor <iframe>.

A lot of the plumbing has been done for you in this React-based template…