wasm-idle 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/clang/app.js +17 -5
- package/dist/clang/index.d.ts +6 -0
- package/dist/clang/index.js +135 -19
- package/dist/index.d.ts +1 -0
- package/dist/playground/assetBridge.d.ts +20 -0
- package/dist/playground/assetBridge.js +192 -0
- package/dist/playground/assets.d.ts +32 -0
- package/dist/playground/assets.js +97 -0
- package/dist/playground/clang.d.ts +6 -1
- package/dist/playground/clang.js +13 -2
- package/dist/playground/index.js +3 -1
- package/dist/playground/java.d.ts +7 -2
- package/dist/playground/java.js +31 -5
- package/dist/playground/javaStdin.js +235 -2
- package/dist/playground/options.d.ts +9 -0
- package/dist/playground/options.js +15 -1
- package/dist/playground/python.d.ts +6 -1
- package/dist/playground/python.js +38 -5
- package/dist/playground/sandbox.d.ts +2 -1
- package/dist/playground/worker/assets.d.ts +12 -0
- package/dist/playground/worker/assets.js +257 -0
- package/dist/playground/worker/clang.js +11 -3
- package/dist/playground/worker/java.js +15 -35
- package/dist/playground/worker/python.js +13 -33
- package/dist/terminal/Terminal.svelte +57 -7
- package/dist/terminal/Terminal.svelte.d.ts +2 -0
- package/dist/terminal/theme.js +2 -2
- package/dist/terminal/types.d.ts +1 -0
- package/package.json +11 -11
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CompilerDiagnostic, DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
|
|
2
|
+
import type { PlaygroundRuntimeAssets } from './assets';
|
|
2
3
|
import type { Writable } from 'svelte/store';
|
|
3
4
|
type ProgressLike = Writable<number> | {
|
|
4
5
|
set?: (value: number) => void;
|
|
@@ -6,7 +7,7 @@ type ProgressLike = Writable<number> | {
|
|
|
6
7
|
export interface Sandbox {
|
|
7
8
|
constructor: any;
|
|
8
9
|
eof: () => void;
|
|
9
|
-
load: (
|
|
10
|
+
load: (runtimeAssets?: string | PlaygroundRuntimeAssets, code?: string, log?: boolean, args?: string[], options?: SandboxExecutionOptions, progress?: ProgressLike) => Promise<void>;
|
|
10
11
|
run: (code: string, prepare: boolean, log?: boolean, prog?: ProgressLike, args?: string[], options?: SandboxExecutionOptions) => Promise<boolean | string>;
|
|
11
12
|
terminate: () => void;
|
|
12
13
|
clear: () => Promise<void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface WorkerRuntimeAssetConfig {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
useAssetBridge: boolean;
|
|
4
|
+
}
|
|
5
|
+
interface LoadedWorkerAsset {
|
|
6
|
+
bytes: Uint8Array;
|
|
7
|
+
mimeType?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function configureWorkerRuntimeAssets(config: WorkerRuntimeAssetConfig | null): void;
|
|
10
|
+
export declare function handleWorkerAssetMessage(data: any): boolean;
|
|
11
|
+
export declare function loadWorkerRuntimeAsset(asset: string): Promise<LoadedWorkerAsset>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
const decoder = new TextDecoder();
|
|
2
|
+
const originalFetch = globalThis.fetch.bind(globalThis);
|
|
3
|
+
const NativeXMLHttpRequest = globalThis.XMLHttpRequest;
|
|
4
|
+
let activeConfig = null;
|
|
5
|
+
let interceptorsInstalled = false;
|
|
6
|
+
let nextAssetRequestId = 0;
|
|
7
|
+
const pendingAssetRequests = new Map();
|
|
8
|
+
const responseBuffer = (bytes) => {
|
|
9
|
+
const buffer = bytes.buffer;
|
|
10
|
+
return bytes.byteOffset === 0 && bytes.byteLength === buffer.byteLength
|
|
11
|
+
? buffer
|
|
12
|
+
: buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
13
|
+
};
|
|
14
|
+
const trackedAssetUrl = (input) => {
|
|
15
|
+
if (!activeConfig)
|
|
16
|
+
return null;
|
|
17
|
+
if (typeof input === 'string')
|
|
18
|
+
return new URL(input, activeConfig.baseUrl).href;
|
|
19
|
+
if (input instanceof URL)
|
|
20
|
+
return input.href;
|
|
21
|
+
return input.url;
|
|
22
|
+
};
|
|
23
|
+
const trackedAssetName = (url) => {
|
|
24
|
+
if (!activeConfig || !url.startsWith(activeConfig.baseUrl))
|
|
25
|
+
return null;
|
|
26
|
+
return url.slice(activeConfig.baseUrl.length);
|
|
27
|
+
};
|
|
28
|
+
const isTrackedAssetUrl = (url) => trackedAssetName(url) !== null;
|
|
29
|
+
const loadAssetFromBridge = async (asset) => {
|
|
30
|
+
const id = ++nextAssetRequestId;
|
|
31
|
+
return await new Promise((resolve, reject) => {
|
|
32
|
+
pendingAssetRequests.set(id, { resolve, reject });
|
|
33
|
+
self.postMessage({
|
|
34
|
+
assetRequest: {
|
|
35
|
+
id,
|
|
36
|
+
asset
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
const loadAssetFromUrl = async (url, asset) => {
|
|
42
|
+
const response = await originalFetch(url);
|
|
43
|
+
if (!response.ok)
|
|
44
|
+
throw new Error(`Failed to load ${asset}: ${response.status}`);
|
|
45
|
+
const total = Number(response.headers.get('content-length') || 0) || undefined;
|
|
46
|
+
const mimeType = response.headers.get('content-type') || undefined;
|
|
47
|
+
if (!response.body) {
|
|
48
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
49
|
+
self.postMessage({
|
|
50
|
+
assetProgress: {
|
|
51
|
+
asset,
|
|
52
|
+
loaded: bytes.byteLength,
|
|
53
|
+
total: total ?? bytes.byteLength
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return { bytes, mimeType };
|
|
57
|
+
}
|
|
58
|
+
const reader = response.body.getReader();
|
|
59
|
+
let receivedLength = 0;
|
|
60
|
+
const chunks = [];
|
|
61
|
+
while (true) {
|
|
62
|
+
const { done, value } = await reader.read();
|
|
63
|
+
if (done)
|
|
64
|
+
break;
|
|
65
|
+
if (!value)
|
|
66
|
+
continue;
|
|
67
|
+
const chunk = Uint8Array.from(value);
|
|
68
|
+
chunks.push(chunk);
|
|
69
|
+
receivedLength += chunk.byteLength;
|
|
70
|
+
self.postMessage({
|
|
71
|
+
assetProgress: {
|
|
72
|
+
asset,
|
|
73
|
+
loaded: receivedLength,
|
|
74
|
+
total
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const bytes = new Uint8Array(receivedLength);
|
|
79
|
+
let position = 0;
|
|
80
|
+
for (const chunk of chunks) {
|
|
81
|
+
bytes.set(chunk, position);
|
|
82
|
+
position += chunk.byteLength;
|
|
83
|
+
}
|
|
84
|
+
self.postMessage({
|
|
85
|
+
assetProgress: {
|
|
86
|
+
asset,
|
|
87
|
+
loaded: receivedLength,
|
|
88
|
+
total: total ?? receivedLength
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return { bytes, mimeType };
|
|
92
|
+
};
|
|
93
|
+
async function loadTrackedAsset(url) {
|
|
94
|
+
const asset = trackedAssetName(url);
|
|
95
|
+
if (!asset || !activeConfig)
|
|
96
|
+
throw new Error('Untracked runtime asset request');
|
|
97
|
+
return activeConfig.useAssetBridge
|
|
98
|
+
? await loadAssetFromBridge(asset)
|
|
99
|
+
: await loadAssetFromUrl(url, asset);
|
|
100
|
+
}
|
|
101
|
+
function createTrackedResponse(asset) {
|
|
102
|
+
return new Response(responseBuffer(asset.bytes), {
|
|
103
|
+
status: 200,
|
|
104
|
+
headers: asset.mimeType ? { 'Content-Type': asset.mimeType } : undefined
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function installTrackedFetch() {
|
|
108
|
+
if (typeof NativeXMLHttpRequest === 'undefined')
|
|
109
|
+
return;
|
|
110
|
+
class RuntimeAssetXMLHttpRequest {
|
|
111
|
+
responseType = '';
|
|
112
|
+
response = null;
|
|
113
|
+
responseText = '';
|
|
114
|
+
readyState = 0;
|
|
115
|
+
status = 0;
|
|
116
|
+
statusText = '';
|
|
117
|
+
timeout = 0;
|
|
118
|
+
withCredentials = false;
|
|
119
|
+
onload = null;
|
|
120
|
+
onerror = null;
|
|
121
|
+
onprogress = null;
|
|
122
|
+
onreadystatechange = null;
|
|
123
|
+
native = null;
|
|
124
|
+
url = '';
|
|
125
|
+
open(method, url) {
|
|
126
|
+
const resolvedUrl = trackedAssetUrl(url);
|
|
127
|
+
if (!resolvedUrl || !isTrackedAssetUrl(resolvedUrl)) {
|
|
128
|
+
const nativeUrl = resolvedUrl || (url instanceof URL ? url.href : String(url));
|
|
129
|
+
this.native = new NativeXMLHttpRequest();
|
|
130
|
+
this.native.responseType = this.responseType;
|
|
131
|
+
this.native.timeout = this.timeout;
|
|
132
|
+
this.native.withCredentials = this.withCredentials;
|
|
133
|
+
this.native.onload = (event) => {
|
|
134
|
+
this.response = this.native?.response;
|
|
135
|
+
this.responseText = this.native?.responseText || '';
|
|
136
|
+
this.readyState = this.native?.readyState || 0;
|
|
137
|
+
this.status = this.native?.status || 0;
|
|
138
|
+
this.statusText = this.native?.statusText || '';
|
|
139
|
+
this.onreadystatechange?.call(this, event);
|
|
140
|
+
this.onload?.call(this, event);
|
|
141
|
+
};
|
|
142
|
+
this.native.onerror = (event) => {
|
|
143
|
+
this.readyState = this.native?.readyState || 4;
|
|
144
|
+
this.status = this.native?.status || 0;
|
|
145
|
+
this.statusText = this.native?.statusText || '';
|
|
146
|
+
this.onreadystatechange?.call(this, event);
|
|
147
|
+
this.onerror?.call(this, event);
|
|
148
|
+
};
|
|
149
|
+
this.native.onprogress = (event) => {
|
|
150
|
+
this.onprogress?.call(this, event);
|
|
151
|
+
};
|
|
152
|
+
this.native.onreadystatechange = (event) => {
|
|
153
|
+
this.readyState = this.native?.readyState || 0;
|
|
154
|
+
this.onreadystatechange?.call(this, event);
|
|
155
|
+
};
|
|
156
|
+
this.native.open(method, nativeUrl);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this.url = resolvedUrl;
|
|
160
|
+
this.readyState = 1;
|
|
161
|
+
this.onreadystatechange?.call(this, new ProgressEvent('readystatechange'));
|
|
162
|
+
}
|
|
163
|
+
setRequestHeader(name, value) {
|
|
164
|
+
this.native?.setRequestHeader(name, value);
|
|
165
|
+
}
|
|
166
|
+
async send(body) {
|
|
167
|
+
if (this.native) {
|
|
168
|
+
this.native.send(body);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
const loaded = await loadTrackedAsset(this.url);
|
|
173
|
+
const buffer = responseBuffer(loaded.bytes);
|
|
174
|
+
this.status = 200;
|
|
175
|
+
this.statusText = 'OK';
|
|
176
|
+
this.readyState = 4;
|
|
177
|
+
if (this.responseType === 'arraybuffer') {
|
|
178
|
+
this.response = buffer;
|
|
179
|
+
}
|
|
180
|
+
else if (this.responseType === 'blob') {
|
|
181
|
+
this.response = new Blob([buffer], {
|
|
182
|
+
type: loaded.mimeType || 'application/octet-stream'
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const text = decoder.decode(loaded.bytes);
|
|
187
|
+
this.responseText = text;
|
|
188
|
+
this.response = text;
|
|
189
|
+
}
|
|
190
|
+
const progressEvent = new ProgressEvent('progress', {
|
|
191
|
+
lengthComputable: true,
|
|
192
|
+
loaded: loaded.bytes.byteLength,
|
|
193
|
+
total: loaded.bytes.byteLength
|
|
194
|
+
});
|
|
195
|
+
this.onprogress?.call(this, progressEvent);
|
|
196
|
+
this.onreadystatechange?.call(this, new ProgressEvent('readystatechange'));
|
|
197
|
+
this.onload?.call(this, new ProgressEvent('load'));
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
this.readyState = 4;
|
|
201
|
+
this.status = 0;
|
|
202
|
+
this.statusText = error instanceof Error ? error.message : String(error);
|
|
203
|
+
this.onreadystatechange?.call(this, new ProgressEvent('readystatechange'));
|
|
204
|
+
this.onerror?.call(this, new ProgressEvent('error'));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
abort() {
|
|
208
|
+
this.native?.abort();
|
|
209
|
+
}
|
|
210
|
+
getAllResponseHeaders() {
|
|
211
|
+
return this.native?.getAllResponseHeaders() || '';
|
|
212
|
+
}
|
|
213
|
+
getResponseHeader(name) {
|
|
214
|
+
return this.native?.getResponseHeader(name) || null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
globalThis.XMLHttpRequest = RuntimeAssetXMLHttpRequest;
|
|
218
|
+
}
|
|
219
|
+
function installRuntimeAssetInterceptors() {
|
|
220
|
+
if (interceptorsInstalled)
|
|
221
|
+
return;
|
|
222
|
+
interceptorsInstalled = true;
|
|
223
|
+
globalThis.fetch = (async (input, init) => {
|
|
224
|
+
const resolvedUrl = trackedAssetUrl(input);
|
|
225
|
+
if (!resolvedUrl || !isTrackedAssetUrl(resolvedUrl))
|
|
226
|
+
return originalFetch(input, init);
|
|
227
|
+
return createTrackedResponse(await loadTrackedAsset(resolvedUrl));
|
|
228
|
+
});
|
|
229
|
+
installTrackedFetch();
|
|
230
|
+
}
|
|
231
|
+
export function configureWorkerRuntimeAssets(config) {
|
|
232
|
+
activeConfig = config;
|
|
233
|
+
installRuntimeAssetInterceptors();
|
|
234
|
+
}
|
|
235
|
+
export function handleWorkerAssetMessage(data) {
|
|
236
|
+
const response = data?.assetResponse;
|
|
237
|
+
if (!response)
|
|
238
|
+
return false;
|
|
239
|
+
const pending = pendingAssetRequests.get(response.id);
|
|
240
|
+
if (!pending)
|
|
241
|
+
return true;
|
|
242
|
+
pendingAssetRequests.delete(response.id);
|
|
243
|
+
if (!response.ok) {
|
|
244
|
+
pending.reject(new Error(response.error || 'Runtime asset request failed'));
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
pending.resolve({
|
|
248
|
+
bytes: new Uint8Array(response.bytes),
|
|
249
|
+
mimeType: response.mimeType || undefined
|
|
250
|
+
});
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
export async function loadWorkerRuntimeAsset(asset) {
|
|
254
|
+
if (!activeConfig)
|
|
255
|
+
throw new Error('Runtime asset config unavailable');
|
|
256
|
+
return await loadTrackedAsset(new URL(asset, activeConfig.baseUrl).href);
|
|
257
|
+
}
|
|
@@ -17,7 +17,7 @@ async function loadClang(path, log) {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
self.onmessage = async (event) => {
|
|
20
|
-
const { code, buffer, debugBuffer, load, interrupt, log, path, prepare,
|
|
20
|
+
const { code, buffer, debugBuffer, load, interrupt, log, path, prepare, language, compileArgs, programArgs, cppVersion, cVersion, debug, breakpoints, pauseOnEntry } = event.data;
|
|
21
21
|
if (load) {
|
|
22
22
|
await loadClang(path, log);
|
|
23
23
|
postMessage({ load: true });
|
|
@@ -28,7 +28,11 @@ self.onmessage = async (event) => {
|
|
|
28
28
|
interruptBufferClang = new Uint8Array(interrupt);
|
|
29
29
|
try {
|
|
30
30
|
await clang.compileLink(code, {
|
|
31
|
-
|
|
31
|
+
language,
|
|
32
|
+
compileArgs,
|
|
33
|
+
programArgs,
|
|
34
|
+
cppVersion,
|
|
35
|
+
cVersion,
|
|
32
36
|
debug,
|
|
33
37
|
breakpoints,
|
|
34
38
|
pauseOnEntry,
|
|
@@ -48,7 +52,11 @@ self.onmessage = async (event) => {
|
|
|
48
52
|
interruptBufferClang = new Uint8Array(interrupt);
|
|
49
53
|
try {
|
|
50
54
|
await clang.compileLinkRun(code, {
|
|
51
|
-
|
|
55
|
+
language,
|
|
56
|
+
compileArgs,
|
|
57
|
+
programArgs,
|
|
58
|
+
cppVersion,
|
|
59
|
+
cVersion,
|
|
52
60
|
debug,
|
|
53
61
|
breakpoints,
|
|
54
62
|
pauseOnEntry,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { prepareJavaStdinInjection } from '../javaStdin';
|
|
2
2
|
import { waitForBufferedStdin } from '../stdinBuffer';
|
|
3
|
-
import {
|
|
3
|
+
import { configureWorkerRuntimeAssets, handleWorkerAssetMessage, loadWorkerRuntimeAsset } from './assets';
|
|
4
4
|
let compilerLib = null;
|
|
5
5
|
let compiler = null;
|
|
6
6
|
let runtimeLoad = null;
|
|
@@ -15,18 +15,8 @@ let compiledStdin = '';
|
|
|
15
15
|
let compiledMainClass = '';
|
|
16
16
|
let compiledWasm = null;
|
|
17
17
|
let currentSourcePath = '';
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
if (!isGzip(bytes))
|
|
21
|
-
return bytes;
|
|
22
|
-
if (typeof DecompressionStream === 'undefined') {
|
|
23
|
-
throw new Error('Generated WebAssembly is gzip-compressed, but DecompressionStream is unavailable');
|
|
24
|
-
}
|
|
25
|
-
const stream = new Blob([Uint8Array.from(bytes)])
|
|
26
|
-
.stream()
|
|
27
|
-
.pipeThrough(new DecompressionStream('gzip'));
|
|
28
|
-
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
29
|
-
};
|
|
18
|
+
const decoder = new TextDecoder();
|
|
19
|
+
const toInt8Array = (bytes) => new Int8Array(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
|
30
20
|
const flushStdout = () => {
|
|
31
21
|
if (!stdoutBuffer)
|
|
32
22
|
return;
|
|
@@ -50,41 +40,31 @@ const pushStderr = (charCode) => {
|
|
|
50
40
|
flushStderr();
|
|
51
41
|
};
|
|
52
42
|
self.addEventListener('message', async (event) => {
|
|
53
|
-
|
|
43
|
+
if (handleWorkerAssetMessage(event.data))
|
|
44
|
+
return;
|
|
45
|
+
const { load, assets, buffer, code, prepare, args = [], stdin = '' } = event.data;
|
|
54
46
|
try {
|
|
55
47
|
if (load) {
|
|
48
|
+
const runtimeAssets = assets;
|
|
49
|
+
configureWorkerRuntimeAssets(runtimeAssets || null);
|
|
50
|
+
const baseUrl = runtimeAssets?.baseUrl || '';
|
|
56
51
|
if (!compiler || loadedBaseUrl !== baseUrl) {
|
|
57
52
|
loadedBaseUrl = baseUrl;
|
|
58
|
-
const runtimeSource = await
|
|
59
|
-
if (!response.ok)
|
|
60
|
-
throw new Error(`Failed to load TeaVM compiler runtime: ${response.status}`);
|
|
61
|
-
return response.text();
|
|
62
|
-
});
|
|
53
|
+
const runtimeSource = decoder.decode((await loadWorkerRuntimeAsset('compiler.wasm-runtime.js')).bytes);
|
|
63
54
|
const runtimeUrl = URL.createObjectURL(new Blob([runtimeSource], { type: 'text/javascript;charset=utf-8' }));
|
|
64
55
|
const runtimeModule = await import(/* @vite-ignore */ runtimeUrl);
|
|
56
|
+
URL.revokeObjectURL(runtimeUrl);
|
|
65
57
|
const runtimeLoadFn = runtimeModule.load;
|
|
66
58
|
runtimeLoad = runtimeLoadFn;
|
|
67
|
-
const compilerWasm = await
|
|
68
|
-
if (!response.ok)
|
|
69
|
-
throw new Error(`Failed to load TeaVM compiler wasm: ${response.status}`);
|
|
70
|
-
return await maybeDecompressWasm(new Uint8Array(await response.arrayBuffer()));
|
|
71
|
-
});
|
|
59
|
+
const compilerWasm = (await loadWorkerRuntimeAsset('compiler.wasm')).bytes;
|
|
72
60
|
const compilerModule = await runtimeLoadFn(compilerWasm, {
|
|
73
61
|
stackDeobfuscator: { enabled: false }
|
|
74
62
|
});
|
|
75
63
|
compilerLib = compilerModule.exports;
|
|
76
64
|
compiler = compilerLib.createCompiler();
|
|
77
65
|
const [sdk, runtimeClasslib] = await Promise.all([
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
throw new Error(`Failed to load TeaVM SDK: ${response.status}`);
|
|
81
|
-
return new Int8Array(await response.arrayBuffer());
|
|
82
|
-
}),
|
|
83
|
-
fetch(resolveTeaVmAssetUrl(baseUrl, 'runtime-classlib-teavm.bin')).then(async (response) => {
|
|
84
|
-
if (!response.ok)
|
|
85
|
-
throw new Error(`Failed to load TeaVM runtime classlib: ${response.status}`);
|
|
86
|
-
return new Int8Array(await response.arrayBuffer());
|
|
87
|
-
})
|
|
66
|
+
loadWorkerRuntimeAsset('compile-classlib-teavm.bin').then(({ bytes }) => toInt8Array(bytes)),
|
|
67
|
+
loadWorkerRuntimeAsset('runtime-classlib-teavm.bin').then(({ bytes }) => toInt8Array(bytes))
|
|
88
68
|
]);
|
|
89
69
|
compiler.setSdk(sdk);
|
|
90
70
|
compiler.setTeaVMClasslib(runtimeClasslib);
|
|
@@ -185,7 +165,7 @@ self.addEventListener('message', async (event) => {
|
|
|
185
165
|
compiledCode = code;
|
|
186
166
|
compiledStdin = stdinInjection.stdinCacheKey;
|
|
187
167
|
compiledMainClass = mainClass;
|
|
188
|
-
compiledWasm =
|
|
168
|
+
compiledWasm = new Uint8Array(compiler.getWebAssemblyOutputFile('app.wasm'));
|
|
189
169
|
}
|
|
190
170
|
if (prepare) {
|
|
191
171
|
self.postMessage({ results: true });
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { flushQueuedStdin, readBufferedStdin, waitForBufferedStdin } from '../stdinBuffer';
|
|
2
|
+
import { configureWorkerRuntimeAssets, handleWorkerAssetMessage } from './assets';
|
|
2
3
|
self.document = {
|
|
3
4
|
querySelector() {
|
|
4
5
|
return null;
|
|
@@ -7,8 +8,7 @@ self.document = {
|
|
|
7
8
|
return [];
|
|
8
9
|
}
|
|
9
10
|
};
|
|
10
|
-
let stdinBufferPyodide, debugBufferPyodide, watchBufferPyodide, watchResultBufferPyodide, interruptBufferPyodide, pyodide,
|
|
11
|
-
let jungolRobotReady = false;
|
|
11
|
+
let stdinBufferPyodide, debugBufferPyodide, watchBufferPyodide, watchResultBufferPyodide, interruptBufferPyodide, pyodide, baseUrl = '', packageBaseUrl = '';
|
|
12
12
|
const imageHook = `
|
|
13
13
|
if not globals().get("__wasm_idle_img_inited__", False):
|
|
14
14
|
globals()["__wasm_idle_img_inited__"] = True
|
|
@@ -157,33 +157,12 @@ async function loadPyodide(path) {
|
|
|
157
157
|
if (pyodide)
|
|
158
158
|
return;
|
|
159
159
|
const { loadPyodide } = await import('pyodide');
|
|
160
|
-
pyodide = await loadPyodide({ indexURL: path
|
|
161
|
-
packageBaseUrl = path
|
|
160
|
+
pyodide = await loadPyodide({ indexURL: path });
|
|
161
|
+
packageBaseUrl = path;
|
|
162
162
|
const setCdnUrl = pyodide?.setCdnUrl;
|
|
163
163
|
if (typeof setCdnUrl === 'function')
|
|
164
164
|
setCdnUrl(packageBaseUrl);
|
|
165
165
|
}
|
|
166
|
-
async function ensureRobotJungol() {
|
|
167
|
-
if (jungolRobotReady || !pyodide)
|
|
168
|
-
return;
|
|
169
|
-
try {
|
|
170
|
-
const res = await fetch(path + '/jungol-robot/jungol_robot.zip');
|
|
171
|
-
if (!res.ok)
|
|
172
|
-
throw new Error('jungol-robot zip not found');
|
|
173
|
-
const data = await res.arrayBuffer();
|
|
174
|
-
const siteRaw = pyodide.runPython('import site; site.getsitepackages()[0]');
|
|
175
|
-
const sitePath = typeof siteRaw === 'string' ? siteRaw : siteRaw.toString();
|
|
176
|
-
const unpackArchive = pyodide?.unpackArchive;
|
|
177
|
-
if (typeof unpackArchive !== 'function')
|
|
178
|
-
throw new Error('pyodide.unpackArchive unavailable');
|
|
179
|
-
unpackArchive(data, 'zip', sitePath);
|
|
180
|
-
pyodide.runPython('import importlib; importlib.invalidate_caches()');
|
|
181
|
-
jungolRobotReady = true;
|
|
182
|
-
}
|
|
183
|
-
catch (e) {
|
|
184
|
-
console.warn('jungol-robot preload failed', e);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
166
|
async function loadPackages(code) {
|
|
188
167
|
if (!code)
|
|
189
168
|
return;
|
|
@@ -203,20 +182,22 @@ async function loadPackages(code) {
|
|
|
203
182
|
}
|
|
204
183
|
}
|
|
205
184
|
self.onmessage = async (event) => {
|
|
206
|
-
|
|
185
|
+
if (handleWorkerAssetMessage(event.data))
|
|
186
|
+
return;
|
|
187
|
+
const { code, buffer, debugBuffer, watchBuffer, watchResultBuffer, load, interrupt, assets, prepare, debug = false, breakpoints = [], pauseOnEntry = false } = event.data;
|
|
207
188
|
if (load) {
|
|
208
|
-
|
|
189
|
+
const runtimeAssets = assets;
|
|
190
|
+
baseUrl = runtimeAssets?.baseUrl || baseUrl;
|
|
191
|
+
configureWorkerRuntimeAssets(runtimeAssets || null);
|
|
209
192
|
postMessage({ output: 'Loading Pyodide...' });
|
|
210
|
-
await loadPyodide(
|
|
211
|
-
await ensureRobotJungol();
|
|
193
|
+
await loadPyodide(baseUrl);
|
|
212
194
|
postMessage({ output: ' Done.\n\r' });
|
|
213
195
|
postMessage({ load: true });
|
|
214
196
|
}
|
|
215
197
|
else if (prepare) {
|
|
216
198
|
postMessage({ output: 'Loading packages...' });
|
|
217
199
|
try {
|
|
218
|
-
await loadPyodide(
|
|
219
|
-
await ensureRobotJungol();
|
|
200
|
+
await loadPyodide(baseUrl);
|
|
220
201
|
await loadPackages(code);
|
|
221
202
|
postMessage({ output: ' Done.\n\r' });
|
|
222
203
|
self.postMessage({ results: true });
|
|
@@ -226,8 +207,7 @@ self.onmessage = async (event) => {
|
|
|
226
207
|
}
|
|
227
208
|
}
|
|
228
209
|
else if (code) {
|
|
229
|
-
await loadPyodide(
|
|
230
|
-
await ensureRobotJungol();
|
|
210
|
+
await loadPyodide(baseUrl);
|
|
231
211
|
await loadPackages(code);
|
|
232
212
|
const ts = Date.now();
|
|
233
213
|
stdinBufferPyodide = new Int32Array(buffer);
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
DebugSessionEvent,
|
|
6
6
|
SandboxExecutionOptions
|
|
7
7
|
} from '../playground/options';
|
|
8
|
+
import type { PlaygroundRuntimeAssets } from '../playground/assets';
|
|
8
9
|
import type { Sandbox } from '../playground/sandbox';
|
|
9
10
|
import type { TerminalControl } from './types';
|
|
10
11
|
import { Theme, registerAllPlugins } from './';
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
interface Props {
|
|
17
18
|
dark?: boolean;
|
|
18
19
|
path?: string;
|
|
20
|
+
runtimeAssets?: string | PlaygroundRuntimeAssets;
|
|
19
21
|
font?: string;
|
|
20
22
|
|
|
21
23
|
onload?: () => void;
|
|
@@ -30,6 +32,7 @@
|
|
|
30
32
|
let {
|
|
31
33
|
dark = false,
|
|
32
34
|
path = '',
|
|
35
|
+
runtimeAssets = undefined,
|
|
33
36
|
font = "'D2 coding', monospace",
|
|
34
37
|
onload,
|
|
35
38
|
onfinish,
|
|
@@ -50,7 +53,22 @@
|
|
|
50
53
|
first = true,
|
|
51
54
|
tc = 0,
|
|
52
55
|
plugin = $state(),
|
|
53
|
-
ll: string | null = null
|
|
56
|
+
ll: string | null = null,
|
|
57
|
+
stopRequested = false;
|
|
58
|
+
|
|
59
|
+
function phaseProgress(
|
|
60
|
+
progress: { set?: (value: number) => void } | undefined,
|
|
61
|
+
start: number,
|
|
62
|
+
end: number
|
|
63
|
+
) {
|
|
64
|
+
if (!progress) return undefined;
|
|
65
|
+
return {
|
|
66
|
+
set(value: number) {
|
|
67
|
+
const clamped = Number.isFinite(value) ? Math.min(Math.max(value, 0), 1) : 0;
|
|
68
|
+
progress.set?.(start + (end - start) * clamped);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
54
72
|
|
|
55
73
|
function wait() {
|
|
56
74
|
return new Promise<void>((r) => {
|
|
@@ -88,10 +106,12 @@
|
|
|
88
106
|
return x;
|
|
89
107
|
})
|
|
90
108
|
.catch((msg) => {
|
|
109
|
+
if (stopRequested) return false;
|
|
91
110
|
term?.write(`\r\n\x1B[1;3;31m${msg}\u001B[?25l`);
|
|
92
111
|
return false;
|
|
93
112
|
})
|
|
94
113
|
.finally(() => {
|
|
114
|
+
stopRequested = false;
|
|
95
115
|
onfinish?.();
|
|
96
116
|
finish = true;
|
|
97
117
|
if (term) term.options.cursorBlink = false;
|
|
@@ -102,6 +122,7 @@
|
|
|
102
122
|
await wait();
|
|
103
123
|
if (!term) return;
|
|
104
124
|
term.options.cursorBlink = blink;
|
|
125
|
+
term.write('\u001B[?25h');
|
|
105
126
|
term.focus();
|
|
106
127
|
|
|
107
128
|
if (!first) term?.write(`\r\n\x1b[0m`);
|
|
@@ -146,11 +167,18 @@
|
|
|
146
167
|
args: string[] = [],
|
|
147
168
|
options: SandboxExecutionOptions = {}
|
|
148
169
|
) {
|
|
170
|
+
const loadProgress = phaseProgress(prog, 0, 0.85);
|
|
171
|
+
const prepareProgress = phaseProgress(prog, 0.85, 0.99);
|
|
149
172
|
await Promise.all([
|
|
150
|
-
initSandbox(language).then(() =>
|
|
173
|
+
initSandbox(language).then(() =>
|
|
174
|
+
sandbox.load(runtimeAssets || path, code, log, args, options, loadProgress)
|
|
175
|
+
),
|
|
151
176
|
initTerm(false)
|
|
152
177
|
]);
|
|
153
|
-
|
|
178
|
+
prepareProgress?.set?.(0);
|
|
179
|
+
return !!(await runSandbox(
|
|
180
|
+
sandbox.run(code, true, log, prepareProgress, args, options)
|
|
181
|
+
));
|
|
154
182
|
},
|
|
155
183
|
async run(
|
|
156
184
|
language: string,
|
|
@@ -161,7 +189,9 @@
|
|
|
161
189
|
options: SandboxExecutionOptions = {}
|
|
162
190
|
) {
|
|
163
191
|
await Promise.all([
|
|
164
|
-
initSandbox(language).then(() =>
|
|
192
|
+
initSandbox(language).then(() =>
|
|
193
|
+
sandbox.load(runtimeAssets || path, code, log, args, options, prog)
|
|
194
|
+
),
|
|
165
195
|
initTerm()
|
|
166
196
|
]);
|
|
167
197
|
return await runSandbox(sandbox.run(code, false, log, prog, args, options));
|
|
@@ -171,6 +201,13 @@
|
|
|
171
201
|
term?.dispose();
|
|
172
202
|
if (sandbox) await sandbox.clear();
|
|
173
203
|
},
|
|
204
|
+
async stop() {
|
|
205
|
+
await wait();
|
|
206
|
+
stopRequested = true;
|
|
207
|
+
finish = true;
|
|
208
|
+
if (sandbox?.kill) sandbox.kill();
|
|
209
|
+
else sandbox?.terminate?.();
|
|
210
|
+
},
|
|
174
211
|
async debugCommand(command: DebugCommand) {
|
|
175
212
|
await wait();
|
|
176
213
|
sandbox.debugCommand?.(command);
|
|
@@ -233,12 +270,19 @@
|
|
|
233
270
|
) {
|
|
234
271
|
appendInputText(e.key);
|
|
235
272
|
}
|
|
236
|
-
} else if ((ev.ctrlKey || ev.metaKey) && ev.key === 'c') {
|
|
273
|
+
} else if ((ev.ctrlKey || ev.metaKey) && ev.key.toLowerCase() === 'c') {
|
|
274
|
+
if (term.hasSelection()) {
|
|
275
|
+
const selectedText = term.getSelection();
|
|
276
|
+
if (selectedText) {
|
|
277
|
+
navigator.clipboard.writeText(selectedText).catch(() => {});
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
237
281
|
sandbox.kill?.();
|
|
238
|
-
} else if ((ev.ctrlKey || ev.metaKey) && ev.key === 'd') {
|
|
282
|
+
} else if ((ev.ctrlKey || ev.metaKey) && ev.key.toLowerCase() === 'd') {
|
|
239
283
|
if (input.length > 0) submitCurrentInput();
|
|
240
284
|
else sandbox.eof?.();
|
|
241
|
-
} else if ((ev.ctrlKey || ev.metaKey) && ev.key === 'v') {
|
|
285
|
+
} else if ((ev.ctrlKey || ev.metaKey) && ev.key.toLowerCase() === 'v') {
|
|
242
286
|
navigator.clipboard.readText().then((text) => {
|
|
243
287
|
applyPastedText(text);
|
|
244
288
|
});
|
|
@@ -272,4 +316,10 @@
|
|
|
272
316
|
width: 100%;
|
|
273
317
|
height: 100%;
|
|
274
318
|
}
|
|
319
|
+
|
|
320
|
+
:global(.xterm),
|
|
321
|
+
:global(.xterm .xterm-viewport),
|
|
322
|
+
:global(.xterm .composition-view) {
|
|
323
|
+
background-color: transparent;
|
|
324
|
+
}
|
|
275
325
|
</style>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { CompilerDiagnostic, DebugSessionEvent } from '../playground/options';
|
|
2
|
+
import type { PlaygroundRuntimeAssets } from '../playground/assets';
|
|
2
3
|
import type { TerminalControl } from './types';
|
|
3
4
|
import '@xterm/xterm/css/xterm.css';
|
|
4
5
|
interface Props {
|
|
5
6
|
dark?: boolean;
|
|
6
7
|
path?: string;
|
|
8
|
+
runtimeAssets?: string | PlaygroundRuntimeAssets;
|
|
7
9
|
font?: string;
|
|
8
10
|
onload?: () => void;
|
|
9
11
|
onfinish?: () => void;
|
package/dist/terminal/theme.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const theme = {
|
|
2
2
|
Tango_Dark: {
|
|
3
3
|
foreground: '#eee',
|
|
4
|
-
background: '
|
|
4
|
+
background: 'transparent',
|
|
5
5
|
cursor: '#FFFFFF',
|
|
6
6
|
cursorAccent: '#ffffff',
|
|
7
7
|
selection: '#ffffff44',
|
|
@@ -24,7 +24,7 @@ const theme = {
|
|
|
24
24
|
},
|
|
25
25
|
Tango_Light: {
|
|
26
26
|
foreground: '#111',
|
|
27
|
-
background: '
|
|
27
|
+
background: 'rgba(0, 0, 0, 0)',
|
|
28
28
|
cursor: '#000000',
|
|
29
29
|
cursorAccent: '#000000',
|
|
30
30
|
selection: '#00000044',
|