Skip to content

Commit 335b1aa

Browse files
committed
added docs.
1 parent 810c060 commit 335b1aa

File tree

10 files changed

+68
-22
lines changed

10 files changed

+68
-22
lines changed

src/Draco.Editor.Web/app/build.js

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ const outDir = path.join(__dirname, distDir);
2020
const binFolder = process.argv[2];
2121
const debug = binFolder.includes('Debug');
2222

23+
// rebundling the dotnet.js with vite bundler
24+
// this was needed because firefox didn't supported esmodule in webworker
25+
// it does now (the bugzilla ticket i followed on the subject was completed), so it may not be needed anymore.
2326
async function dotnetjsBuild() {
2427
await viteBuild(defineConfig({ // Yes, I'm using another bundler, because this one bundle correctly dotnet.js to CJS...
2528
build: {

src/Draco.Editor.Web/app/favicon-downloader.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// this simply take two svg, slap a css media query so a single image can be used for both light and dark theme
2+
// this is necessary for the favicon, to have dark/light favicon
3+
14
function stripXMLHeader(xml) {
25
const indexOf = xml.indexOf('\n');
36
return xml.slice(indexOf);

src/Draco.Editor.Web/app/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
</head>
1212

1313
<body>
14-
<div id="layoutContainer">
14+
<div id="layoutContainer"> <!-- for golden layout. -->
1515
</div>
1616
<script src="ts/app.js" type="module"></script>
1717
</body>

src/Draco.Editor.Web/app/ts/LayoutComponents/StdOut.ts

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import { Terminal } from 'xterm';
33
import { FitAddon } from 'xterm-addon-fit';
44
import { getDownloadViewElement } from '../cache.js';
55

6+
7+
/**
8+
* This simply setup a xterm (it's what vscode uses) terminal
9+
*/
610
export class StdOut {
711
static terminals: Terminal[] = [];
812

src/Draco.Editor.Web/app/ts/LayoutComponents/TextDisplay.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { ComponentContainer } from 'golden-layout';
33
import { getDownloadViewElement } from '../cache.js';
44
import { subscribeOutputChange } from '../dotnet.js';
55

6+
// this setup a readonly monaco editor to view code output (IR/IL)
7+
68
export class TextDisplay {
79
static editors = {};
810

@@ -17,8 +19,8 @@ export class TextDisplay {
1719
div.classList.add('output-viewer');
1820
this.rootElement.appendChild(div);
1921
const editor = monaco.editor.create(div, {
20-
theme: 'dynamic-theme',
21-
language: container.title.toLowerCase(),
22+
theme: 'dynamic-theme', // this is a theme with create that we update dynamically when the user change the theme
23+
language: container.title.toLowerCase(), // we use the title of the container to set the language
2224
readOnly: true,
2325
scrollbar: {
2426
vertical: 'visible'
@@ -38,6 +40,8 @@ export class TextDisplay {
3840
editor.layout();
3941
});
4042
subscribeOutputChange((arg) => {
43+
// subscribeOutputChange fire when there is a new 'output',
44+
// the title of our container correspond to the type of output it display.
4145
if (arg.outputType == container.title) {
4246
editor.setValue(arg.value);
4347
}

src/Draco.Editor.Web/app/ts/LayoutComponents/TextInput.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as monaco from 'monaco-editor/esm/vs/editor/editor.main.js';
22
import { ComponentContainer } from 'golden-layout';
33
import { inputCode } from '../app.js';
44

5+
// this setup a monaco editor to input the code.
56
export class TextInput {
67
static editors = [];
78

@@ -16,7 +17,7 @@ export class TextInput {
1617
const editor = monaco.editor.create(div, {
1718
value: inputCode,
1819
language: 'draco',
19-
theme: 'dynamic-theme',
20+
theme: 'dynamic-theme', // this is a theme with create that we update dynamically when the user change the theme
2021
scrollbar: {
2122
vertical: 'visible'
2223
},

src/Draco.Editor.Web/app/ts/app.ts

+12-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { TextInput } from './LayoutComponents/TextInput.js';
88
import { loadThemes } from './loadThemes.js';
99
import { Settings } from './LayoutComponents/Settings.js';
1010

11+
// this file is the entry point.
12+
1113
function updateHash(code: string) {
1214
// setting the URL Hash with the state of the editor.
1315
// Doing this before invoking DotNet will allow sharing hard crash.
@@ -18,8 +20,9 @@ function updateHash(code: string) {
1820
buffer.set(compressed, 1);
1921
history.replaceState(undefined, undefined, '#' + fromBase64ToBase64URL(toBase64(buffer)));
2022
}
21-
// This file is run on page load.
22-
// This run before blazor load, and will tell blazor to start.
23+
24+
// that's because monaco doesn't help you when you have a bundler, you have to do it by hand.
25+
// https://github.com/microsoft/monaco-editor/blob/main/samples/browser-esm-esbuild/index.js
2326

2427
self.MonacoEnvironment = {
2528
// Web Workers need to start a new script, by url.
@@ -29,7 +32,7 @@ self.MonacoEnvironment = {
2932
}
3033
};
3134

32-
const hash = window.location.hash.slice(1);
35+
const hash = window.location.hash.slice(1); // retrieve hash, which contain the stored code.
3336
export let inputCode = `import System.Console;
3437
3538
func main() {
@@ -109,12 +112,13 @@ goldenLayout.registerComponentConstructor('Settings', Settings);
109112

110113
goldenLayout.loadLayout(config);
111114
const inputEditor = TextInput.editors[0];
112-
inputEditor.getModel().onDidChangeContent(() => {
115+
inputEditor.getModel().onDidChangeContent(() => { // when the input editor content change...
113116
const code = inputEditor.getModel().getValue();
114-
setCode(code);
115-
updateHash(code);
117+
setCode(code); // this sends the code to the dotnet worker.
118+
updateHash(code); // and update the hash of the URL.
116119
});
117-
subscribeOutputChange((arg) => {
120+
121+
subscribeOutputChange((arg) => { // and this piece of code that have nothing to do here, update the xterm terminal with what the dotnet worker sent.
118122
if (arg.outputType == 'stdout') {
119123
if (arg.clear) {
120124
StdOut.terminals[0].reset();
@@ -123,5 +127,5 @@ subscribeOutputChange((arg) => {
123127
}
124128
});
125129

126-
loadThemes();
130+
loadThemes(); // this does the black magic in order to have vscode theme on monaco.
127131
initDotnetWorkers(inputCode);

src/Draco.Editor.Web/app/ts/cache.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import { blobToBase64 } from './helpers.js';
22
import { buildDate } from './metadata.js';
33

4+
// this is a dll cache to avoid downloading the same assembly multiple times.
5+
46
const elements = [];
57

8+
// when you download assemblies, we have a progress bar to show the download progress.
9+
// to be honest it also have a cool factor and i like it.
10+
// it also make people patient to have a progress bar :p.
611
export function getDownloadViewElement() {
712
const downloadViewElement = document.createElement('div');
813
downloadViewElement.classList.add('monaco-editor');
@@ -15,6 +20,10 @@ export function getDownloadViewElement() {
1520
return downloadViewElement;
1621
}
1722

23+
// dotnet js would download every single assembly every single time,
24+
// it provided a way to have custom logic back thenn, but it simply didnt worked, and it was not documented at all.
25+
// a way i found by browsing the code, is that if you sets the buffer field, it wont download the dll and use the buffer as the dll.
26+
// so we take the dotnet js config, download/pull from cache all the dll listed in it, and set the buffer field.
1827
export async function downloadAssemblies(cfg: unknown) {
1928
await ensureCacheUpToDate();
2029
const assets = cfg['assets'];
@@ -91,10 +100,6 @@ async function downloadAssembly(dlPath: string, asset: unknown): Promise<void> {
91100
});
92101
}
93102

94-
function wait(milliseconds) {
95-
return new Promise(resolve => setTimeout(resolve, milliseconds));
96-
}
97-
98103
async function progressFetch(url: string, onProgress: (loaded: number, total: number) => void): Promise<Blob> {
99104
const response = await fetch(url);
100105
const contentLength = response.headers.get('content-length');

src/Draco.Editor.Web/app/ts/dotnet.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { downloadAssemblies } from './cache.js';
2+
// this file handle the communication with the .NET worker.
23

34
const compilerWorker = new Worker('worker.js'); // first thing: we start the worker so it loads in parallel.
45
let runtimeWorker: Worker | undefined;
@@ -11,21 +12,30 @@ compilerWorker.onmessage = async (ev) => {
1112
message: string;
1213
};
1314
switch (msg.type) {
14-
case 'setOutputText': {
15+
case 'setOutputText': { // the worker is sending us some output
1516
const parsed = JSON.parse(msg.message);
1617
onOutputChange(parsed['OutputType'], parsed['Text'], true);
1718
break;
1819
}
19-
case 'runtimeAssembly': {
20+
case 'runtimeAssembly': { // the worker sent a compiled dll to run.
2021
if (runtimeWorker != undefined) {
2122
runtimeWorker.terminate();
2223
}
2324
onOutputChange('stdout', 'Loading script\'s .NET Runtime...', true);
25+
2426
runtimeWorker = new Worker('worker.js');
27+
28+
// our small dotnet wrapper around the compiler:
29+
// listed all the assemblies that this dll will need
30+
// and built a json that mono wasm understand to run a .NET dll.
31+
32+
// the small wrapepr part generate an ideal config file, this script here bring the reality back to it: dotnet js api is far from being usable.
2533
const cfg = JSON.parse(msg.message);
2634
console.log('Starting worker with boot config:');
27-
cfg['disableInterop'] = true;
35+
36+
cfg['disableInterop'] = true; // we don't do js-dotnet interop in user dlls for now.
2837
const assets = cfg['assets'];
38+
// for some reason, mono js really need these two files. why does it doesn't do itself ? good question.
2939
assets.unshift({
3040
name: jsModuleNativeName,
3141
behavior: 'js-module-native',
@@ -34,8 +44,8 @@ compilerWorker.onmessage = async (ev) => {
3444
name: jsModuleRuntimeName,
3545
behavior: 'js-module-runtime',
3646
});
37-
await downloadAssemblies(cfg);
38-
runtimeWorker.postMessage(cfg);
47+
await downloadAssemblies(cfg); // this download the assemblies by ourself, i explain in the function why we do that.
48+
runtimeWorker.postMessage(cfg); // we send the config to the worker.
3949
let shouldClean = true;
4050
runtimeWorker.onmessage = (e) => {
4151
const runtimeMsg = e.data as {
@@ -84,9 +94,14 @@ export function unsubscribeOutputChange(listener: (arg: { outputType: string; va
8494
}
8595

8696
export async function initDotnetWorkers(initCode: string) {
97+
// msbuild generated a shiny file for dotnet js, it's called blazor even if we don't use blazor
8798
const cfg = await (await fetch('_framework/blazor.boot.json')).json();
99+
// dotnet js doesn't cache dlls, so we again use our download mechanism to cache them.
100+
// also, there is multiple way to structure this config file, because why not.
101+
// and the way msbuild generate this file, don't allow to use the buffer trick explained in cache.ts.
102+
// so I recreate the config file, using this config structure that i understand.
88103
console.log(cfg);
89-
const assets: unknown[] = Object.keys(cfg.resources.assembly).map(
104+
const assets: unknown[] = Object.keys(cfg.resources.assembly).map( // this rewrite the msbuild generated config into the config structure i use.
90105
s => {
91106
return {
92107
'behavior': 'assembly',
@@ -98,6 +113,11 @@ export async function initDotnetWorkers(initCode: string) {
98113
'behavior': 'dotnetwasm',
99114
'name': 'dotnet.native.wasm'
100115
});
116+
117+
// if i remember correctly, the file structure they use have dedicated fields to specify theses 2 files
118+
// since we don't use their file strucutre, we have to add them manually.
119+
// Luckily, we can just pull the value from the msbuild generated file.
120+
101121
jsModuleNativeName = Object.keys(cfg['resources']['jsModuleNative'])[0];
102122
assets.unshift({
103123
name: jsModuleNativeName,

src/Draco.Editor.Web/app/ts/loadThemes.ts

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { Registry } from 'monaco-textmate';
55
import { wireTmGrammars } from 'monaco-editor-textmate';
66
import grammarDefinition from '../generated/draco.tmLanguage.json';
77
import { isDarkMode } from './helpers.js';
8+
9+
// dark magic pulled out in an evening.
810
export async function loadThemes() {
911
const wasmPromise = loadWASM(onigasmWasm.buffer); // https://www.npmjs.com/package/onigasm;
1012

0 commit comments

Comments
 (0)