-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
1,712 additions
and
1,228 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,4 +103,7 @@ dist | |
# TernJS port file | ||
.tern-port | ||
|
||
.vscode-test-web | ||
.vscode-test-web | ||
|
||
resources/sim.js | ||
resources/sim.js.map |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
interface VSCodeAPI { | ||
postMessage(data: any): void; | ||
} | ||
|
||
interface BaseMessage { | ||
type: "simulator-extension" | ||
src: "simulator" | "extension"; | ||
action: string; | ||
id?: string; | ||
} | ||
|
||
interface VSCodeResponse extends BaseMessage { | ||
success: boolean; | ||
id: string; | ||
} | ||
|
||
interface TargetConfigMessage extends BaseMessage { | ||
action: "targetConfig"; | ||
} | ||
|
||
interface TargetConfigResponse extends VSCodeResponse { | ||
action: "targetConfig"; | ||
config: any; | ||
} | ||
|
||
type SimulatorExtensionMessage = TargetConfigMessage; | ||
type SimulatorExtensionResponse = TargetConfigResponse | VSCodeResponse; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import { getTargetConfigAsync } from "./service"; | ||
|
||
const FRAME_DATA_MESSAGE_CHANNEL = "messagechannel" | ||
const FRAME_ASPECT_RATIO = "aspectratio" | ||
const MESSAGE_SOURCE = "pxtdriver" | ||
const PERMANENT = "permanent" | ||
|
||
|
||
let simFrames: {[index: string]: HTMLIFrameElement} = {}; | ||
let _targetConfig: Promise<any>; | ||
|
||
|
||
function nextId() { | ||
return crypto.randomUUID(); | ||
} | ||
|
||
let pendingFrames: {[index: string]: Promise<void>} = {}; | ||
|
||
export function handleMessagePacket(message: any, source?: Window) { | ||
if (message.type === "messagepacket") { | ||
const channel = message.channel as string; | ||
|
||
if (!pendingFrames[channel]) { | ||
pendingFrames[channel] = (async () => { | ||
const targetConfig = await targetConfigAsync(); | ||
const simInfo = targetConfig.packages.approvedRepoLib[channel]; | ||
|
||
if (simInfo.simx) { | ||
startSimulatorExtension( | ||
getSimxUrl(channel, simInfo.simx.index), | ||
true, | ||
undefined, | ||
channel | ||
); | ||
} | ||
})(); | ||
} | ||
|
||
pendingFrames[channel].then(() => { | ||
for (const frame of Object.keys(simFrames)) { | ||
const contentWindow = simFrames[frame].contentWindow; | ||
|
||
if (contentWindow && contentWindow !== source) { | ||
simFrames[frame].contentWindow?.postMessage(message, "*"); | ||
} | ||
} | ||
}) | ||
|
||
const simFrame = getSimFrame(); | ||
|
||
if (simFrame.contentWindow && simFrame.contentWindow !== source) { | ||
simFrame.contentWindow.postMessage(message, "*") | ||
} | ||
} | ||
} | ||
|
||
function getSimFrame() { | ||
return document.getElementById("simframe") as HTMLIFrameElement; | ||
} | ||
|
||
function getSimUrl(): URL { | ||
return new URL(getSimFrame().src); | ||
} | ||
|
||
|
||
function getSimxUrl(repo: string, index = "index.html") { | ||
const simUrl = getSimUrl(); | ||
|
||
// Ensure we preserve upload target path (/app/<sha>---simulator) | ||
const simPath = simUrl.pathname.replace(/---?.*/, ""); | ||
// Construct the path. The "-" element delineates the extension key from the resource name. | ||
const simxPath = [simPath, "simx", repo, "-", index].join("/"); | ||
// Create the fully-qualified URL, preserving the origin by removing all leading slashes | ||
return new URL(simxPath.replace(/^\/+/, ""), simUrl.origin).toString(); | ||
} | ||
|
||
|
||
function createFrame(url: string): HTMLDivElement { | ||
const wrapper = document.createElement("div") as HTMLDivElement; | ||
wrapper.className = `simframe ui embed`; | ||
|
||
const frame = document.createElement('iframe') as HTMLIFrameElement; | ||
frame.id = 'sim-frame-' + nextId() | ||
frame.title = "Simulator"; | ||
frame.allowFullscreen = true; | ||
frame.setAttribute('allow', 'autoplay;microphone'); | ||
frame.setAttribute('sandbox', 'allow-same-origin allow-scripts'); | ||
frame.className = 'no-select'; | ||
|
||
let furl = url; | ||
furl += '#' + frame.id; | ||
|
||
frame.src = furl; | ||
frame.frameBorder = "0"; | ||
// frame.dataset['runid'] = this.runId; | ||
frame.dataset['origin'] = new URL(furl).origin || "*"; | ||
frame.dataset['loading'] = "true"; | ||
|
||
wrapper.appendChild(frame); | ||
|
||
const i = document.createElement("i"); | ||
i.className = "videoplay xicon icon"; | ||
i.style.display = "none"; | ||
wrapper.appendChild(i); | ||
|
||
const l = document.createElement("div"); | ||
l.className = "ui active loader"; | ||
i.style.display = "none"; | ||
wrapper.appendChild(l); | ||
|
||
return wrapper; | ||
} | ||
|
||
function startSimulatorExtension(url: string, permanent: boolean, aspectRatio?: number, messageChannel?: string) { | ||
const root = document.getElementById("root"); | ||
if (root) { | ||
root.classList.add("simx"); | ||
} | ||
|
||
aspectRatio = aspectRatio || 1.22; | ||
let wrapper = createFrame(url); | ||
getContainer().appendChild(wrapper); | ||
const messageFrame = wrapper.firstElementChild as HTMLIFrameElement; | ||
messageFrame.dataset[FRAME_DATA_MESSAGE_CHANNEL] = messageChannel; | ||
messageFrame.dataset[FRAME_ASPECT_RATIO] = aspectRatio + ""; | ||
wrapper.classList.add("simmsg"); | ||
if (permanent) { | ||
messageFrame.dataset[PERMANENT] = "true"; | ||
} | ||
|
||
simFrames[messageChannel!] = messageFrame; | ||
} | ||
|
||
function getContainer() { | ||
return document.getElementById("simulator-extension-frames") as HTMLDivElement; | ||
} | ||
|
||
async function targetConfigAsync() { | ||
if (!_targetConfig) { | ||
_targetConfig = await getTargetConfigAsync(); | ||
} | ||
|
||
return _targetConfig; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { handleMessagePacket } from "./frames"; | ||
import { initService } from "./service"; | ||
|
||
const acquireApi = (window as any).acquireVsCodeApi; | ||
const vscode = acquireApi(); | ||
|
||
(window as any).acquireVsCodeApi = () => vscode; | ||
|
||
window.addEventListener("message", function (m) { | ||
handleMessagePacket(m.data, m.source as Window); | ||
}); | ||
|
||
document.addEventListener("DOMContentLoaded", function (event) { | ||
const fs = document.getElementById("fullscreen"); | ||
if (fs) { | ||
fs.remove(); | ||
} | ||
|
||
const simFrame = document.getElementById("simframe") as HTMLIFrameElement; | ||
|
||
const framesContainer = document.createElement("div"); | ||
framesContainer.id = "simulator-extension-frames"; | ||
simFrame.insertAdjacentElement("afterend", framesContainer); | ||
}); | ||
|
||
initService(vscode); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/// <reference path="../localtypings/simulatorExtensionMessages.d.ts" /> | ||
|
||
let vscode: VSCodeAPI; | ||
const pendingMessages: {[index: string]: (resp: SimulatorExtensionResponse) => void} = {}; | ||
|
||
export async function getTargetConfigAsync() { | ||
const resp = await sendMessageAsync({ | ||
type: "simulator-extension", | ||
src: "simulator", | ||
action: "targetConfig" | ||
}) as TargetConfigResponse; | ||
|
||
return resp.config; | ||
} | ||
|
||
function sendMessageAsync(message: SimulatorExtensionMessage): Promise<VSCodeResponse> { | ||
return new Promise((resolve, reject) => { | ||
const toSend: BaseMessage = { | ||
...message, | ||
id: crypto.randomUUID() | ||
}; | ||
|
||
pendingMessages[toSend.id!] = resp => { | ||
if (resp.success) { | ||
resolve(resp); | ||
} | ||
else { | ||
reject(resp); | ||
} | ||
}; | ||
|
||
vscode.postMessage(toSend); | ||
}); | ||
} | ||
|
||
export function initService(api: VSCodeAPI) { | ||
vscode = api; | ||
|
||
window.addEventListener("message", function (m) { | ||
if (m.data?.type === "simulator-extension") { | ||
const response = m.data as SimulatorExtensionResponse; | ||
|
||
const handler = pendingMessages[response.id]; | ||
if (handler) { | ||
delete pendingMessages[response.id]; | ||
handler(response); | ||
} | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"compilerOptions": { | ||
"module": "commonjs", | ||
"target": "ES2020", | ||
"outDir": "../../dist/sim", | ||
"lib": [ | ||
"ES2020", "DOM" | ||
], | ||
"sourceMap": true, | ||
"rootDir": ".", | ||
"strict": true /* enable all strict type-checking options */ | ||
/* Additional Checks */ | ||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ | ||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ | ||
// "noUnusedParameters": true, /* Report errors on unused parameters. */ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
//@ts-check | ||
"use strict"; | ||
|
||
//@ts-check | ||
/** @typedef {import("webpack").Configuration} WebpackConfig **/ | ||
|
||
const path = require("path"); | ||
const webpack = require("webpack"); | ||
|
||
/** @type WebpackConfig */ | ||
const webExtensionConfig = { | ||
mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to "production") | ||
target: "web", // extensions run in a webworker context | ||
entry: { | ||
"extension": "./index.ts", | ||
}, | ||
output: { | ||
filename: "sim.js", | ||
path: path.join(__dirname, "../../resources"), | ||
libraryTarget: "commonjs", | ||
devtoolModuleFilenameTemplate: "../../[resource-path]" | ||
}, | ||
resolve: { | ||
mainFields: ["browser", "module", "main"], // look for `browser` entry point in imported node modules | ||
extensions: [".ts", ".js"], // support ts-files and js-files | ||
alias: { | ||
// provides alternate implementation for node module and source files | ||
}, | ||
fallback: { | ||
// Webpack 5 no longer polyfills Node.js core modules automatically. | ||
// see https://webpack.js.org/configuration/resolve/#resolvefallback | ||
// for the list of Node.js core module polyfills. | ||
"assert": require.resolve("assert"), | ||
"path": require.resolve("path-browserify") | ||
} | ||
}, | ||
module: { | ||
rules: [{ | ||
test: /\.ts$/, | ||
exclude: /node_modules/, | ||
use: [{ | ||
loader: "ts-loader" | ||
}] | ||
}, | ||
{ | ||
// Make sure our marketplace icon is copied to final output | ||
test: /\.(jpe?g|gif|png|svg)$/, | ||
loader: "file" | ||
}] | ||
}, | ||
plugins: [ | ||
new webpack.optimize.LimitChunkCountPlugin({ | ||
maxChunks: 1 // disable chunks by default since web extensions must be a single bundle | ||
}), | ||
new webpack.ProvidePlugin({ | ||
process: "process/browser", // provide a shim for the global `process` variable | ||
}), | ||
], | ||
externals: { | ||
"vscode": "commonjs vscode", // ignored because it doesn"t exist | ||
}, | ||
performance: { | ||
hints: false | ||
}, | ||
devtool: "nosources-source-map", // create a source map that points to the original source file | ||
infrastructureLogging: { | ||
level: "log", // enables logging required for problem matchers | ||
}, | ||
}; | ||
|
||
module.exports = [ webExtensionConfig ]; |
Oops, something went wrong.