/ concept-collection / minwebide-demo
Sign in
concept-collection / minwebide-demo
minwebide-demo / src / runners.ts
78 lines · 2.9 KBBlameHistoryRaw
1import type { FileRunner } from 'minwebide';
2import { renderPlot, type PlotSpec } from './plot';
4// Two example file runners. Execution strategy is entirely app-defined —
5// here: in-page JavaScript evaluation with a captured console, and a trivial
6// text statistics command. Output goes to VS Code-style output channels.
8function formatValue(value: unknown): string {
9 if (typeof value === 'string') {
10 return value;
11 }
12 if (value instanceof Error) {
13 return value.stack ?? value.message;
14 }
15 try {
16 return JSON.stringify(value, undefined, 2) ?? String(value);
17 } catch {
18 return String(value);
19 }
22/**
23 * Runs JavaScript files in the page (the app is trusted code; a real app
24 * might instead target a worker, an iframe sandbox, or Pyodide).
25 * console.* is captured into the output channel; a plot() function renders
26 * charts into the runner's view in the secondary side bar.
27 */
28export const jsRunner: FileRunner = {
29 id: 'demo.runJavaScript',
30 displayName: 'Run JavaScript',
31 selector: [{ filenamePattern: '*.{js,mjs}' }],
32 async run({ uri, getText, output, getView }) {
33 output.info(`Running ${uri.path}`);
34 const code = await getText();
35 const capturedConsole = {
36 log: (...args: unknown[]) => output.appendLine(args.map(formatValue).join(' ')),
37 info: (...args: unknown[]) => output.appendLine(args.map(formatValue).join(' ')),
38 warn: (...args: unknown[]) => output.warn(args.map(formatValue).join(' ')),
39 error: (...args: unknown[]) => output.error(args.map(formatValue).join(' ')),
40 };
41 let plotsEl: HTMLElement | undefined;
42 const plot = (spec: PlotSpec | number[]) => {
43 if (!plotsEl) {
44 // first plot of this run: take over the runner's side bar
45 // view and reveal it
46 const view = getView();
47 view.element.textContent = '';
48 plotsEl = document.createElement('div');
49 plotsEl.className = 'demo-plots';
50 view.element.appendChild(plotsEl);
51 view.show();
52 }
53 renderPlot(plotsEl, Array.isArray(spec) ? { y: spec } : spec);
54 };
55 const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor as new (...args: string[]) => (...fnArgs: unknown[]) => Promise<unknown>;
56 const started = performance.now();
57 try {
58 await new AsyncFunction('console', 'plot', code)(capturedConsole, plot);
59 output.info(`Finished in ${Math.round(performance.now() - started)}ms`);
60 } catch (error) {
61 output.error(error instanceof Error ? error : String(error));
62 }
63 },
64};
66export const wordCountRunner: FileRunner = {
67 id: 'demo.wordCount',
68 displayName: 'Word Count',
69 selector: [{ filenamePattern: '*.{md,txt}' }],
70 async run({ uri, getText, output }) {
71 const text = await getText();
72 const lines = text.split(/\r\n|\r|\n/).length;
73 const words = (text.match(/\S+/g) ?? []).length;
74 output.info(`${uri.path}: ${lines} lines, ${words} words, ${text.length} characters`);
75 },
76};
78export const demoRunners: FileRunner[] = [jsRunner, wordCountRunner];
moveopenescclose