wasm-idle 0.1.0 → 0.2.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 CHANGED
@@ -6,4 +6,4 @@ Executes C++, Python Code with working stdio.
6
6
 
7
7
  Refer to src/lib/clang.
8
8
 
9
- Powered by [wasm-clang](https://github.com/binji/wasm-clang) and Pyodide.
9
+ Powered by [wasm-clang](https://github.com/binji/wasm-clang) and Pyodide.
@@ -1,4 +1,4 @@
1
- import { Memory, type MemFS } from "./memory";
1
+ import { Memory, type MemFS } from './memory';
2
2
  export default class App {
3
3
  ready: Promise<void>;
4
4
  mem: Memory;
package/dist/clang/app.js CHANGED
@@ -1,9 +1,9 @@
1
- import { bindNew } from "./apply";
2
- import { Memory } from "./memory";
3
- import { getInstance } from "./wasm";
4
- import { NotImplemented, ProcExit } from "./error";
1
+ import { bindNew } from './apply';
2
+ import { Memory } from './memory';
3
+ import { getInstance } from './wasm';
4
+ import { NotImplemented, ProcExit } from './error';
5
5
  const ESUCCESS = 0;
6
- const RAF_PROC_EXIT_CODE = 0xC0C0A;
6
+ const RAF_PROC_EXIT_CODE = 0xc0c0a;
7
7
  export default class App {
8
8
  ready;
9
9
  mem = null;
@@ -20,9 +20,10 @@ export default class App {
20
20
  this.memfs = memfs;
21
21
  const env = bindNew(this);
22
22
  const wasi_unstable = {
23
- ...bindNew(this, 'proc_exit', 'environ_sizes_get', 'environ_get', 'args_sizes_get', 'args_get', 'random_get', 'clock_time_get', 'poll_oneoff'), ...this.memfs.exports
23
+ ...bindNew(this, 'proc_exit', 'environ_sizes_get', 'environ_get', 'args_sizes_get', 'args_get', 'random_get', 'clock_time_get', 'poll_oneoff'),
24
+ ...this.memfs.exports
24
25
  };
25
- this.ready = getInstance(module, { wasi_unstable, env }).then(instance => {
26
+ this.ready = getInstance(module, { wasi_unstable, env }).then((instance) => {
26
27
  this.instance = instance;
27
28
  this.exports = this.instance.exports;
28
29
  this.mem = new Memory(this.exports.memory);
@@ -79,8 +80,7 @@ export default class App {
79
80
  for (const name of names) {
80
81
  this.mem.write32(environ_ptrs, environ_buf);
81
82
  environ_ptrs += 4;
82
- environ_buf +=
83
- this.mem.writeStr(environ_buf, `${name}=${this.environ[name]}`);
83
+ environ_buf += this.mem.writeStr(environ_buf, `${name}=${this.environ[name]}`);
84
84
  }
85
85
  this.mem.write32(environ_ptrs, 0);
86
86
  return ESUCCESS;
@@ -1,4 +1,4 @@
1
1
  export declare function readStr(u8: Uint8Array, o: number, len?: number): string;
2
2
  export declare function readStrR(u8: Uint8Array, o: number, len?: number): string;
3
3
  export declare function readOct(u8: Uint8Array, o: number, len: number): number;
4
- export declare function readUint8(u8: Uint8Array, o: number, len: number): Uint8Array;
4
+ export declare function readUint8(u8: Uint8Array, o: number, len: number): Uint8Array<ArrayBufferLike>;
@@ -1,6 +1,6 @@
1
- import App from "./app";
2
- import { MemFS } from "./memory";
3
- import { type Writable } from "svelte/store";
1
+ import App from './app';
2
+ import { MemFS } from './memory';
3
+ import { type Writable } from 'svelte/store';
4
4
  interface APIOption {
5
5
  stdin: () => string;
6
6
  stdout: (str: string) => void;
@@ -1,18 +1,24 @@
1
- import App from "./app";
2
- import { MemFS, untar } from "./memory";
3
- import { green, yellow, normal } from "./color";
4
- import { compile, readBuffer } from "./wasm";
5
- import { clangUrl, lldUrl, rootUrl } from "./url";
6
- import { derived, writable } from "svelte/store";
1
+ import App from './app';
2
+ import { MemFS, untar } from './memory';
3
+ import { green, yellow, normal } from './color';
4
+ import { compile, readBuffer } from './wasm';
5
+ import { clangUrl, lldUrl, rootUrl } from './url';
6
+ import { derived, writable } from 'svelte/store';
7
7
  const clangCommonArgs = [
8
8
  '-disable-free',
9
- '-isysroot', '/',
10
- '-internal-isystem', '/include/c++/v1',
11
- '-internal-isystem', '/include',
12
- '-internal-isystem', '/lib/clang/8.0.1/include',
13
- '-ferror-limit', '19',
14
- '-fmessage-length', '80',
15
- '-fcolor-diagnostics',
9
+ '-isysroot',
10
+ '/',
11
+ '-internal-isystem',
12
+ '/include/c++/v1',
13
+ '-internal-isystem',
14
+ '/include',
15
+ '-internal-isystem',
16
+ '/lib/clang/8.0.1/include',
17
+ '-ferror-limit',
18
+ '19',
19
+ '-fmessage-length',
20
+ '80',
21
+ '-fcolor-diagnostics'
16
22
  ];
17
23
  const toUtf8 = (text) => {
18
24
  const surrogate = encodeURIComponent(text);
@@ -21,7 +27,7 @@ const toUtf8 = (text) => {
21
27
  const character = surrogate[i];
22
28
  i += 1;
23
29
  if (character == '%') {
24
- const hex = surrogate.substring(i, i += 2);
30
+ const hex = surrogate.substring(i, (i += 2));
25
31
  if (hex)
26
32
  result += String.fromCharCode(parseInt(hex, 16));
27
33
  }
@@ -44,7 +50,7 @@ export default class Clang {
44
50
  progress = {
45
51
  clang: writable(0),
46
52
  lld: writable(0),
47
- memfs: writable(0),
53
+ memfs: writable(0)
48
54
  };
49
55
  constructor(options) {
50
56
  this.moduleCache = {};
@@ -56,17 +62,17 @@ export default class Clang {
56
62
  stdout: this.stdout,
57
63
  stdin: options.stdin,
58
64
  path: this.path,
59
- progress: this.progress.memfs,
65
+ progress: this.progress.memfs
60
66
  });
61
67
  const progress = derived([this.progress.clang, this.progress.lld, this.progress.memfs], ([clang, lld, memfs]) => {
62
68
  return (clang + lld + memfs) / 3;
63
69
  });
64
- progress.subscribe(value => {
70
+ progress.subscribe((value) => {
65
71
  options.progress(value);
66
72
  });
67
73
  this.getModule(clangUrl(this.path), this.progress.clang);
68
74
  this.getModule(lldUrl(this.path), this.progress.lld);
69
- this.ready = this.memfs.ready.then(() => this.hostLogAsync(`Untarring ${rootUrl(this.path)}`, readBuffer(rootUrl(this.path)).then(buffer => untar(buffer, this.memfs))));
75
+ this.ready = this.memfs.ready.then(() => this.hostLogAsync(`Untarring ${rootUrl(this.path)}`, readBuffer(rootUrl(this.path)).then((buffer) => untar(buffer, this.memfs))));
70
76
  }
71
77
  hostLog(message) {
72
78
  if (!this.log)
@@ -134,7 +140,7 @@ export default class Clang {
134
140
  await this.compile({ input, code, obj });
135
141
  await this.link(obj, wasm);
136
142
  this.lastCode = code;
137
- return this.wasm = await this.hostLogAsync(`Compiling ${wasm}`, WebAssembly.compile(this.memfs.getFileContents(wasm)));
143
+ return (this.wasm = await this.hostLogAsync(`Compiling ${wasm}`, WebAssembly.compile(this.memfs.getFileContents(wasm))));
138
144
  }
139
145
  async compileLinkRun(code) {
140
146
  return await this.run(await this.compileLink(code), true, `test.wasm`);
@@ -1,4 +1,4 @@
1
- import MemFS from "./memFs";
2
- import Memory from "./memory";
3
- import untar from "./tar";
1
+ import MemFS from './memFs';
2
+ import Memory from './memory';
3
+ import untar from './tar';
4
4
  export { MemFS, Memory, untar };
@@ -1,4 +1,4 @@
1
- import MemFS from "./memFs";
2
- import Memory from "./memory";
3
- import untar from "./tar";
1
+ import MemFS from './memFs';
2
+ import Memory from './memory';
3
+ import untar from './tar';
4
4
  export { MemFS, Memory, untar };
@@ -1,5 +1,5 @@
1
1
  import { Memory } from './';
2
- import type { Writable } from "svelte/store";
2
+ import type { Writable } from 'svelte/store';
3
3
  interface MemFsOptions {
4
4
  stdin: () => string;
5
5
  stdout: (str: string) => void;
@@ -22,7 +22,7 @@ export default class MemFS {
22
22
  setStdinStr(str: string): void;
23
23
  addDirectory(path: string): void;
24
24
  addFile(path: string, contents: string | ArrayBuffer | Uint8Array): void;
25
- getFileContents(path: string): Uint8Array;
25
+ getFileContents(path: string): Uint8Array<ArrayBuffer>;
26
26
  abort(): void;
27
27
  host_write(fd: number, iovs: number, iovs_len: number, nwritten_out: number): number;
28
28
  host_read(fd: number, iovs: number, iovs_len: number, nread: number): number;
@@ -1,8 +1,8 @@
1
1
  import { Memory } from './';
2
- import { bindNew } from "../apply";
3
- import { AbortError, assert } from "../error";
4
- import { compile } from "../wasm";
5
- import { memfsUrl } from "../url";
2
+ import { bindNew } from '../apply';
3
+ import { AbortError, assert } from '../error';
4
+ import { compile } from '../wasm';
5
+ import { memfsUrl } from '../url';
6
6
  const ESUCCESS = 0;
7
7
  export default class MemFS {
8
8
  ready;
@@ -17,11 +17,11 @@ export default class MemFS {
17
17
  constructor(options) {
18
18
  this.stdin = options.stdin;
19
19
  this.stdout = options.stdout;
20
- this.stdinStr = options.stdinStr || "";
20
+ this.stdinStr = options.stdinStr || '';
21
21
  const env = bindNew(this, 'abort', 'host_write', 'host_read', 'memfs_log', 'copy_in', 'copy_out');
22
22
  this.ready = compile(memfsUrl(options.path), options.progress)
23
- .then(module => WebAssembly.instantiate(module, { env }))
24
- .then(instance => {
23
+ .then((module) => WebAssembly.instantiate(module, { env }))
24
+ .then((instance) => {
25
25
  this.instance = instance;
26
26
  this.exports = instance.exports;
27
27
  this.mem = new Memory(this.exports.memory);
@@ -1,4 +1,4 @@
1
- import { readStr, readStrR } from "../encode";
1
+ import { readStr, readStrR } from '../encode';
2
2
  export default class Memory {
3
3
  memory;
4
4
  buffer;
@@ -52,7 +52,7 @@ export default class Memory {
52
52
  if (buf instanceof ArrayBuffer)
53
53
  return this.writeUint8(o, new Uint8Array(buf));
54
54
  else if (typeof buf === 'string')
55
- return this.writeUint8(o, buf.split('').map(x => x.charCodeAt(0)));
55
+ return this.writeUint8(o, buf.split('').map((x) => x.charCodeAt(0)));
56
56
  else
57
57
  return this.writeUint8(o, buf);
58
58
  }
@@ -1,2 +1,2 @@
1
- import type { MemFS } from "./";
1
+ import type { MemFS } from './';
2
2
  export default function untar(buffer: ArrayBuffer, memfs: MemFS): void;
@@ -1,5 +1,5 @@
1
- import { readStr, readOct } from "../encode";
2
- import { assert } from "../error";
1
+ import { readStr, readOct } from '../encode';
2
+ import { assert } from '../error';
3
3
  function* readEntry(buffer) {
4
4
  const u8 = new Uint8Array(buffer);
5
5
  let offset = 0;
@@ -9,7 +9,7 @@ function* readEntry(buffer) {
9
9
  }, oct = (len) => {
10
10
  offset += len;
11
11
  return readOct(u8, offset - len, len);
12
- }, align = () => offset = (offset + 511) & ~511;
12
+ }, align = () => (offset = (offset + 511) & ~511);
13
13
  while (offset + 512 <= u8.length) {
14
14
  const entryInit = {
15
15
  filename: str(100),
@@ -21,7 +21,7 @@ function* readEntry(buffer) {
21
21
  checksum: oct(8),
22
22
  type: str(1),
23
23
  linkname: str(100),
24
- ustar: str(8),
24
+ ustar: str(8)
25
25
  };
26
26
  if (!entryInit.ustar)
27
27
  return;
@@ -31,7 +31,7 @@ function* readEntry(buffer) {
31
31
  groupName: str(32),
32
32
  devMajor: str(8),
33
33
  devMinor: str(8),
34
- filenamePrefix: str(155),
34
+ filenamePrefix: str(155)
35
35
  };
36
36
  align();
37
37
  if (entry.type === '0') {
@@ -1,4 +1,4 @@
1
- import type { Writable } from "svelte/store";
2
- export declare const readBuffer: (name: string, progress?: Writable<number>) => Promise<Uint8Array>;
1
+ import type { Writable } from 'svelte/store';
2
+ export declare const readBuffer: (name: string, progress?: Writable<number>) => Promise<Uint8Array<ArrayBufferLike>>;
3
3
  export declare function compile(filename: string, progress?: Writable<number>): Promise<any>;
4
4
  export declare function getInstance(module: WebAssembly.Module, imports: WebAssembly.Imports): Promise<WebAssembly.Instance>;
@@ -1,4 +1,4 @@
1
- import * as zip from "@zip.js/zip.js";
1
+ import * as zip from '@zip.js/zip.js';
2
2
  const store = {};
3
3
  export const readBuffer = async (name, progress) => {
4
4
  const response = await fetch(name);
@@ -34,7 +34,7 @@ export async function compile(filename, progress) {
34
34
  // application/wasm mimetype.
35
35
  if (store[filename])
36
36
  return store[filename];
37
- return store[filename] = WebAssembly.compile(await readBuffer(filename, progress));
37
+ return (store[filename] = WebAssembly.compile(await readBuffer(filename, progress)));
38
38
  }
39
39
  export function getInstance(module, imports) {
40
40
  return WebAssembly.instantiate(module, imports);
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import Terminal from "./terminal";
2
- import playground from "./playground";
1
+ import Terminal from './terminal';
2
+ import playground from './playground';
3
3
  export default Terminal;
4
4
  export { playground };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import Terminal from "./terminal";
2
- import playground from "./playground";
1
+ import Terminal from './terminal';
2
+ import playground from './playground';
3
3
  export default Terminal;
4
4
  export { playground };
@@ -1,5 +1,5 @@
1
- import type Sandbox from './sandbox';
2
- import type { Writable } from "svelte/store";
1
+ import type { Sandbox } from './sandbox';
2
+ import type { Writable } from 'svelte/store';
3
3
  declare class Clang implements Sandbox {
4
4
  ts: number;
5
5
  output: any;
@@ -38,8 +38,7 @@ class Clang {
38
38
  const data = enc.encode(strInfo);
39
39
  buffer.set(new Int32Array(data.buffer.slice(data.byteOffset), 0));
40
40
  }
41
- eof() {
42
- }
41
+ eof() { }
43
42
  run(code, prepare, log = this.log, prog) {
44
43
  this.exit = false;
45
44
  return new Promise(async (resolve, reject) => {
@@ -50,7 +49,7 @@ class Clang {
50
49
  if (!this.worker)
51
50
  return reject('Worker not loaded');
52
51
  if (_uid !== this.uid)
53
- return this.worker.onmessage = null;
52
+ return (this.worker.onmessage = null);
54
53
  const { id, output, results, log, error, buffer, progress } = event.data;
55
54
  if (buffer && this.internalBuffer.length)
56
55
  this._write(this.internalBuffer.splice(0, 1)[0]);
@@ -79,7 +78,7 @@ class Clang {
79
78
  buffer: this.buffer,
80
79
  interrupt: this.interruptBuffer,
81
80
  context: {},
82
- log,
81
+ log
83
82
  });
84
83
  });
85
84
  }
@@ -1,4 +1,5 @@
1
1
  import Clang from './clang';
2
- import type Sandbox from './sandbox';
2
+ import Python from './python';
3
+ import type { Sandbox } from './sandbox';
3
4
  export declare const supportedLanguages: string[];
4
- export default function load(language: string): Promise<Sandbox | Clang | undefined>;
5
+ export default function load(language: string): Promise<Sandbox | Clang | Python>;
@@ -15,6 +15,9 @@ export default async function load(language) {
15
15
  case 'C':
16
16
  case 'CPP':
17
17
  sandbox = new Clang();
18
+ break;
19
+ default:
20
+ throw new Error(`Unsupported language: ${language}`);
18
21
  }
19
22
  sandboxCache[language] = sandbox;
20
23
  if (sandbox) {
@@ -1,4 +1,4 @@
1
- import type Sandbox from './sandbox';
1
+ import type { Sandbox } from './sandbox';
2
2
  declare class Python implements Sandbox {
3
3
  ts: number;
4
4
  output: any;
@@ -35,14 +35,13 @@ class Python {
35
35
  const data = enc.encode(strInfo);
36
36
  buffer.set(new Int32Array(data.buffer.slice(data.byteOffset), 0));
37
37
  }
38
- eof() {
39
- }
38
+ eof() { }
40
39
  run(code, prepare) {
41
40
  return new Promise(async (resolve, reject) => {
42
41
  const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
43
42
  const handler = (event) => {
44
43
  if (_uid !== this.uid)
45
- return this.worker.onmessage = null;
44
+ return (this.worker.onmessage = null);
46
45
  const { id, output, results, log, error, buffer } = event.data;
47
46
  if (buffer && this.internalBuffer.length)
48
47
  this._write(this.internalBuffer.splice(0, 1)[0]);
@@ -1,9 +1,12 @@
1
- interface sandbox {
1
+ export interface Sandbox {
2
2
  constructor: any;
3
3
  eof: () => void;
4
4
  load: (path: string, code?: string, log?: boolean) => Promise<void>;
5
5
  run: (code: string, prepare: boolean, log?: boolean) => Promise<string>;
6
6
  terminate: () => void;
7
7
  clear: () => Promise<void>;
8
+ kill?: () => void;
9
+ write?: (data: string) => void;
10
+ output?: (data: string) => void;
11
+ elapse?: number;
8
12
  }
9
- export default sandbox;
@@ -1 +1 @@
1
- export default sandbox;
1
+ export {};
@@ -28,7 +28,8 @@ async function loadClang(path, log) {
28
28
  }
29
29
  },
30
30
  progress: (value) => postMessage({ progress: value }),
31
- log, path,
31
+ log,
32
+ path
32
33
  });
33
34
  }
34
35
  self.onmessage = async (event) => {
@@ -1,158 +1,221 @@
1
- <script>import { Theme, registerAllPlugins } from "./";
2
- import load from "../playground";
3
- import { onMount, createEventDispatcher } from "svelte";
4
- import "@xterm/xterm/css/xterm.css";
5
- const dispatch = createEventDispatcher();
6
- export let dark = false, path = "", font = "'D2 coding', monospace";
7
- let ref, clientWidth, clientHeight, term, finish = true, input = "", sandbox, first = true, tc = 0, plugin, ll = null;
8
- function wait() {
9
- return new Promise((r) => {
10
- const i = setInterval(() => {
11
- if (term) {
12
- clearInterval(i);
13
- r();
14
- }
15
- }, 100);
16
- });
17
- }
18
- async function initSandbox(language) {
19
- let _tc = ++tc;
20
- await wait();
21
- if (sandbox) await sandbox.clear();
22
- input = "";
23
- finish = false;
24
- if (ll !== language) {
25
- sandbox = await load(language);
26
- await sandbox.clear();
27
- ll = language;
28
- }
29
- sandbox.output = (output) => _tc === tc && term.write(output.replaceAll("\n", "\r\n"));
30
- }
31
- function runSandbox(pr) {
32
- return pr.then((x) => {
33
- term.write(`\r
34
- Process finished after ${sandbox.elapse}ms\x1B[?25l`);
35
- return x;
36
- }).catch((msg) => {
37
- term.write(`\r
38
- \x1B[1;3;31m${msg}\x1B[?25l`);
39
- return false;
40
- }).finally((ret) => {
41
- dispatch("finish");
42
- finish = true;
43
- term.options.cursorBlink = false;
44
- return ret;
45
- });
46
- }
47
- async function initTerm(blink = true) {
48
- await wait();
49
- term.options.cursorBlink = blink;
50
- term.focus();
51
- if (!first) term.write(`\r
52
- \x1B[0m`);
53
- first = false;
54
- }
55
- export const terminal = {
56
- async clear() {
57
- await wait();
58
- term.reset();
59
- term.write(`\x1B[?25l\x1B[0m\x1B[?25h`);
60
- term.options.cursorBlink = false;
61
- first = true;
62
- await new Promise((r) => setTimeout(r, 100));
63
- },
64
- async prepare(language, code, log = true, prog) {
65
- await Promise.all([initSandbox(language).then(() => sandbox.load(path, code, log)), initTerm(false)]);
66
- return await runSandbox(sandbox.run(code, true, log, prog));
67
- },
68
- async run(language, code, log = true, prog) {
69
- await Promise.all([initSandbox(language).then(() => sandbox.load(path, code, log)), initTerm()]);
70
- return await runSandbox(sandbox.run(code, false, log, prog));
71
- },
72
- async destroy() {
73
- await wait();
74
- term.dispose();
75
- if (sandbox) await sandbox.clear();
76
- },
77
- async write(input2) {
78
- await wait();
79
- term.write(input2 + "\r\n");
80
- sandbox.write(input2.replaceAll("\r", "\n") + "\n");
81
- }
82
- };
83
- $: if (term) {
84
- if (dark) term.options.theme = Theme.Tango_Dark;
85
- else term.options.theme = Theme.Tango_Light;
86
- }
87
- $: {
88
- void 0, clientHeight;
89
- plugin?.fit?.fit?.();
90
- }
91
- onMount(() => {
92
- import("@xterm/xterm").then(async ({ Terminal }) => {
93
- term = new Terminal({
94
- theme: dark ? Theme.Tango_Dark : Theme.Tango_Light,
95
- cursorBlink: false,
96
- allowTransparency: true,
97
- fontFamily: font,
98
- allowProposedApi: true
99
- });
100
- term.open(ref);
101
- term.onKey((e) => {
102
- if (finish) return;
103
- const ev = e.domEvent;
104
- const printable = !ev.altKey && !ev.ctrlKey && !ev.metaKey;
105
- dispatch("key", ev);
106
- if (ev.key === "Enter") {
107
- term.write("\r\n");
108
- sandbox.write(input + "\n");
109
- input = "";
110
- } else if (ev.key === "Backspace") {
111
- if (term._core.buffer.x > 0) {
112
- term.write("\b \b");
113
- if (input.length > 0) input = input.substring(0, input.length - 1);
114
- }
115
- } else if (printable) {
116
- if (e.key >= String.fromCharCode(32) && e.key <= String.fromCharCode(126) || e.key >= "\xA0") {
117
- input += e.key;
118
- term.write(e.key);
119
- }
120
- } else if ((ev.ctrlKey || ev.metaKey) && ev.key === "c") {
121
- sandbox.kill();
122
- } else if ((ev.ctrlKey || ev.metaKey) && ev.key === "d") {
123
- sandbox.kill();
124
- } else if ((ev.ctrlKey || ev.metaKey) && ev.key === "v") {
125
- navigator.clipboard.readText().then((text) => {
126
- term.write(text);
127
- sandbox.write(text);
128
- });
129
- }
130
- });
131
- plugin = await registerAllPlugins(term);
132
- dispatch("load");
133
- });
134
- return async () => {
135
- term.dispose();
136
- if (sandbox) await sandbox.clear();
137
- };
138
- });
1
+ <script lang="ts">
2
+ import type { Sandbox } from '../playground/sandbox';
3
+ import { Theme, registerAllPlugins } from './';
4
+ import load from '../playground';
5
+ import { onMount } from 'svelte';
6
+ import '@xterm/xterm/css/xterm.css';
7
+ import type { Terminal } from '@xterm/xterm';
8
+
9
+ type TerminalControl = typeof terminalControl;
10
+ interface Props {
11
+ dark?: boolean;
12
+ path?: string;
13
+ font?: string;
14
+
15
+ onload?: () => void;
16
+ onfinish?: () => void;
17
+ onkey?: (e: KeyboardEvent) => void;
18
+ terminal?: TerminalControl;
19
+ }
20
+
21
+ let {
22
+ dark = false,
23
+ path = '',
24
+ font = "'D2 coding', monospace",
25
+ onload,
26
+ onfinish,
27
+ onkey,
28
+ terminal = $bindable()
29
+ }: Props = $props();
30
+
31
+ let ref = $state<HTMLElement>(),
32
+ clientWidth = $state(0),
33
+ clientHeight = $state(0),
34
+ term = $state<Terminal>(),
35
+ finish = true,
36
+ input = '',
37
+ sandbox: Sandbox,
38
+ first = true,
39
+ tc = 0,
40
+ plugin = $state(),
41
+ ll: string | null = null;
42
+
43
+ function wait() {
44
+ return new Promise<void>((r) => {
45
+ const i = setInterval(() => {
46
+ if (term) {
47
+ clearInterval(i);
48
+ r();
49
+ }
50
+ }, 100);
51
+ });
52
+ }
53
+
54
+ async function initSandbox(language: string) {
55
+ let _tc = ++tc;
56
+ await wait();
57
+ if (sandbox) await sandbox.clear();
58
+ input = '';
59
+ finish = false;
60
+ if (ll !== language) {
61
+ sandbox = await load(language);
62
+ await sandbox.clear();
63
+ ll = language;
64
+ }
65
+ sandbox.output = (output: string) =>
66
+ _tc === tc && term && term.write(output.replaceAll('\n', '\r\n'));
67
+ }
68
+
69
+ function runSandbox(pr: Promise<boolean>) {
70
+ return pr
71
+ .then((x) => {
72
+ term?.write(`\r\nProcess finished after ${sandbox.elapse}ms\u001B[?25l`);
73
+ return x;
74
+ })
75
+ .catch((msg) => {
76
+ term?.write(`\r\n\x1B[1;3;31m${msg}\u001B[?25l`);
77
+ return false;
78
+ })
79
+ .finally((ret) => {
80
+ onfinish?.();
81
+ finish = true;
82
+ if (term) term.options.cursorBlink = false;
83
+ return ret;
84
+ });
85
+ }
86
+
87
+ async function initTerm(blink = true) {
88
+ await wait();
89
+ if (!term) return;
90
+ term.options.cursorBlink = blink;
91
+ term.focus();
92
+
93
+ if (!first) term?.write(`\r\n\x1b[0m`);
94
+ first = false;
95
+ }
96
+
97
+ const terminalControl = {
98
+ async clear() {
99
+ await wait();
100
+ term?.reset();
101
+ term?.write(`\u001B[?25l\x1b[0m\x1b[?25h`);
102
+ if (term) term.options.cursorBlink = false;
103
+ first = true;
104
+ await new Promise((r) => setTimeout(r, 100));
105
+ },
106
+ async prepare(language: string, code: string, log = true, prog) {
107
+ await Promise.all([
108
+ initSandbox(language).then(() => sandbox.load(path, code, log)),
109
+ initTerm(false)
110
+ ]);
111
+ return await runSandbox(sandbox.run(code, true, log, prog));
112
+ },
113
+ async run(language: string, code: string, log = true, prog) {
114
+ await Promise.all([
115
+ initSandbox(language).then(() => sandbox.load(path, code, log)),
116
+ initTerm()
117
+ ]);
118
+ return await runSandbox(sandbox.run(code, false, log, prog));
119
+ },
120
+ async destroy() {
121
+ await wait();
122
+ term?.dispose();
123
+ if (sandbox) await sandbox.clear();
124
+ },
125
+ async write(input: string) {
126
+ await wait();
127
+ term?.write(input + '\r\n');
128
+ sandbox.write?.(input.replaceAll('\r', '\n') + '\n');
129
+ }
130
+ };
131
+
132
+ $effect(() => {
133
+ if (term) {
134
+ if (dark) term.options.theme = Theme.Tango_Dark;
135
+ else term.options.theme = Theme.Tango_Light;
136
+ }
137
+ });
138
+
139
+ $effect(() => {
140
+ let _ = clientWidth + clientHeight;
141
+ (plugin as any)?.fit?.fit?.();
142
+ });
143
+
144
+ $effect(() => {
145
+ terminal = terminalControl;
146
+ });
147
+
148
+ onMount(() => {
149
+ import('@xterm/xterm').then(async ({ Terminal }) => {
150
+ if (!ref) return;
151
+ term = new Terminal({
152
+ theme: dark ? Theme.Tango_Dark : Theme.Tango_Light,
153
+ cursorBlink: false,
154
+ allowTransparency: true,
155
+ fontFamily: font,
156
+ allowProposedApi: true
157
+ });
158
+ term.open(ref);
159
+ term.onKey((e: { key: string; domEvent: KeyboardEvent }) => {
160
+ if (finish || !term) return;
161
+ const ev = e.domEvent;
162
+ const printable = !ev.altKey && !ev.ctrlKey && !ev.metaKey;
163
+ onkey?.(ev);
164
+ if (ev.key === 'Enter') {
165
+ term.write('\r\n');
166
+ sandbox.write?.(input + '\n');
167
+ input = '';
168
+ } else if (ev.key === 'Backspace') {
169
+ if (term.buffer.active.cursorX > 0) {
170
+ term.write('\b \b');
171
+ if (input.length > 0) input = input.substring(0, input.length - 1);
172
+ }
173
+ } else if (printable) {
174
+ if (
175
+ (e.key >= String.fromCharCode(0x20) &&
176
+ e.key <= String.fromCharCode(0x7e)) ||
177
+ e.key >= '\u00a0'
178
+ ) {
179
+ input += e.key;
180
+ term.write(e.key);
181
+ }
182
+ } else if ((ev.ctrlKey || ev.metaKey) && ev.key === 'c') {
183
+ sandbox.kill?.();
184
+ } else if ((ev.ctrlKey || ev.metaKey) && ev.key === 'd') {
185
+ sandbox.kill?.();
186
+ } else if ((ev.ctrlKey || ev.metaKey) && ev.key === 'v') {
187
+ navigator.clipboard.readText().then((text) => {
188
+ term?.write(text);
189
+ sandbox.write?.(text);
190
+ });
191
+ }
192
+ });
193
+ plugin = await registerAllPlugins(term);
194
+
195
+ onload?.();
196
+ });
197
+
198
+ return async () => {
199
+ term?.dispose();
200
+ if (sandbox) await sandbox.clear();
201
+ };
202
+ });
139
203
  </script>
140
204
 
141
205
  <main>
142
- <div bind:this={ref} bind:clientWidth bind:clientHeight></div>
206
+ <div bind:this={ref} bind:clientWidth bind:clientHeight></div>
143
207
  </main>
144
208
 
145
-
146
209
  <style>
147
- main {
148
- padding: 10px;
149
- width: calc(100% - 20px);
150
- height: calc(100% - 20px);
151
- overflow: hidden;
152
- }
153
-
154
- div {
155
- width: 100%;
156
- height: 100%;
157
- }
158
- </style>
210
+ main {
211
+ padding: 10px;
212
+ width: calc(100% - 20px);
213
+ height: calc(100% - 20px);
214
+ overflow: hidden;
215
+ }
216
+
217
+ div {
218
+ width: 100%;
219
+ height: 100%;
220
+ }
221
+ </style>
@@ -1,39 +1,19 @@
1
- import { SvelteComponent } from "svelte";
2
1
  import '@xterm/xterm/css/xterm.css';
3
- declare const __propDef: {
4
- props: {
5
- dark?: boolean;
6
- path?: string;
7
- font?: string;
8
- terminal?: {
9
- clear(): Promise<void>;
10
- prepare(language: any, code: any, log: boolean | undefined, prog: any): Promise<any>;
11
- run(language: any, code: any, log: boolean | undefined, prog: any): Promise<any>;
12
- destroy(): Promise<void>;
13
- write(input: string): Promise<void>;
14
- };
15
- };
16
- events: {
17
- finish: CustomEvent<any>;
18
- key: CustomEvent<any>;
19
- load: CustomEvent<any>;
20
- } & {
21
- [evt: string]: CustomEvent<any>;
22
- };
23
- slots: {};
24
- exports?: {} | undefined;
25
- bindings?: string | undefined;
26
- };
27
- export type TerminalProps = typeof __propDef.props;
28
- export type TerminalEvents = typeof __propDef.events;
29
- export type TerminalSlots = typeof __propDef.slots;
30
- export default class Terminal extends SvelteComponent<TerminalProps, TerminalEvents, TerminalSlots> {
31
- get terminal(): {
2
+ import type { Terminal } from '@xterm/xterm';
3
+ declare const Terminal: import("svelte").Component<{
4
+ dark?: boolean;
5
+ path?: string;
6
+ font?: string;
7
+ onload?: () => void;
8
+ onfinish?: () => void;
9
+ onkey?: (e: KeyboardEvent) => void;
10
+ terminal?: {
32
11
  clear(): Promise<void>;
33
- prepare(language: any, code: any, log: boolean | undefined, prog: any): Promise<any>;
34
- run(language: any, code: any, log: boolean | undefined, prog: any): Promise<any>;
12
+ prepare(language: string, code: string, log: boolean | undefined, prog: any): Promise<boolean>;
13
+ run(language: string, code: string, log: boolean | undefined, prog: any): Promise<boolean>;
35
14
  destroy(): Promise<void>;
36
15
  write(input: string): Promise<void>;
37
16
  };
38
- }
39
- export {};
17
+ }, {}, "terminal">;
18
+ type Terminal = ReturnType<typeof Terminal>;
19
+ export default Terminal;
@@ -1,5 +1,5 @@
1
- import Theme from "./theme";
2
- import registerAllPlugins from "./plugin";
3
- import Terminal from "./Terminal.svelte";
1
+ import Theme from './theme';
2
+ import registerAllPlugins from './plugin';
3
+ import Terminal from './Terminal.svelte';
4
4
  export { Theme, registerAllPlugins };
5
5
  export default Terminal;
@@ -1,5 +1,5 @@
1
- import Theme from "./theme";
2
- import registerAllPlugins from "./plugin";
3
- import Terminal from "./Terminal.svelte";
1
+ import Theme from './theme';
2
+ import registerAllPlugins from './plugin';
3
+ import Terminal from './Terminal.svelte';
4
4
  export { Theme, registerAllPlugins };
5
5
  export default Terminal;
package/package.json CHANGED
@@ -1,73 +1,73 @@
1
1
  {
2
- "name": "wasm-idle",
3
- "version": "0.1.0",
4
- "description": "A simple web app to run C++/Python code in the browser",
5
- "license": "MIT",
6
- "type": "module",
7
- "scripts": {
8
- "dev": "vite dev",
9
- "build": "vite build && npm run package",
10
- "preview": "vite preview",
11
- "package": "svelte-kit sync && svelte-package && publint",
12
- "prepublishOnly": "npm run package",
13
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
14
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
15
- "lint": "prettier --check . && eslint .",
16
- "format": "prettier --write ."
17
- },
18
- "devDependencies": {
19
- "@eslint/js": "^9.8.0",
20
- "@sveltejs/adapter-auto": "^3.2.2",
21
- "@sveltejs/adapter-static": "^3.0.2",
22
- "@sveltejs/kit": "^2.5.20",
23
- "@sveltejs/package": "^2.3.2",
24
- "@sveltejs/vite-plugin-svelte": "^3.1.1",
25
- "@typescript-eslint/eslint-plugin": "^8.0.1",
26
- "@typescript-eslint/parser": "^8.0.1",
27
- "eslint": "^9.8.0",
28
- "eslint-config-prettier": "^9.1.0",
29
- "eslint-plugin-svelte": "^2.43.0",
30
- "eslint-plugin-svelte3": "^4.0.0",
31
- "gh-pages": "^6.1.1",
32
- "globals": "^15.9.0",
33
- "monaco-editor": "^0.50.0",
34
- "prettier": "^3.3.3",
35
- "prettier-plugin-svelte": "^3.2.6",
36
- "publint": "^0.2.9",
37
- "svelte": "^4.2.18",
38
- "svelte-check": "^3.8.5",
39
- "tslib": "^2.6.3",
40
- "typescript": "^5.5.4",
41
- "typescript-eslint": "^8.0.1",
42
- "vite": "^5.3.5"
43
- },
44
- "dependencies": {
45
- "@xterm/addon-canvas": "^0.7.0",
46
- "@xterm/addon-fit": "^0.10.0",
47
- "@xterm/addon-ligatures": "^0.9.0",
48
- "@xterm/addon-search": "^0.15.0",
49
- "@xterm/addon-serialize": "^0.13.0",
50
- "@xterm/addon-unicode11": "^0.8.0",
51
- "@xterm/addon-web-links": "^0.11.0",
52
- "@xterm/addon-webgl": "^0.18.0",
53
- "@xterm/xterm": "^5.5.0",
54
- "@zip.js/zip.js": "^2.7.48",
55
- "pyodide": "0.26.2"
56
- },
57
- "files": [
58
- "dist",
59
- "!dist/**/*.test.*",
60
- "!dist/**/*.spec.*"
61
- ],
62
- "peerDependencies": {
63
- "svelte": "^4.2.18"
64
- },
65
- "exports": {
66
- ".": {
67
- "types": "./dist/index.d.ts",
68
- "svelte": "./dist/index.js"
69
- }
70
- },
71
- "svelte": "./dist/index.js",
72
- "types": "./dist/index.d.ts"
2
+ "name": "wasm-idle",
3
+ "version": "0.2.1",
4
+ "description": "A simple web app to run C++/Python code in the browser",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "scripts": {
8
+ "dev": "vite dev",
9
+ "build": "vite build && npm run package",
10
+ "preview": "vite preview",
11
+ "package": "svelte-kit sync && svelte-package && publint",
12
+ "prepublishOnly": "npm run package",
13
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
14
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
15
+ "lint": "prettier --check . && eslint .",
16
+ "format": "prettier --write ."
17
+ },
18
+ "devDependencies": {
19
+ "@eslint/js": "^9.19.0",
20
+ "@sveltejs/adapter-auto": "^4.0.0",
21
+ "@sveltejs/adapter-static": "^3.0.8",
22
+ "@sveltejs/kit": "^2.17.1",
23
+ "@sveltejs/package": "^2.3.10",
24
+ "@sveltejs/vite-plugin-svelte": "^5.0.3",
25
+ "@typescript-eslint/eslint-plugin": "^8.23.0",
26
+ "@typescript-eslint/parser": "^8.23.0",
27
+ "eslint": "^9.19.0",
28
+ "eslint-config-prettier": "^10.0.1",
29
+ "eslint-plugin-svelte": "^2.46.1",
30
+ "eslint-plugin-svelte3": "^4.0.0",
31
+ "gh-pages": "^6.3.0",
32
+ "globals": "^15.14.0",
33
+ "monaco-editor": "^0.52.2",
34
+ "prettier": "^3.4.2",
35
+ "prettier-plugin-svelte": "^3.3.3",
36
+ "svelte": "^5.19.7",
37
+ "svelte-check": "^4.1.4",
38
+ "tslib": "^2.8.1",
39
+ "typescript": "^5.7.3",
40
+ "typescript-eslint": "^8.23.0",
41
+ "vite": "^6.0.11"
42
+ },
43
+ "dependencies": {
44
+ "@xterm/addon-canvas": "^0.7.0",
45
+ "@xterm/addon-fit": "^0.10.0",
46
+ "@xterm/addon-ligatures": "^0.9.0",
47
+ "@xterm/addon-search": "^0.15.0",
48
+ "@xterm/addon-serialize": "^0.13.0",
49
+ "@xterm/addon-unicode11": "^0.8.0",
50
+ "@xterm/addon-web-links": "^0.11.0",
51
+ "@xterm/addon-webgl": "^0.18.0",
52
+ "@xterm/xterm": "^5.5.0",
53
+ "@zip.js/zip.js": "^2.7.57",
54
+ "publint": "^0.3.3",
55
+ "pyodide": "0.26.2"
56
+ },
57
+ "files": [
58
+ "dist",
59
+ "!dist/**/*.test.*",
60
+ "!dist/**/*.spec.*"
61
+ ],
62
+ "peerDependencies": {
63
+ "svelte": "^4.2.18"
64
+ },
65
+ "exports": {
66
+ ".": {
67
+ "types": "./dist/index.d.ts",
68
+ "svelte": "./dist/index.js"
69
+ }
70
+ },
71
+ "svelte": "./dist/index.js",
72
+ "types": "./dist/index.d.ts"
73
73
  }