From 6cee4e26e43def95016b93149cf45b1a7a48e3fe Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 18 Aug 2021 15:44:13 -0500 Subject: [PATCH] scripting: print(), css, palette layout, flex make syncdev/prod: fixed mime type upload --- Makefile | 4 +- css/ui.css | 43 ++++++++++--- package.json | 3 +- src/common/script/env.ts | 34 ++++++++-- src/common/script/lib/bitmap.ts | 96 ++++++++++++++++++++++++++-- src/common/script/lib/color.ts | 16 ++++- src/common/script/lib/io.ts | 4 +- src/common/script/lib/scriptui.ts | 7 +- src/common/script/ui/notebook.ts | 102 +++++++++++++++++------------- src/common/util.ts | 31 +++++++++ src/platform/vcs.ts | 2 +- src/worker/tools/script.ts | 1 + 12 files changed, 269 insertions(+), 74 deletions(-) diff --git a/Makefile b/Makefile index 3c449907..9596fd11 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ VERSION := $(shell git tag -l --points-at HEAD) syncdev: distro cp config.js $(TMP) #aws --profile pzp s3 sync --follow-symlinks $(TMP)/ s3://8bitworkshop.com/dev/ - s3cmd -c ~/.s3pzp sync -MFP $(TMP)/ s3://8bitworkshop.com/dev/ + s3cmd -c ~/.s3pzp sync -MFP --no-mime-magic $(TMP)/ s3://8bitworkshop.com/dev/ rsync --stats -riltz --delete --chmod=a+rx -e "ssh" $(TMP)/ config.js $(RSYNC_PATH)/dev/ syncprod: distro @@ -63,5 +63,5 @@ syncprod: distro read cp config.js $(TMP) #aws --profile pzp s3 sync --follow-symlinks $(TMP)/ s3://8bitworkshop.com/v$(VERSION)/ - s3cmd -c ~/.s3pzp sync -MFP $(TMP)/ config.js s3://8bitworkshop.com/v$(VERSION)/ + s3cmd -c ~/.s3pzp sync -MFP --no-mime-magic $(TMP)/ config.js s3://8bitworkshop.com/v$(VERSION)/ rsync --stats -riltz --chmod=a+rx -e "ssh" $(TMP)/ config.js $(RSYNC_PATH)/v$(VERSION)/ diff --git a/css/ui.css b/css/ui.css index 8fd934d5..59bffc08 100644 --- a/css/ui.css +++ b/css/ui.css @@ -718,6 +718,7 @@ div.asset_toolbar { padding-top: 0.5em; } .tree-header { + display: flex; border: 2px solid #555; border-radius:8px; color: #fff; @@ -730,8 +731,11 @@ div.asset_toolbar { padding-right:0.75em; font-size: small; } +.tree-key { + padding-right:1em; +} .tree-value { - float:right; + margin-left:auto; font-weight:normal; padding-right:1em; } @@ -779,24 +783,45 @@ div.asset_toolbar { pointer-events: auto; } .scripting-cell canvas { - height: 20vw; + min-height: 20em; max-width: 95%; border: 2px solid #222; outline-color: #ccc; background: #000; - padding: 6px; - margin: 6px; + padding: 4px; + margin: 1px; + image-rendering: pixelated; + image-rendering: crisp-edges; } .scripting-cell canvas:hover { - outline:none; border-color:#ccc; } +.scripting-flex canvas { + min-height: 2vw; + max-height: 20vw; +} +div.scripting-editor { + width: 100%; +} +.scripting-editor canvas { + min-height: 30vw; + max-height: 50vw; +} div.scripting-color { - padding:0.1em; - margin:0.1em; + padding: 0.5em; +} +div.scripting-color span { + visibility: hidden; +} +div.scripting-color:hover span { + visibility: visible; } div.scripting-grid { display: grid; - grid-template-columns: repeat( auto-fit, minmax(2em, 1fr) ); + grid-template-columns: repeat( auto-fill, minmax(4em, 1fr) ); justify-items: center; -} \ No newline at end of file +} +div.scripting-flex { + display: flex; + flex-wrap: wrap; +} diff --git a/package.json b/package.json index 44ed7be9..1c45e8c6 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,8 @@ "test-worker": "NODE_PATH=$(pwd) mocha --timeout 60000 test/cli/testworker.js", "test-platforms": "NODE_PATH=$(pwd) mocha --timeout 60000 test/cli/testplatforms.js", "test-verilog": "NODE_PATH=$(pwd) mocha --timeout 60000 --reporter mocha-simple-html-reporter --reporter-options output=test/output/verilog.html test/cli/testverilog.js", - "test-web": "nightwatch -e chrome test/web", + "test-web-quick": "nightwatch -e chrome test/web/testembed.js", + "test-web-all": "nightwatch -e chrome test/web", "start": "electron .", "fuzzbasic": "jsfuzz gen/common/basic/fuzz.js ~/basic/corpus/ --versifier false", "fuzzhdl": "jsfuzz -r binaryen gen/common/hdl/fuzz.js ~/verilator/corpus/ --versifier false", diff --git a/src/common/script/env.ts b/src/common/script/env.ts index c4ad6d03..9cecdc94 100644 --- a/src/common/script/env.ts +++ b/src/common/script/env.ts @@ -55,14 +55,19 @@ export class Environment { obj: {}; seq: number; declvars : {[name : string] : acorn.Node}; + builtins : {} constructor( public readonly globalenv: any, public readonly path: string ) { var badlst = Object.getOwnPropertyNames(this.globalenv).filter(name => GLOBAL_GOODLIST.indexOf(name) < 0); + this.builtins = { + print: (...args) => this.print(args), + ...IMPORTS + } this.preamble = `'use strict';var ${badlst.join(',')};`; - for (var impname in IMPORTS) { + for (var impname in this.builtins) { this.preamble += `var ${impname}=$$.${impname};` } this.preamble += '{\n'; @@ -73,6 +78,11 @@ export class Environment { console.log(varname, obj); throw new RuntimeError(obj && obj.loc, msg); } + print(args: any[]) { + if (args && args.length > 0 && args[0] != null) { + this.obj[`$$print__${this.seq++}`] = args.length == 1 ? args[0] : args; + } + } preprocess(code: string): string { this.declvars = {}; this.seq = 0; @@ -94,15 +104,20 @@ export class Environment { } }; const result = yufka(code, options, (node, { update, source, parent }) => { - function isTopLevel() { + const isTopLevel = () => { return parent() && parent().type === 'ExpressionStatement' && parent(2) && parent(2).type === 'Program'; } - let left = node['left']; + const convertTopToPrint = () => { + if (isTopLevel()) update(`print(${source()});`) + } + const left = node['left']; switch (node.type) { // error on forbidden keywords case 'Identifier': if (GLOBAL_BADLIST.indexOf(source()) >= 0) { update(`__FORBIDDEN__KEYWORD__${source()}__`) // TODO? how to preserve line number? + } else { + convertTopToPrint(); } break; // x = expr --> var x = expr (first use) @@ -118,10 +133,19 @@ export class Environment { } } break; + // convert lone expressions to print() + case 'UnaryExpression': + case 'BinaryExpression': + case 'CallExpression': + case 'MemberExpression': + convertTopToPrint(); + break; // literal comments case 'Literal': - if (isTopLevel() && typeof node['value'] === 'string') { + if (typeof node['value'] === 'string' && isTopLevel()) { update(`this.$$doc__${this.seq++} = { literaltext: ${source()} };`); + } else { + convertTopToPrint(); } break; } @@ -133,7 +157,7 @@ export class Environment { code = this.preprocess(code); this.obj = {}; const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor; - const fn = new AsyncFunction('$$', this.preamble + code + this.postamble).bind(this.obj, IMPORTS); + const fn = new AsyncFunction('$$', this.preamble + code + this.postamble).bind(this.obj, this.builtins); await fn.call(this); this.checkResult(this.obj, new Set(), []); } diff --git a/src/common/script/lib/bitmap.ts b/src/common/script/lib/bitmap.ts index 59970fa9..18eec1e1 100644 --- a/src/common/script/lib/bitmap.ts +++ b/src/common/script/lib/bitmap.ts @@ -4,6 +4,7 @@ import * as fastpng from 'fast-png'; import { Palette } from './color'; import * as io from './io' import * as color from './color' +import { findIntegerFactors, RGBA } from '../../util'; export type PixelMapFunction = (x: number, y: number) => number; @@ -19,6 +20,7 @@ export abstract class AbstractBitmap { abstract setarray(arr: ArrayLike) : AbstractBitmap; abstract set(x: number, y: number, val: number) : AbstractBitmap; abstract get(x: number, y: number): number; + abstract getrgba(x: number, y: number): number; inbounds(x: number, y: number): boolean { return (x >= 0 && x < this.width && y >= 0 && y < this.height); @@ -45,6 +47,17 @@ export abstract class AbstractBitmap { dest.assign((x, y) => this.get(x + srcx, y + srcy)); return dest; } + blit(src: BitmapType, dest: BitmapType, + destx: number, desty: number, + srcx: number, srcy: number) + { + for (var y=0; y { @@ -70,6 +83,9 @@ export class RGBABitmap extends AbstractBitmap { get(x: number, y: number): number { return this.inbounds(x,y) ? this.rgba[y * this.width + x] : 0; } + getrgba(x: number, y: number): number { + return this.get(x, y); + } blank(width: number, height: number) : RGBABitmap { return new RGBABitmap(width, height); } @@ -106,7 +122,6 @@ export abstract class MappedBitmap extends AbstractBitmap { get(x: number, y: number): number { return this.inbounds(x,y) ? this.pixels[y * this.width + x] : 0; } - abstract getRGBAForIndex(index: number): number; } export class IndexedBitmap extends MappedBitmap { @@ -122,8 +137,8 @@ export class IndexedBitmap extends MappedBitmap { this.palette = color.palette.colors(1 << this.bitsPerPixel); } - getRGBAForIndex(index: number): number { - return this.palette.colors[index]; + getrgba(x: number, y: number): number { + return this.palette && this.palette.colors[this.get(x, y)]; } blank(width: number, height: number) : IndexedBitmap { let bitmap = new IndexedBitmap(width, height, this.bitsPerPixel); @@ -153,9 +168,64 @@ export function decode(arr: Uint8Array, fmt: PixelEditorImageFormat) { // TODO: guess if missing w/h/count? // TODO: reverse mapping // TODO: maybe better composable functions - return pixels.map(data => new IndexedBitmap(fmt.w, fmt.h, fmt.bpp | 1, data)); + let bpp = (fmt.bpp||1) * (fmt.np||1); + return pixels.map(data => new IndexedBitmap(fmt.w, fmt.h, bpp, data)); } +export interface BitmapAnalysis { + min: {w: number, h: number}; + max: {w: number, h: number}; +} + +export function analyze(bitmaps: BitmapType[]) { + let r = {min:{w:0,h:0}, max:{w:0,h:0}}; + for (let bmp of bitmaps) { + if (!(bmp instanceof AbstractBitmap)) return null; + r.min.w = Math.min(bmp.width); + r.max.w = Math.max(bmp.width); + r.min.h = Math.min(bmp.height); + r.max.h = Math.max(bmp.height); + } + return r; +} + +export interface MontageOptions { + analysis?: BitmapAnalysis; + gap?: number; + aspect?: number; +} + +export function montage(bitmaps: BitmapType[], options?: MontageOptions) { + let minmax = (options && options.analysis) || analyze(bitmaps); + if (minmax == null) throw new Error(`Expected an array of bitmaps`); + let hitrects = []; + let aspect = (options && options.aspect) || 1; + let gap = (options && options.gap) || 0; + if (minmax.min.w == minmax.max.w && minmax.min.h == minmax.max.h) { + let totalPixels = minmax.min.w * minmax.min.h * bitmaps.length; + let factors = findIntegerFactors(totalPixels, minmax.max.w, minmax.max.h, aspect); + let columns = Math.ceil(factors.a / minmax.min.w); // TODO: rounding? + let rows = Math.ceil(factors.b / minmax.min.h); + let result = new RGBABitmap(factors.a + gap * (columns-1), factors.b + gap * (rows-1)); + let x = 0; + let y = 0; + bitmaps.forEach((bmp) => { + result.blit(bmp, result, x, y, 0, 0); + hitrects.push({x, y, w: bmp.width, h: bmp.height }) + x += bmp.width + gap; + if (x >= result.width) { + x = 0; + y += bmp.height + gap; + } + }) + return result; + } else { + throw new Error(`combine() only supports uniformly-sized images right now`); // TODO + } +} + +///// + export namespace png { export function read(url: string): BitmapType { return decode(io.readbin(url)); @@ -325,6 +395,22 @@ export type PixelEditorImageFormat = { skip?:number aspect?:number }; + + function remapBits(x:number, arr:number[]) : number { + if (!arr) return x; + var y = 0; + for (var i=0; i, fmt:PixelEditorImageFormat) : Uint8Array[] { var width = fmt.w; @@ -346,7 +432,7 @@ export type PixelEditorImageFormat = { var shift = 0; for (var x=0; x 65536) { + throw new Error("Palettes cannot have more than 2^16 (65536) colors."); + } +} + export class Palette { readonly colors: Uint32Array; constructor(arg: number | any[] | Uint32Array) { // TODO: more array types if (typeof arg === 'number') { + checkCount(arg); this.colors = new Uint32Array(arg); } else if (arg instanceof Uint32Array) { this.colors = new Uint32Array(arg); @@ -42,9 +49,10 @@ export function rgba(r: number, g: number, b: number, a: number) : number; export function rgba(obj: ColorSource, g?: number, b?: number, a?: number) : number { if (typeof obj === 'number') { let r = obj; - if (g != null && b != null) + if (typeof g === 'number' && typeof b === 'number') return ((r & 0xff) << 0) | ((g & 0xff) << 8) | ((b & 0xff) << 16) | ((a & 0xff) << 24); - return obj; + else + return obj; } if (typeof obj !== 'string' && isArray(obj) && typeof obj[0] === 'number') { let arr = obj; @@ -75,6 +83,7 @@ type ColorGenFunc = (index: number) => number; export namespace palette { export function from(obj: number | any[] | Uint32Array | ColorGenFunc, count?: number) { + checkCount(count); if (typeof obj === 'function') { if (!count) throw new Error(`You must also pass the number of colors to generate.`) var pal = new Palette(count); @@ -87,7 +96,7 @@ export namespace palette { } } export function mono() { - return greys(1); + return greys(2); } function rgb2() { return new Palette([ @@ -124,6 +133,7 @@ export namespace palette { } } export function helix(count: number) { + checkCount(count); return new Palette(chroma.cubehelix().scale().colors(count)); } export function factors(count: number, mult?: number) { diff --git a/src/common/script/lib/io.ts b/src/common/script/lib/io.ts index d0676ba0..1752f3a7 100644 --- a/src/common/script/lib/io.ts +++ b/src/common/script/lib/io.ts @@ -76,7 +76,7 @@ export function fetchurl(url: string, type?: 'binary' | 'text'): FileData { export function readnocache(url: string, type?: 'binary' | 'text'): FileData { if (url.startsWith('http:') || url.startsWith('https:')) { - return fetchurl(url); + return fetchurl(url, type); } if ($$store) { return $$store.getFileData(url); @@ -93,6 +93,8 @@ export function read(url: string, type?: 'binary' | 'text'): FileData { } let data = readnocache(url, type); if (data == null) throw new Error(`Cannot find resource "${url}"`); + if (type === 'text' && typeof data !== 'string') throw new Error(`Resource "${url}" is not a string`); + if (type === 'binary' && !(data instanceof Uint8Array)) throw new Error(`Resource "${url}" is not a binary file`); $$cache.set(cachekey, data); return data; } diff --git a/src/common/script/lib/scriptui.ts b/src/common/script/lib/scriptui.ts index c311125b..6a7034a3 100644 --- a/src/common/script/lib/scriptui.ts +++ b/src/common/script/lib/scriptui.ts @@ -6,7 +6,8 @@ export class ScriptUISliderType { value: number; constructor( readonly min: number, - readonly max: number + readonly max: number, + readonly step: number ) { } } @@ -25,6 +26,6 @@ export class ScriptUISlider extends ScriptUISliderType implements io.Loadable { } } -export function slider(min: number, max: number) { - return new ScriptUISlider(min, max); +export function slider(min: number, max: number, step?: number) { + return new ScriptUISlider(min, max, step || 1); } diff --git a/src/common/script/ui/notebook.ts b/src/common/script/ui/notebook.ts index e15faa43..ed0626b8 100644 --- a/src/common/script/ui/notebook.ts +++ b/src/common/script/ui/notebook.ts @@ -2,7 +2,7 @@ import { BitmapType, IndexedBitmap, RGBABitmap } from "../lib/bitmap"; import { Component, render, h, ComponentType } from 'preact'; import { Cell } from "../env"; -import { hex, rgb2bgr, RGBA } from "../../util"; +import { findIntegerFactors, hex, rgb2bgr, RGBA } from "../../util"; import { dumpRAM } from "../../emu"; // TODO: can't call methods from this end import { Palette } from "../lib/color"; @@ -10,6 +10,30 @@ import { ScriptUISlider, ScriptUISliderType } from "../lib/scriptui"; import { current_project } from "../../../ide/ui"; const MAX_STRING_LEN = 100; +const DEFAULT_ASPECT = 1; + +interface ObjectStats { + type: 'prim' | 'complex' | 'bitmap' + width: number + height: number + units: 'em' | 'px' +} + +// TODO +class ObjectAnalyzer { + recurse(obj: any) : ObjectStats { + if (typeof obj === 'string') { + return { type: 'prim', width: obj.length, height: 1, units: 'em' } + } else if (obj instanceof Uint8Array) { + return { type: 'complex', width: 60, height: Math.ceil(obj.length / 16), units: 'em' } + } else if (typeof obj === 'object') { + let stats : ObjectStats = { type: 'complex', width: 0, height: 0, units: 'em'}; + return stats; // TODO + } else { + return { type: 'prim', width: 12, height: 1, units: 'em' } + } + } +} interface ColorComponentProps { rgbavalue: number; @@ -18,13 +42,17 @@ interface ColorComponentProps { class ColorComponent extends Component { render(virtualDom, containerNode, replaceNode) { let rgb = this.props.rgbavalue & 0xffffff; - var htmlcolor = `#${hex(rgb2bgr(rgb),6)}`; - var textcol = (rgb & 0x008000) ? 'black' : 'white'; + let bgr = rgb2bgr(rgb); + var htmlcolor = `#${hex(bgr,6)}`; + var textcolor = (rgb & 0x008000) ? 'black' : 'white'; + var printcolor = hex(rgb & 0xffffff, 6); // TODO: show index instead? return h('div', { class: 'scripting-color', - style: `background-color: ${htmlcolor}; color: ${textcol}`, + style: `background-color: ${htmlcolor}; color: ${textcolor}`, alt: htmlcolor, // TODO - }, '\u00a0'); + }, [ + h('span', { }, printcolor ) + ]); } } @@ -45,7 +73,6 @@ class BitmapComponent extends Component { } render(virtualDom, containerNode, replaceNode) { return h('canvas', { - class: 'pixelated', width: this.props.width, height: this.props.height, ...this.props @@ -124,7 +151,7 @@ class BitmapEditor extends Component { } return h('div', { tabIndex: 0, - class: this.state.isEditing ? 'scripting-cell' : '' // TODO + class: this.state.isEditing ? 'scripting-editor' : '' // TODO }, [ bitmapRender, okCancel, @@ -163,6 +190,8 @@ class ObjectKeyValueComponent extends Component this.toggleExpand() : null }, [ - this.props.name + "", - h('span', { class: 'tree-value' }, [ - getShortName(this.props.object) - ]) + h('span', { class: 'tree-key' }, [ propName, expandable ]), + h('span', { class: 'tree-value' }, [ getShortName(this.props.object) ]) ]), this.state.expanded ? objectToContentsDiv(this.props.object, this.props.objpath) : null ]); @@ -243,26 +270,25 @@ function objectToDiv(object: any, name: string, objpath: string) { if (object == null) { return object + ""; } else if (object['uitype']) { - children.push(h(UISliderComponent, { iokey: objpath, uiobject: object })); + return h(UISliderComponent, { iokey: objpath, uiobject: object }); } else if (object['literaltext']) { - children.push(h("pre", { }, [ object['literaltext'] ])); // TODO - } else if (isIndexedBitmap(object)) { - //Object.setPrototypeOf(object, IndexedBitmap.prototype); // TODO: use Object.create()? - addBitmapComponent(children, object); - } else if (isRGBABitmap(object)) { - //Object.setPrototypeOf(object, RGBABitmap.prototype); // TODO: use Object.create()? - addBitmapComponent(children, object); + return h("pre", { }, [ object['literaltext'] ]); + } else if (isIndexedBitmap(object) || isRGBABitmap(object)) { + return h(BitmapEditor, { bitmap: object, width: object.width, height: object.height }); } else if (isPalette(object)) { - // TODO: make sets of 2/4/8/16/etc - props.class += ' scripting-grid '; - object.colors.forEach((val) => { - children.push(h(ColorComponent, { rgbavalue: val })); - }) + if (object.colors.length <= 64) { + props.class += ' scripting-flex '; + object.colors.forEach((val) => { + children.push(h(ColorComponent, { rgbavalue: val })); + }) + return h('div', props, children); + } else { + let {a,b} = findIntegerFactors(object.colors.length, 1, 1, DEFAULT_ASPECT); + return objectToDiv({ rgba: object.colors, width: a, height: b }, name, objpath); + } } else { return h(ObjectKeyValueComponent, { name, object, objpath }, []); } - let div = h('div', props, children); - return div; } function fixedArrayToDiv(tyarr: Array, bpel: number, objpath: string) { @@ -288,11 +314,7 @@ function objectToContentsDiv(object: {} | [], objpath: string) { } let objectEntries = Object.entries(object); let objectDivs = objectEntries.map(entry => objectToDiv(entry[1], entry[0], `${objpath}.${entry[1]}`)); - return h('div', {}, objectDivs); -} - -function addBitmapComponent(children, bitmap: BitmapType) { - children.push(h(BitmapEditor, { bitmap: bitmap, width: bitmap.width, height: bitmap.height })); + return h('div', { class: 'scripting-flex' }, objectDivs); } interface UISliderComponentProps { @@ -307,17 +329,16 @@ class UISliderComponent extends Component { this.props.iokey, h('input', { type: 'range', - min: slider.min, - max: slider.max, - value: this.props.uiobject.value, + min: slider.min / slider.step, + max: slider.max / slider.step, + value: this.props.uiobject.value / slider.step, onInput: (ev) => { - let newValue = { value: ev.target.value }; - slider.value = parseFloat(ev.target.value); + let newValue = { value: parseFloat(ev.target.value) * slider.step }; this.setState(this.state); current_project.updateDataItems([{key: this.props.iokey, value: newValue}]); } }, []), - slider.value + getShortName(slider.value) ]); } } @@ -328,22 +349,15 @@ export class Notebook { public readonly maindiv: HTMLElement ) { maindiv.classList.add('vertical-scroll'); - //maindiv.classList.add('container') } updateCells(cells: Cell[]) { let hTree = cells.map(cell => { - //return objectToDiv(cell.object, cell.id) return h('div', { class: 'scripting-cell', key: `${cell.id}__cell` }, [ objectToDiv(cell.object, cell.id, cell.id) ]) - /* - let cellDiv = objectToDiv(cell.object, cell.id); - cellDiv.props['class'] += ' scripting-cell '; - return cellDiv; - */ }); render(hTree, this.maindiv); } diff --git a/src/common/util.ts b/src/common/util.ts index c5c526cd..df2a5e12 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -631,3 +631,34 @@ export function escapeHTML(s: string): string { return s.replace(/[&]/g, '&').replace(/[<]/g, '<').replace(/[>]/g, '>'); } +// lame factorization for displaying bitmaps +// returns a > b such that a * b == x (or higher), a >= mina, b >= minb +export function findIntegerFactors(x: number, mina: number, minb: number, aspect: number) : {a: number, b: number} { + let a = x; + let b = 1; + if (minb > 1 && minb < a) { + a = Math.ceil(x / minb); + b = minb; + } + while (a > b) { + let a2 = a; + let b2 = b; + if ((a & 1) == 0) { + b2 = b * 2; + a2 = a / 2; + } + if ((a % 3) == 0) { + b2 = b * 3; + a2 = a / 3; + } + if ((a % 5) == 0) { + b2 = b * 5; + a2 = a / 5; + } + if (a2 < mina) break; + if (a2 < b2 * aspect) break; + a = a2; + b = b2; + } + return {a, b}; +} diff --git a/src/platform/vcs.ts b/src/platform/vcs.ts index 10f184bd..b4398eb6 100644 --- a/src/platform/vcs.ts +++ b/src/platform/vcs.ts @@ -70,7 +70,7 @@ class VCSPlatform extends BasePlatform { async start() { var self : VCSPlatform = this; // load Javatari and configure settings - await loadScript("javatari.js/release/javatari/javatari.js"); + await loadScript("javatari/javatari.js"); Javatari.AUTO_START = false; Javatari.SHOW_ERRORS = false; Javatari.CARTRIDGE_CHANGE_DISABLED = true; diff --git a/src/worker/tools/script.ts b/src/worker/tools/script.ts index bca3454c..b3eb1ae6 100644 --- a/src/worker/tools/script.ts +++ b/src/worker/tools/script.ts @@ -27,6 +27,7 @@ export async function runJavascript(step: BuildStep): Promise { let output : RunResult = { cells, state }; return { output: output }; } catch (e) { + console.log(e); return { errors: env.extractErrors(e) }; } finally { io.$$setupFS(null);