wasm-idle 0.3.2 → 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 +22 -2
- package/dist/clang/app.d.ts +2 -1
- package/dist/clang/app.js +166 -137
- package/dist/clang/index.d.ts +6 -0
- package/dist/clang/index.js +137 -21
- package/dist/debug/expression.js +135 -19
- package/dist/debug/index.d.ts +1 -1
- package/dist/debug/index.js +1 -1
- package/dist/debug/language/index.d.ts +1 -0
- package/dist/debug/language/index.js +1 -0
- package/dist/debug/language/python.d.ts +2 -0
- package/dist/debug/language/python.js +14 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- 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 +11 -3
- package/dist/playground/clang.js +57 -20
- package/dist/playground/index.d.ts +2 -1
- package/dist/playground/index.js +10 -2
- package/dist/playground/java.d.ts +33 -0
- package/dist/playground/java.js +161 -0
- package/dist/playground/javaStdin.d.ts +8 -0
- package/dist/playground/javaStdin.js +323 -0
- package/dist/playground/options.d.ts +19 -0
- package/dist/playground/options.js +15 -1
- package/dist/playground/python.d.ts +21 -6
- package/dist/playground/python.js +135 -25
- package/dist/playground/sandbox.d.ts +5 -2
- package/dist/playground/stdinBuffer.d.ts +7 -0
- package/dist/playground/stdinBuffer.js +100 -0
- package/dist/playground/teavmConfig.d.ts +3 -0
- package/dist/playground/teavmConfig.js +14 -0
- package/dist/playground/worker/assets.d.ts +12 -0
- package/dist/playground/worker/assets.js +257 -0
- package/dist/playground/worker/clang.js +13 -23
- package/dist/playground/worker/java.d.ts +1 -0
- package/dist/playground/worker/java.js +228 -0
- package/dist/playground/worker/python.js +264 -58
- package/dist/terminal/Terminal.svelte +98 -18
- package/dist/terminal/Terminal.svelte.d.ts +4 -1
- package/dist/terminal/theme.js +2 -2
- package/dist/terminal/types.d.ts +4 -2
- package/package.json +11 -11
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { flushQueuedStdin, readBufferedStdin, waitForBufferedStdin } from '../stdinBuffer';
|
|
2
|
+
import { configureWorkerRuntimeAssets, handleWorkerAssetMessage } from './assets';
|
|
1
3
|
self.document = {
|
|
2
4
|
querySelector() {
|
|
3
5
|
return null;
|
|
@@ -6,8 +8,7 @@ self.document = {
|
|
|
6
8
|
return [];
|
|
7
9
|
}
|
|
8
10
|
};
|
|
9
|
-
let stdinBufferPyodide, interruptBufferPyodide, pyodide,
|
|
10
|
-
let jungolRobotReady = false;
|
|
11
|
+
let stdinBufferPyodide, debugBufferPyodide, watchBufferPyodide, watchResultBufferPyodide, interruptBufferPyodide, pyodide, baseUrl = '', packageBaseUrl = '';
|
|
11
12
|
const imageHook = `
|
|
12
13
|
if not globals().get("__wasm_idle_img_inited__", False):
|
|
13
14
|
globals()["__wasm_idle_img_inited__"] = True
|
|
@@ -156,33 +157,12 @@ async function loadPyodide(path) {
|
|
|
156
157
|
if (pyodide)
|
|
157
158
|
return;
|
|
158
159
|
const { loadPyodide } = await import('pyodide');
|
|
159
|
-
pyodide = await loadPyodide({ indexURL: path
|
|
160
|
-
packageBaseUrl = path
|
|
160
|
+
pyodide = await loadPyodide({ indexURL: path });
|
|
161
|
+
packageBaseUrl = path;
|
|
161
162
|
const setCdnUrl = pyodide?.setCdnUrl;
|
|
162
163
|
if (typeof setCdnUrl === 'function')
|
|
163
164
|
setCdnUrl(packageBaseUrl);
|
|
164
165
|
}
|
|
165
|
-
async function ensureRobotJungol() {
|
|
166
|
-
if (jungolRobotReady || !pyodide)
|
|
167
|
-
return;
|
|
168
|
-
try {
|
|
169
|
-
const res = await fetch(path + '/jungol-robot/jungol_robot.zip');
|
|
170
|
-
if (!res.ok)
|
|
171
|
-
throw new Error('jungol-robot zip not found');
|
|
172
|
-
const data = await res.arrayBuffer();
|
|
173
|
-
const siteRaw = pyodide.runPython('import site; site.getsitepackages()[0]');
|
|
174
|
-
const sitePath = typeof siteRaw === 'string' ? siteRaw : siteRaw.toString();
|
|
175
|
-
const unpackArchive = pyodide?.unpackArchive;
|
|
176
|
-
if (typeof unpackArchive !== 'function')
|
|
177
|
-
throw new Error('pyodide.unpackArchive unavailable');
|
|
178
|
-
unpackArchive(data, 'zip', sitePath);
|
|
179
|
-
pyodide.runPython('import importlib; importlib.invalidate_caches()');
|
|
180
|
-
jungolRobotReady = true;
|
|
181
|
-
}
|
|
182
|
-
catch (e) {
|
|
183
|
-
console.warn('jungol-robot preload failed', e);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
166
|
async function loadPackages(code) {
|
|
187
167
|
if (!code)
|
|
188
168
|
return;
|
|
@@ -202,20 +182,22 @@ async function loadPackages(code) {
|
|
|
202
182
|
}
|
|
203
183
|
}
|
|
204
184
|
self.onmessage = async (event) => {
|
|
205
|
-
|
|
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;
|
|
206
188
|
if (load) {
|
|
207
|
-
|
|
189
|
+
const runtimeAssets = assets;
|
|
190
|
+
baseUrl = runtimeAssets?.baseUrl || baseUrl;
|
|
191
|
+
configureWorkerRuntimeAssets(runtimeAssets || null);
|
|
208
192
|
postMessage({ output: 'Loading Pyodide...' });
|
|
209
|
-
await loadPyodide(
|
|
210
|
-
await ensureRobotJungol();
|
|
193
|
+
await loadPyodide(baseUrl);
|
|
211
194
|
postMessage({ output: ' Done.\n\r' });
|
|
212
195
|
postMessage({ load: true });
|
|
213
196
|
}
|
|
214
197
|
else if (prepare) {
|
|
215
198
|
postMessage({ output: 'Loading packages...' });
|
|
216
199
|
try {
|
|
217
|
-
await loadPyodide(
|
|
218
|
-
await ensureRobotJungol();
|
|
200
|
+
await loadPyodide(baseUrl);
|
|
219
201
|
await loadPackages(code);
|
|
220
202
|
postMessage({ output: ' Done.\n\r' });
|
|
221
203
|
self.postMessage({ results: true });
|
|
@@ -225,11 +207,13 @@ self.onmessage = async (event) => {
|
|
|
225
207
|
}
|
|
226
208
|
}
|
|
227
209
|
else if (code) {
|
|
228
|
-
await loadPyodide(
|
|
229
|
-
await ensureRobotJungol();
|
|
210
|
+
await loadPyodide(baseUrl);
|
|
230
211
|
await loadPackages(code);
|
|
231
212
|
const ts = Date.now();
|
|
232
213
|
stdinBufferPyodide = new Int32Array(buffer);
|
|
214
|
+
debugBufferPyodide = new Int32Array(debugBuffer);
|
|
215
|
+
watchBufferPyodide = new Int32Array(watchBuffer);
|
|
216
|
+
watchResultBufferPyodide = new Int32Array(watchResultBuffer);
|
|
233
217
|
interruptBufferPyodide = new Uint8Array(interrupt);
|
|
234
218
|
pyodide.setInterruptBuffer(interruptBufferPyodide);
|
|
235
219
|
const toPythonStr = (obj) => {
|
|
@@ -246,23 +230,7 @@ self.onmessage = async (event) => {
|
|
|
246
230
|
self.prompt = self['__pyodide__input_' + ts] = (output) => {
|
|
247
231
|
if (output)
|
|
248
232
|
postMessage({ output });
|
|
249
|
-
|
|
250
|
-
postMessage({ buffer: true });
|
|
251
|
-
const res = Atomics.wait(stdinBufferPyodide, 0, 0, 100);
|
|
252
|
-
if (res === 'not-equal') {
|
|
253
|
-
try {
|
|
254
|
-
const cpy = new Int32Array(stdinBufferPyodide.byteLength);
|
|
255
|
-
cpy.set(stdinBufferPyodide);
|
|
256
|
-
stdinBufferPyodide.fill(0);
|
|
257
|
-
const dec = new TextDecoder();
|
|
258
|
-
const strInfo = dec.decode(cpy).replace(/\x00/g, ''), padding = parseInt(strInfo.slice(-1));
|
|
259
|
-
return strInfo.slice(0, -padding);
|
|
260
|
-
}
|
|
261
|
-
catch (e) {
|
|
262
|
-
postMessage({ log: { e } });
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
233
|
+
return waitForBufferedStdin(stdinBufferPyodide, () => postMessage({ buffer: true }));
|
|
266
234
|
};
|
|
267
235
|
self['__pyodide__output_' + ts] = (...data) => {
|
|
268
236
|
let sep = ' ', end = '\r\n', output = '', clear = [];
|
|
@@ -284,24 +252,262 @@ self.onmessage = async (event) => {
|
|
|
284
252
|
output += end;
|
|
285
253
|
postMessage({ output });
|
|
286
254
|
};
|
|
255
|
+
const debugPauseName = `__wasm_idle_python_debug_pause_${ts}`;
|
|
256
|
+
const debugWaitName = `__wasm_idle_python_debug_wait_${ts}`;
|
|
257
|
+
const debugReadWatchName = `__wasm_idle_python_debug_watch_read_${ts}`;
|
|
258
|
+
const debugWriteWatchName = `__wasm_idle_python_debug_watch_write_${ts}`;
|
|
259
|
+
self[debugPauseName] = (line, reason, localsJson, callStackJson) => {
|
|
260
|
+
let locals = [];
|
|
261
|
+
let callStack = [];
|
|
262
|
+
try {
|
|
263
|
+
locals = JSON.parse(localsJson);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
locals = [];
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
callStack = JSON.parse(callStackJson);
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
callStack = [];
|
|
273
|
+
}
|
|
274
|
+
postMessage({
|
|
275
|
+
debugEvent: {
|
|
276
|
+
type: 'pause',
|
|
277
|
+
line: Number(line),
|
|
278
|
+
reason,
|
|
279
|
+
locals,
|
|
280
|
+
callStack
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
self[debugWaitName] = () => {
|
|
285
|
+
const sequence = Atomics.load(debugBufferPyodide, 0);
|
|
286
|
+
while (true) {
|
|
287
|
+
if (interruptBufferPyodide?.[0] === 2)
|
|
288
|
+
return -1;
|
|
289
|
+
Atomics.wait(debugBufferPyodide, 0, sequence, 100);
|
|
290
|
+
if (interruptBufferPyodide?.[0] === 2)
|
|
291
|
+
return -1;
|
|
292
|
+
const command = Atomics.exchange(debugBufferPyodide, 1, 0);
|
|
293
|
+
if (command)
|
|
294
|
+
return command;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
self[debugReadWatchName] = () => readBufferedStdin(watchBufferPyodide) || '';
|
|
298
|
+
self[debugWriteWatchName] = (value) => {
|
|
299
|
+
flushQueuedStdin([value], watchResultBufferPyodide);
|
|
300
|
+
};
|
|
301
|
+
const userFilename = '__wasm_idle_user__.py';
|
|
302
|
+
const normalizedBreakpoints = JSON.stringify([...(Array.isArray(breakpoints) ? breakpoints : [])]
|
|
303
|
+
.map((value) => Number(value))
|
|
304
|
+
.filter((value) => Number.isInteger(value) && value > 0));
|
|
287
305
|
try {
|
|
288
|
-
await pyodide.runPythonAsync(`import
|
|
306
|
+
await pyodide.runPythonAsync(`import ast
|
|
307
|
+
import builtins
|
|
308
|
+
import inspect
|
|
309
|
+
import json
|
|
310
|
+
import sys
|
|
289
311
|
from js import __pyodide__input_${ts}, __pyodide__output_${ts}
|
|
312
|
+
${debug ? `from js import ${debugPauseName}, ${debugWaitName}` : ''}
|
|
313
|
+
${debug ? `from js import ${debugReadWatchName}, ${debugWriteWatchName}` : ''}
|
|
290
314
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
315
|
+
__wasm_idle_input = __pyodide__input_${ts}
|
|
316
|
+
def __wasm_idle_input_wrapper(prompt = ""):
|
|
317
|
+
value = __wasm_idle_input(prompt)
|
|
318
|
+
if value is None:
|
|
319
|
+
raise EOFError
|
|
320
|
+
return value
|
|
321
|
+
__wasm_idle_output = __pyodide__output_${ts}
|
|
322
|
+
builtins.input = __wasm_idle_input_wrapper
|
|
323
|
+
builtins.print = __wasm_idle_output
|
|
296
324
|
|
|
297
325
|
${imageHook}
|
|
298
326
|
|
|
299
|
-
${
|
|
327
|
+
${debug
|
|
328
|
+
? `
|
|
329
|
+
__wasm_idle_debug_breakpoints = set(${normalizedBreakpoints})
|
|
330
|
+
__wasm_idle_debug_pause_on_entry = ${pauseOnEntry ? 'True' : 'False'}
|
|
331
|
+
__wasm_idle_debug_step_mode = None
|
|
332
|
+
__wasm_idle_debug_resume_skip = None
|
|
333
|
+
__wasm_idle_debug_next_depth = None
|
|
334
|
+
__wasm_idle_debug_next_line = None
|
|
335
|
+
__wasm_idle_debug_step_out_depth = None
|
|
336
|
+
|
|
337
|
+
def __wasm_idle_debug_depth(frame):
|
|
338
|
+
depth = 0
|
|
339
|
+
current = frame
|
|
340
|
+
while current is not None:
|
|
341
|
+
if current.f_code.co_filename == "${userFilename}":
|
|
342
|
+
depth += 1
|
|
343
|
+
current = current.f_back
|
|
344
|
+
return depth
|
|
345
|
+
|
|
346
|
+
def __wasm_idle_debug_preview(value, depth = 0):
|
|
347
|
+
if depth >= 2:
|
|
348
|
+
return "..."
|
|
349
|
+
if value is None:
|
|
350
|
+
return "None"
|
|
351
|
+
if isinstance(value, bool):
|
|
352
|
+
return "True" if value else "False"
|
|
353
|
+
if isinstance(value, (int, float)):
|
|
354
|
+
return repr(value)
|
|
355
|
+
if isinstance(value, (bytes, bytearray)):
|
|
356
|
+
text = repr(bytes(value))
|
|
357
|
+
return text if len(text) <= 80 else text[:77] + "..."
|
|
358
|
+
if isinstance(value, str):
|
|
359
|
+
text = repr(value)
|
|
360
|
+
return text if len(text) <= 80 else text[:77] + "..."
|
|
361
|
+
if isinstance(value, list):
|
|
362
|
+
items = [__wasm_idle_debug_preview(item, depth + 1) for item in value[:8]]
|
|
363
|
+
if len(value) > 8:
|
|
364
|
+
items.append("...")
|
|
365
|
+
return "[" + ", ".join(items) + "]"
|
|
366
|
+
if isinstance(value, tuple):
|
|
367
|
+
items = [__wasm_idle_debug_preview(item, depth + 1) for item in value[:8]]
|
|
368
|
+
if len(value) > 8:
|
|
369
|
+
items.append("...")
|
|
370
|
+
if len(value) == 1 and items:
|
|
371
|
+
return "(" + items[0] + ",)"
|
|
372
|
+
return "(" + ", ".join(items) + ")"
|
|
373
|
+
if isinstance(value, dict):
|
|
374
|
+
items = []
|
|
375
|
+
for index, (key, item) in enumerate(value.items()):
|
|
376
|
+
if index >= 6:
|
|
377
|
+
items.append("...")
|
|
378
|
+
break
|
|
379
|
+
items.append(__wasm_idle_debug_preview(key, depth + 1) + ": " + __wasm_idle_debug_preview(item, depth + 1))
|
|
380
|
+
return "{" + ", ".join(items) + "}"
|
|
381
|
+
if isinstance(value, set):
|
|
382
|
+
items = [__wasm_idle_debug_preview(item, depth + 1) for item in sorted(list(value), key = repr)[:6]]
|
|
383
|
+
if len(value) > 6:
|
|
384
|
+
items.append("...")
|
|
385
|
+
return "{" + ", ".join(items) + "}"
|
|
386
|
+
try:
|
|
387
|
+
text = repr(value)
|
|
388
|
+
return text if len(text) <= 80 else text[:77] + "..."
|
|
389
|
+
except Exception:
|
|
390
|
+
return "?"
|
|
391
|
+
|
|
392
|
+
def __wasm_idle_debug_locals(frame):
|
|
393
|
+
locals_preview = []
|
|
394
|
+
for name, value in frame.f_locals.items():
|
|
395
|
+
if name == "__builtins__" or name.startswith("__wasm_idle_") or name.startswith("."):
|
|
396
|
+
continue
|
|
397
|
+
locals_preview.append({"name": name, "value": __wasm_idle_debug_preview(value)})
|
|
398
|
+
locals_preview.sort(key = lambda item: item["name"])
|
|
399
|
+
return locals_preview
|
|
400
|
+
|
|
401
|
+
def __wasm_idle_debug_stack(frame):
|
|
402
|
+
stack = []
|
|
403
|
+
current = frame
|
|
404
|
+
while current is not None:
|
|
405
|
+
if current.f_code.co_filename == "${userFilename}":
|
|
406
|
+
stack.append({"functionName": current.f_code.co_name, "line": current.f_lineno})
|
|
407
|
+
current = current.f_back
|
|
408
|
+
return stack
|
|
409
|
+
|
|
410
|
+
def __wasm_idle_debug_trace(frame, event, arg):
|
|
411
|
+
global __wasm_idle_debug_pause_on_entry
|
|
412
|
+
global __wasm_idle_debug_step_mode
|
|
413
|
+
global __wasm_idle_debug_resume_skip
|
|
414
|
+
global __wasm_idle_debug_next_depth
|
|
415
|
+
global __wasm_idle_debug_next_line
|
|
416
|
+
global __wasm_idle_debug_step_out_depth
|
|
417
|
+
|
|
418
|
+
if frame.f_code.co_filename != "${userFilename}":
|
|
419
|
+
return None
|
|
420
|
+
if event != "line":
|
|
421
|
+
return __wasm_idle_debug_trace
|
|
422
|
+
|
|
423
|
+
depth = __wasm_idle_debug_depth(frame)
|
|
424
|
+
line = frame.f_lineno
|
|
425
|
+
if __wasm_idle_debug_resume_skip == (depth, line):
|
|
426
|
+
return __wasm_idle_debug_trace
|
|
427
|
+
if __wasm_idle_debug_resume_skip is not None:
|
|
428
|
+
__wasm_idle_debug_resume_skip = None
|
|
429
|
+
|
|
430
|
+
reason = None
|
|
431
|
+
if __wasm_idle_debug_pause_on_entry:
|
|
432
|
+
reason = "entry"
|
|
433
|
+
elif line in __wasm_idle_debug_breakpoints:
|
|
434
|
+
reason = "breakpoint"
|
|
435
|
+
elif __wasm_idle_debug_step_mode == "step":
|
|
436
|
+
reason = "step"
|
|
437
|
+
elif __wasm_idle_debug_step_mode == "next" and __wasm_idle_debug_next_depth is not None and depth <= __wasm_idle_debug_next_depth and line != __wasm_idle_debug_next_line:
|
|
438
|
+
reason = "nextLine"
|
|
439
|
+
elif __wasm_idle_debug_step_mode == "out" and __wasm_idle_debug_step_out_depth is not None and depth <= __wasm_idle_debug_step_out_depth:
|
|
440
|
+
reason = "stepOut"
|
|
441
|
+
|
|
442
|
+
if reason is None:
|
|
443
|
+
return __wasm_idle_debug_trace
|
|
444
|
+
|
|
445
|
+
__wasm_idle_debug_pause_on_entry = False
|
|
446
|
+
__wasm_idle_debug_step_mode = None
|
|
447
|
+
__wasm_idle_debug_next_depth = None
|
|
448
|
+
__wasm_idle_debug_next_line = None
|
|
449
|
+
__wasm_idle_debug_step_out_depth = None
|
|
450
|
+
|
|
451
|
+
${debugPauseName}(line, reason, json.dumps(__wasm_idle_debug_locals(frame)), json.dumps(__wasm_idle_debug_stack(frame)))
|
|
452
|
+
while True:
|
|
453
|
+
command = ${debugWaitName}()
|
|
454
|
+
if command < 0:
|
|
455
|
+
raise KeyboardInterrupt()
|
|
456
|
+
if command != 5:
|
|
457
|
+
break
|
|
458
|
+
try:
|
|
459
|
+
expression = ${debugReadWatchName}()
|
|
460
|
+
result = __wasm_idle_debug_preview(eval(expression, frame.f_globals, frame.f_locals))
|
|
461
|
+
except Exception as error:
|
|
462
|
+
result = "?" if error.__class__.__name__ == "NameError" else "error"
|
|
463
|
+
${debugWriteWatchName}(result)
|
|
464
|
+
__wasm_idle_debug_resume_skip = (depth, line)
|
|
465
|
+
if command == 2:
|
|
466
|
+
__wasm_idle_debug_step_mode = "step"
|
|
467
|
+
elif command == 3:
|
|
468
|
+
__wasm_idle_debug_step_mode = "next"
|
|
469
|
+
__wasm_idle_debug_next_depth = depth
|
|
470
|
+
__wasm_idle_debug_next_line = line
|
|
471
|
+
elif command == 4:
|
|
472
|
+
__wasm_idle_debug_step_mode = "out"
|
|
473
|
+
__wasm_idle_debug_step_out_depth = max(0, depth - 1)
|
|
474
|
+
return __wasm_idle_debug_trace
|
|
475
|
+
|
|
476
|
+
sys.settrace(__wasm_idle_debug_trace)
|
|
477
|
+
`
|
|
478
|
+
: ''}
|
|
479
|
+
|
|
480
|
+
try:
|
|
481
|
+
__wasm_idle_globals = {"__name__": "__main__"}
|
|
482
|
+
__wasm_idle_result = eval(
|
|
483
|
+
compile(${JSON.stringify(code)}, "${userFilename}", "exec", flags = ast.PyCF_ALLOW_TOP_LEVEL_AWAIT),
|
|
484
|
+
__wasm_idle_globals,
|
|
485
|
+
__wasm_idle_globals,
|
|
486
|
+
)
|
|
487
|
+
if inspect.isawaitable(__wasm_idle_result):
|
|
488
|
+
await __wasm_idle_result
|
|
489
|
+
finally:
|
|
490
|
+
sys.settrace(None)
|
|
491
|
+
${debug
|
|
492
|
+
? `
|
|
493
|
+
__wasm_idle_debug_step_mode = None
|
|
494
|
+
__wasm_idle_debug_resume_skip = None
|
|
495
|
+
__wasm_idle_debug_next_depth = None
|
|
496
|
+
__wasm_idle_debug_next_line = None
|
|
497
|
+
__wasm_idle_debug_step_out_depth = None
|
|
498
|
+
`
|
|
499
|
+
: ''}
|
|
500
|
+
`);
|
|
300
501
|
self.postMessage({ results: true });
|
|
301
502
|
}
|
|
302
503
|
catch (e) {
|
|
303
504
|
self.postMessage({ error: e.message || 'Unknown error' });
|
|
304
505
|
}
|
|
506
|
+
finally {
|
|
507
|
+
delete self[debugPauseName];
|
|
508
|
+
delete self[debugWaitName];
|
|
509
|
+
delete self[debugReadWatchName];
|
|
510
|
+
delete self[debugWriteWatchName];
|
|
511
|
+
}
|
|
305
512
|
}
|
|
306
513
|
};
|
|
307
|
-
export {};
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
CompilerDiagnostic,
|
|
4
|
+
DebugCommand,
|
|
5
|
+
DebugSessionEvent,
|
|
6
|
+
SandboxExecutionOptions
|
|
7
|
+
} from '../playground/options';
|
|
8
|
+
import type { PlaygroundRuntimeAssets } from '../playground/assets';
|
|
3
9
|
import type { Sandbox } from '../playground/sandbox';
|
|
4
10
|
import type { TerminalControl } from './types';
|
|
5
11
|
import { Theme, registerAllPlugins } from './';
|
|
@@ -11,12 +17,14 @@
|
|
|
11
17
|
interface Props {
|
|
12
18
|
dark?: boolean;
|
|
13
19
|
path?: string;
|
|
20
|
+
runtimeAssets?: string | PlaygroundRuntimeAssets;
|
|
14
21
|
font?: string;
|
|
15
22
|
|
|
16
23
|
onload?: () => void;
|
|
17
24
|
onfinish?: () => void;
|
|
18
25
|
onkey?: (e: KeyboardEvent) => void;
|
|
19
26
|
ondebug?: (event: DebugSessionEvent) => void;
|
|
27
|
+
oncompilediagnostic?: (diagnostic: CompilerDiagnostic) => void;
|
|
20
28
|
onimage?: (payload: { mime: string; b64: string; ts?: number }) => void;
|
|
21
29
|
terminal?: TerminalControl;
|
|
22
30
|
}
|
|
@@ -24,11 +32,13 @@
|
|
|
24
32
|
let {
|
|
25
33
|
dark = false,
|
|
26
34
|
path = '',
|
|
35
|
+
runtimeAssets = undefined,
|
|
27
36
|
font = "'D2 coding', monospace",
|
|
28
37
|
onload,
|
|
29
38
|
onfinish,
|
|
30
39
|
onkey,
|
|
31
40
|
ondebug,
|
|
41
|
+
oncompilediagnostic,
|
|
32
42
|
onimage,
|
|
33
43
|
terminal = $bindable()
|
|
34
44
|
}: Props = $props();
|
|
@@ -43,7 +53,22 @@
|
|
|
43
53
|
first = true,
|
|
44
54
|
tc = 0,
|
|
45
55
|
plugin = $state(),
|
|
46
|
-
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
|
+
}
|
|
47
72
|
|
|
48
73
|
function wait() {
|
|
49
74
|
return new Promise<void>((r) => {
|
|
@@ -69,6 +94,7 @@
|
|
|
69
94
|
}
|
|
70
95
|
sandbox.image = onimage;
|
|
71
96
|
sandbox.ondebug = ondebug;
|
|
97
|
+
sandbox.oncompilerdiagnostic = oncompilediagnostic;
|
|
72
98
|
sandbox.output = (output: string) =>
|
|
73
99
|
_tc === tc && term && term.write(output.replaceAll('\n', '\r\n'));
|
|
74
100
|
}
|
|
@@ -80,10 +106,12 @@
|
|
|
80
106
|
return x;
|
|
81
107
|
})
|
|
82
108
|
.catch((msg) => {
|
|
109
|
+
if (stopRequested) return false;
|
|
83
110
|
term?.write(`\r\n\x1B[1;3;31m${msg}\u001B[?25l`);
|
|
84
111
|
return false;
|
|
85
112
|
})
|
|
86
113
|
.finally(() => {
|
|
114
|
+
stopRequested = false;
|
|
87
115
|
onfinish?.();
|
|
88
116
|
finish = true;
|
|
89
117
|
if (term) term.options.cursorBlink = false;
|
|
@@ -94,18 +122,40 @@
|
|
|
94
122
|
await wait();
|
|
95
123
|
if (!term) return;
|
|
96
124
|
term.options.cursorBlink = blink;
|
|
125
|
+
term.write('\u001B[?25h');
|
|
97
126
|
term.focus();
|
|
98
127
|
|
|
99
128
|
if (!first) term?.write(`\r\n\x1b[0m`);
|
|
100
129
|
first = false;
|
|
101
130
|
}
|
|
102
131
|
|
|
132
|
+
function appendInputText(text: string) {
|
|
133
|
+
if (!text) return;
|
|
134
|
+
input += text;
|
|
135
|
+
term?.write(text);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function submitCurrentInput() {
|
|
139
|
+
term?.write('\r\n');
|
|
140
|
+
sandbox.write?.(input + '\n');
|
|
141
|
+
input = '';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function applyPastedText(text: string) {
|
|
145
|
+
const lines = text.replaceAll('\r\n', '\n').replaceAll('\r', '\n').split('\n');
|
|
146
|
+
for (let i = 0; i < lines.length; i++) {
|
|
147
|
+
appendInputText(lines[i]);
|
|
148
|
+
if (i < lines.length - 1) submitCurrentInput();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
103
152
|
const terminalControl: TerminalControl = {
|
|
104
153
|
async clear() {
|
|
105
154
|
await wait();
|
|
106
155
|
term?.reset();
|
|
107
156
|
term?.write(`\u001B[?25l\x1b[0m\x1b[?25h`);
|
|
108
157
|
if (term) term.options.cursorBlink = false;
|
|
158
|
+
input = '';
|
|
109
159
|
first = true;
|
|
110
160
|
await new Promise((r) => setTimeout(r, 100));
|
|
111
161
|
},
|
|
@@ -117,11 +167,18 @@
|
|
|
117
167
|
args: string[] = [],
|
|
118
168
|
options: SandboxExecutionOptions = {}
|
|
119
169
|
) {
|
|
170
|
+
const loadProgress = phaseProgress(prog, 0, 0.85);
|
|
171
|
+
const prepareProgress = phaseProgress(prog, 0.85, 0.99);
|
|
120
172
|
await Promise.all([
|
|
121
|
-
initSandbox(language).then(() =>
|
|
173
|
+
initSandbox(language).then(() =>
|
|
174
|
+
sandbox.load(runtimeAssets || path, code, log, args, options, loadProgress)
|
|
175
|
+
),
|
|
122
176
|
initTerm(false)
|
|
123
177
|
]);
|
|
124
|
-
|
|
178
|
+
prepareProgress?.set?.(0);
|
|
179
|
+
return !!(await runSandbox(
|
|
180
|
+
sandbox.run(code, true, log, prepareProgress, args, options)
|
|
181
|
+
));
|
|
125
182
|
},
|
|
126
183
|
async run(
|
|
127
184
|
language: string,
|
|
@@ -132,7 +189,9 @@
|
|
|
132
189
|
options: SandboxExecutionOptions = {}
|
|
133
190
|
) {
|
|
134
191
|
await Promise.all([
|
|
135
|
-
initSandbox(language).then(() =>
|
|
192
|
+
initSandbox(language).then(() =>
|
|
193
|
+
sandbox.load(runtimeAssets || path, code, log, args, options, prog)
|
|
194
|
+
),
|
|
136
195
|
initTerm()
|
|
137
196
|
]);
|
|
138
197
|
return await runSandbox(sandbox.run(code, false, log, prog, args, options));
|
|
@@ -142,14 +201,25 @@
|
|
|
142
201
|
term?.dispose();
|
|
143
202
|
if (sandbox) await sandbox.clear();
|
|
144
203
|
},
|
|
204
|
+
async stop() {
|
|
205
|
+
await wait();
|
|
206
|
+
stopRequested = true;
|
|
207
|
+
finish = true;
|
|
208
|
+
if (sandbox?.kill) sandbox.kill();
|
|
209
|
+
else sandbox?.terminate?.();
|
|
210
|
+
},
|
|
145
211
|
async debugCommand(command: DebugCommand) {
|
|
146
212
|
await wait();
|
|
147
213
|
sandbox.debugCommand?.(command);
|
|
148
214
|
},
|
|
215
|
+
async debugEvaluate(expression: string) {
|
|
216
|
+
await wait();
|
|
217
|
+
return (await sandbox.debugEvaluate?.(expression)) || '?';
|
|
218
|
+
},
|
|
149
219
|
async write(input: string) {
|
|
150
220
|
await wait();
|
|
151
|
-
|
|
152
|
-
|
|
221
|
+
applyPastedText(input);
|
|
222
|
+
if (!input.endsWith('\n') && !input.endsWith('\r')) submitCurrentInput();
|
|
153
223
|
}
|
|
154
224
|
};
|
|
155
225
|
|
|
@@ -186,9 +256,7 @@
|
|
|
186
256
|
const printable = !ev.altKey && !ev.ctrlKey && !ev.metaKey;
|
|
187
257
|
onkey?.(ev);
|
|
188
258
|
if (ev.key === 'Enter') {
|
|
189
|
-
|
|
190
|
-
sandbox.write?.(input + '\n');
|
|
191
|
-
input = '';
|
|
259
|
+
submitCurrentInput();
|
|
192
260
|
} else if (ev.key === 'Backspace') {
|
|
193
261
|
if (term.buffer.active.cursorX > 0) {
|
|
194
262
|
term.write('\b \b');
|
|
@@ -200,17 +268,23 @@
|
|
|
200
268
|
e.key <= String.fromCharCode(0x7e)) ||
|
|
201
269
|
e.key >= '\u00a0'
|
|
202
270
|
) {
|
|
203
|
-
|
|
204
|
-
|
|
271
|
+
appendInputText(e.key);
|
|
272
|
+
}
|
|
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
|
+
}
|
|
205
280
|
}
|
|
206
|
-
} else if ((ev.ctrlKey || ev.metaKey) && ev.key === 'c') {
|
|
207
|
-
sandbox.kill?.();
|
|
208
|
-
} else if ((ev.ctrlKey || ev.metaKey) && ev.key === 'd') {
|
|
209
281
|
sandbox.kill?.();
|
|
210
|
-
} else if ((ev.ctrlKey || ev.metaKey) && ev.key === '
|
|
282
|
+
} else if ((ev.ctrlKey || ev.metaKey) && ev.key.toLowerCase() === 'd') {
|
|
283
|
+
if (input.length > 0) submitCurrentInput();
|
|
284
|
+
else sandbox.eof?.();
|
|
285
|
+
} else if ((ev.ctrlKey || ev.metaKey) && ev.key.toLowerCase() === 'v') {
|
|
211
286
|
navigator.clipboard.readText().then((text) => {
|
|
212
|
-
|
|
213
|
-
sandbox.write?.(text);
|
|
287
|
+
applyPastedText(text);
|
|
214
288
|
});
|
|
215
289
|
}
|
|
216
290
|
});
|
|
@@ -242,4 +316,10 @@
|
|
|
242
316
|
width: 100%;
|
|
243
317
|
height: 100%;
|
|
244
318
|
}
|
|
319
|
+
|
|
320
|
+
:global(.xterm),
|
|
321
|
+
:global(.xterm .xterm-viewport),
|
|
322
|
+
:global(.xterm .composition-view) {
|
|
323
|
+
background-color: transparent;
|
|
324
|
+
}
|
|
245
325
|
</style>
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import type { DebugSessionEvent } from '../playground/options';
|
|
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;
|
|
10
12
|
onkey?: (e: KeyboardEvent) => void;
|
|
11
13
|
ondebug?: (event: DebugSessionEvent) => void;
|
|
14
|
+
oncompilediagnostic?: (diagnostic: CompilerDiagnostic) => void;
|
|
12
15
|
onimage?: (payload: {
|
|
13
16
|
mime: string;
|
|
14
17
|
b64: string;
|
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',
|
package/dist/terminal/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, SandboxExecutionOptions } from '../playground/options';
|
|
1
|
+
import type { CompilerDiagnostic, DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, SandboxExecutionOptions } from '../playground/options';
|
|
2
2
|
export interface ProgressLike {
|
|
3
3
|
set?: (value: number) => void;
|
|
4
4
|
}
|
|
@@ -7,7 +7,9 @@ export interface TerminalControl {
|
|
|
7
7
|
prepare: (language: string, code: string, log?: boolean, prog?: ProgressLike, args?: string[], options?: SandboxExecutionOptions) => Promise<boolean>;
|
|
8
8
|
run: (language: string, code: string, log?: boolean, prog?: ProgressLike, args?: string[], options?: SandboxExecutionOptions) => Promise<boolean | string>;
|
|
9
9
|
destroy: () => Promise<void>;
|
|
10
|
+
stop?: () => Promise<void>;
|
|
10
11
|
debugCommand: (command: DebugCommand) => Promise<void>;
|
|
12
|
+
debugEvaluate?: (expression: string) => Promise<string>;
|
|
11
13
|
write: (input: string) => Promise<void>;
|
|
12
14
|
}
|
|
13
|
-
export type { DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, SandboxExecutionOptions };
|
|
15
|
+
export type { CompilerDiagnostic, DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, SandboxExecutionOptions };
|