draft
This commit is contained in:
parent
6d7436e829
commit
a70ccbd4a5
5 changed files with 692 additions and 14 deletions
|
@ -1,14 +0,0 @@
|
|||
+++
|
||||
title = "enterprise: a new ui framework"
|
||||
date = 2020-02-11
|
||||
draft = true
|
||||
|
||||
[taxonomies]
|
||||
tags = []
|
||||
+++
|
||||
|
||||
This past weekend, while on my trip to Minneapolis, I completed a very early prototype of "enterprise", a new UI framework I've been kind of envisioning over the past couple of weeks. While the UI framework is mainly targeted at web apps, the hope is that with a bit more effort, native UIs can be produced with almost no changes to existing applications. Before I begin to describe how it works, I'd like to acknowledge [Nathan Ringo][1] for his massively helpful feedback in both the ideation and the implementation process.
|
||||
|
||||
## Goals of the project
|
||||
|
||||
[1]: https://remexre.xyz
|
639
content/enterprise/prototype/helloworld.js
vendored
Normal file
639
content/enterprise/prototype/helloworld.js
vendored
Normal file
|
@ -0,0 +1,639 @@
|
|||
"use strict";
|
||||
|
||||
if( typeof Rust === "undefined" ) {
|
||||
var Rust = {};
|
||||
}
|
||||
|
||||
(function( root, factory ) {
|
||||
if( typeof define === "function" && define.amd ) {
|
||||
define( [], factory );
|
||||
} else if( typeof module === "object" && module.exports ) {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
Rust.helloworld = factory();
|
||||
}
|
||||
}( this, function() {
|
||||
return (function( module_factory ) {
|
||||
var instance = module_factory();
|
||||
|
||||
if( typeof process === "object" && typeof process.versions === "object" && typeof process.versions.node === "string" ) {
|
||||
var fs = require( "fs" );
|
||||
var path = require( "path" );
|
||||
var wasm_path = path.join( __dirname, "helloworld.wasm" );
|
||||
var buffer = fs.readFileSync( wasm_path );
|
||||
var mod = new WebAssembly.Module( buffer );
|
||||
var wasm_instance = new WebAssembly.Instance( mod, instance.imports );
|
||||
return instance.initialize( wasm_instance );
|
||||
} else {
|
||||
var file = fetch( "helloworld.wasm", {credentials: "same-origin"} );
|
||||
|
||||
var wasm_instance = ( typeof WebAssembly.instantiateStreaming === "function"
|
||||
? WebAssembly.instantiateStreaming( file, instance.imports )
|
||||
.then( function( result ) { return result.instance; } )
|
||||
|
||||
: file
|
||||
.then( function( response ) { return response.arrayBuffer(); } )
|
||||
.then( function( bytes ) { return WebAssembly.compile( bytes ); } )
|
||||
.then( function( mod ) { return WebAssembly.instantiate( mod, instance.imports ) } ) );
|
||||
|
||||
return wasm_instance
|
||||
.then( function( wasm_instance ) {
|
||||
var exports = instance.initialize( wasm_instance );
|
||||
console.log( "Finished loading Rust wasm module 'helloworld'" );
|
||||
return exports;
|
||||
})
|
||||
.catch( function( error ) {
|
||||
console.log( "Error loading Rust wasm module 'helloworld':", error );
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}( function() {
|
||||
var Module = {};
|
||||
|
||||
Module.STDWEB_PRIVATE = {};
|
||||
|
||||
// This is based on code from Emscripten's preamble.js.
|
||||
Module.STDWEB_PRIVATE.to_utf8 = function to_utf8( str, addr ) {
|
||||
var HEAPU8 = Module.HEAPU8;
|
||||
for( var i = 0; i < str.length; ++i ) {
|
||||
// Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8.
|
||||
// See http://unicode.org/faq/utf_bom.html#utf16-3
|
||||
// For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629
|
||||
var u = str.charCodeAt( i ); // possibly a lead surrogate
|
||||
if( u >= 0xD800 && u <= 0xDFFF ) {
|
||||
u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt( ++i ) & 0x3FF);
|
||||
}
|
||||
|
||||
if( u <= 0x7F ) {
|
||||
HEAPU8[ addr++ ] = u;
|
||||
} else if( u <= 0x7FF ) {
|
||||
HEAPU8[ addr++ ] = 0xC0 | (u >> 6);
|
||||
HEAPU8[ addr++ ] = 0x80 | (u & 63);
|
||||
} else if( u <= 0xFFFF ) {
|
||||
HEAPU8[ addr++ ] = 0xE0 | (u >> 12);
|
||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63);
|
||||
HEAPU8[ addr++ ] = 0x80 | (u & 63);
|
||||
} else if( u <= 0x1FFFFF ) {
|
||||
HEAPU8[ addr++ ] = 0xF0 | (u >> 18);
|
||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63);
|
||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63);
|
||||
HEAPU8[ addr++ ] = 0x80 | (u & 63);
|
||||
} else if( u <= 0x3FFFFFF ) {
|
||||
HEAPU8[ addr++ ] = 0xF8 | (u >> 24);
|
||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 18) & 63);
|
||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63);
|
||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63);
|
||||
HEAPU8[ addr++ ] = 0x80 | (u & 63);
|
||||
} else {
|
||||
HEAPU8[ addr++ ] = 0xFC | (u >> 30);
|
||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 24) & 63);
|
||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 18) & 63);
|
||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63);
|
||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63);
|
||||
HEAPU8[ addr++ ] = 0x80 | (u & 63);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.noop = function() {};
|
||||
Module.STDWEB_PRIVATE.to_js = function to_js( address ) {
|
||||
var kind = Module.HEAPU8[ address + 12 ];
|
||||
if( kind === 0 ) {
|
||||
return undefined;
|
||||
} else if( kind === 1 ) {
|
||||
return null;
|
||||
} else if( kind === 2 ) {
|
||||
return Module.HEAP32[ address / 4 ];
|
||||
} else if( kind === 3 ) {
|
||||
return Module.HEAPF64[ address / 8 ];
|
||||
} else if( kind === 4 ) {
|
||||
var pointer = Module.HEAPU32[ address / 4 ];
|
||||
var length = Module.HEAPU32[ (address + 4) / 4 ];
|
||||
return Module.STDWEB_PRIVATE.to_js_string( pointer, length );
|
||||
} else if( kind === 5 ) {
|
||||
return false;
|
||||
} else if( kind === 6 ) {
|
||||
return true;
|
||||
} else if( kind === 7 ) {
|
||||
var pointer = Module.STDWEB_PRIVATE.arena + Module.HEAPU32[ address / 4 ];
|
||||
var length = Module.HEAPU32[ (address + 4) / 4 ];
|
||||
var output = [];
|
||||
for( var i = 0; i < length; ++i ) {
|
||||
output.push( Module.STDWEB_PRIVATE.to_js( pointer + i * 16 ) );
|
||||
}
|
||||
return output;
|
||||
} else if( kind === 8 ) {
|
||||
var arena = Module.STDWEB_PRIVATE.arena;
|
||||
var value_array_pointer = arena + Module.HEAPU32[ address / 4 ];
|
||||
var length = Module.HEAPU32[ (address + 4) / 4 ];
|
||||
var key_array_pointer = arena + Module.HEAPU32[ (address + 8) / 4 ];
|
||||
var output = {};
|
||||
for( var i = 0; i < length; ++i ) {
|
||||
var key_pointer = Module.HEAPU32[ (key_array_pointer + i * 8) / 4 ];
|
||||
var key_length = Module.HEAPU32[ (key_array_pointer + 4 + i * 8) / 4 ];
|
||||
var key = Module.STDWEB_PRIVATE.to_js_string( key_pointer, key_length );
|
||||
var value = Module.STDWEB_PRIVATE.to_js( value_array_pointer + i * 16 );
|
||||
output[ key ] = value;
|
||||
}
|
||||
return output;
|
||||
} else if( kind === 9 ) {
|
||||
return Module.STDWEB_PRIVATE.acquire_js_reference( Module.HEAP32[ address / 4 ] );
|
||||
} else if( kind === 10 || kind === 12 || kind === 13 ) {
|
||||
var adapter_pointer = Module.HEAPU32[ address / 4 ];
|
||||
var pointer = Module.HEAPU32[ (address + 4) / 4 ];
|
||||
var deallocator_pointer = Module.HEAPU32[ (address + 8) / 4 ];
|
||||
var num_ongoing_calls = 0;
|
||||
var drop_queued = false;
|
||||
var output = function() {
|
||||
if( pointer === 0 || drop_queued === true ) {
|
||||
if (kind === 10) {
|
||||
throw new ReferenceError( "Already dropped Rust function called!" );
|
||||
} else if (kind === 12) {
|
||||
throw new ReferenceError( "Already dropped FnMut function called!" );
|
||||
} else {
|
||||
throw new ReferenceError( "Already called or dropped FnOnce function called!" );
|
||||
}
|
||||
}
|
||||
|
||||
var function_pointer = pointer;
|
||||
if (kind === 13) {
|
||||
output.drop = Module.STDWEB_PRIVATE.noop;
|
||||
pointer = 0;
|
||||
}
|
||||
|
||||
if (num_ongoing_calls !== 0) {
|
||||
if (kind === 12 || kind === 13) {
|
||||
throw new ReferenceError( "FnMut function called multiple times concurrently!" );
|
||||
}
|
||||
}
|
||||
|
||||
var args = Module.STDWEB_PRIVATE.alloc( 16 );
|
||||
Module.STDWEB_PRIVATE.serialize_array( args, arguments );
|
||||
|
||||
try {
|
||||
num_ongoing_calls += 1;
|
||||
Module.STDWEB_PRIVATE.dyncall( "vii", adapter_pointer, [function_pointer, args] );
|
||||
var result = Module.STDWEB_PRIVATE.tmp;
|
||||
Module.STDWEB_PRIVATE.tmp = null;
|
||||
} finally {
|
||||
num_ongoing_calls -= 1;
|
||||
}
|
||||
|
||||
if( drop_queued === true && num_ongoing_calls === 0 ) {
|
||||
output.drop();
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
output.drop = function() {
|
||||
if (num_ongoing_calls !== 0) {
|
||||
drop_queued = true;
|
||||
return;
|
||||
}
|
||||
|
||||
output.drop = Module.STDWEB_PRIVATE.noop;
|
||||
var function_pointer = pointer;
|
||||
pointer = 0;
|
||||
|
||||
if (function_pointer != 0) {
|
||||
Module.STDWEB_PRIVATE.dyncall( "vi", deallocator_pointer, [function_pointer] );
|
||||
}
|
||||
};
|
||||
|
||||
return output;
|
||||
} else if( kind === 14 ) {
|
||||
var pointer = Module.HEAPU32[ address / 4 ];
|
||||
var length = Module.HEAPU32[ (address + 4) / 4 ];
|
||||
var array_kind = Module.HEAPU32[ (address + 8) / 4 ];
|
||||
var pointer_end = pointer + length;
|
||||
|
||||
switch( array_kind ) {
|
||||
case 0:
|
||||
return Module.HEAPU8.subarray( pointer, pointer_end );
|
||||
case 1:
|
||||
return Module.HEAP8.subarray( pointer, pointer_end );
|
||||
case 2:
|
||||
return Module.HEAPU16.subarray( pointer, pointer_end );
|
||||
case 3:
|
||||
return Module.HEAP16.subarray( pointer, pointer_end );
|
||||
case 4:
|
||||
return Module.HEAPU32.subarray( pointer, pointer_end );
|
||||
case 5:
|
||||
return Module.HEAP32.subarray( pointer, pointer_end );
|
||||
case 6:
|
||||
return Module.HEAPF32.subarray( pointer, pointer_end );
|
||||
case 7:
|
||||
return Module.HEAPF64.subarray( pointer, pointer_end );
|
||||
}
|
||||
} else if( kind === 15 ) {
|
||||
return Module.STDWEB_PRIVATE.get_raw_value( Module.HEAPU32[ address / 4 ] );
|
||||
}
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.serialize_object = function serialize_object( address, value ) {
|
||||
var keys = Object.keys( value );
|
||||
var length = keys.length;
|
||||
var key_array_pointer = Module.STDWEB_PRIVATE.alloc( length * 8 );
|
||||
var value_array_pointer = Module.STDWEB_PRIVATE.alloc( length * 16 );
|
||||
Module.HEAPU8[ address + 12 ] = 8;
|
||||
Module.HEAPU32[ address / 4 ] = value_array_pointer;
|
||||
Module.HEAPU32[ (address + 4) / 4 ] = length;
|
||||
Module.HEAPU32[ (address + 8) / 4 ] = key_array_pointer;
|
||||
for( var i = 0; i < length; ++i ) {
|
||||
var key = keys[ i ];
|
||||
var key_address = key_array_pointer + i * 8;
|
||||
Module.STDWEB_PRIVATE.to_utf8_string( key_address, key );
|
||||
|
||||
Module.STDWEB_PRIVATE.from_js( value_array_pointer + i * 16, value[ key ] );
|
||||
}
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.serialize_array = function serialize_array( address, value ) {
|
||||
var length = value.length;
|
||||
var pointer = Module.STDWEB_PRIVATE.alloc( length * 16 );
|
||||
Module.HEAPU8[ address + 12 ] = 7;
|
||||
Module.HEAPU32[ address / 4 ] = pointer;
|
||||
Module.HEAPU32[ (address + 4) / 4 ] = length;
|
||||
for( var i = 0; i < length; ++i ) {
|
||||
Module.STDWEB_PRIVATE.from_js( pointer + i * 16, value[ i ] );
|
||||
}
|
||||
};
|
||||
|
||||
// New browsers and recent Node
|
||||
var cachedEncoder = ( typeof TextEncoder === "function"
|
||||
? new TextEncoder( "utf-8" )
|
||||
// Old Node (before v11)
|
||||
: ( typeof util === "object" && util && typeof util.TextEncoder === "function"
|
||||
? new util.TextEncoder( "utf-8" )
|
||||
// Old browsers
|
||||
: null ) );
|
||||
|
||||
if ( cachedEncoder != null ) {
|
||||
Module.STDWEB_PRIVATE.to_utf8_string = function to_utf8_string( address, value ) {
|
||||
var buffer = cachedEncoder.encode( value );
|
||||
var length = buffer.length;
|
||||
var pointer = 0;
|
||||
|
||||
if ( length > 0 ) {
|
||||
pointer = Module.STDWEB_PRIVATE.alloc( length );
|
||||
Module.HEAPU8.set( buffer, pointer );
|
||||
}
|
||||
|
||||
Module.HEAPU32[ address / 4 ] = pointer;
|
||||
Module.HEAPU32[ (address + 4) / 4 ] = length;
|
||||
};
|
||||
|
||||
} else {
|
||||
Module.STDWEB_PRIVATE.to_utf8_string = function to_utf8_string( address, value ) {
|
||||
var length = Module.STDWEB_PRIVATE.utf8_len( value );
|
||||
var pointer = 0;
|
||||
|
||||
if ( length > 0 ) {
|
||||
pointer = Module.STDWEB_PRIVATE.alloc( length );
|
||||
Module.STDWEB_PRIVATE.to_utf8( value, pointer );
|
||||
}
|
||||
|
||||
Module.HEAPU32[ address / 4 ] = pointer;
|
||||
Module.HEAPU32[ (address + 4) / 4 ] = length;
|
||||
};
|
||||
}
|
||||
|
||||
Module.STDWEB_PRIVATE.from_js = function from_js( address, value ) {
|
||||
var kind = Object.prototype.toString.call( value );
|
||||
if( kind === "[object String]" ) {
|
||||
Module.HEAPU8[ address + 12 ] = 4;
|
||||
Module.STDWEB_PRIVATE.to_utf8_string( address, value );
|
||||
} else if( kind === "[object Number]" ) {
|
||||
if( value === (value|0) ) {
|
||||
Module.HEAPU8[ address + 12 ] = 2;
|
||||
Module.HEAP32[ address / 4 ] = value;
|
||||
} else {
|
||||
Module.HEAPU8[ address + 12 ] = 3;
|
||||
Module.HEAPF64[ address / 8 ] = value;
|
||||
}
|
||||
} else if( value === null ) {
|
||||
Module.HEAPU8[ address + 12 ] = 1;
|
||||
} else if( value === undefined ) {
|
||||
Module.HEAPU8[ address + 12 ] = 0;
|
||||
} else if( value === false ) {
|
||||
Module.HEAPU8[ address + 12 ] = 5;
|
||||
} else if( value === true ) {
|
||||
Module.HEAPU8[ address + 12 ] = 6;
|
||||
} else if( kind === "[object Symbol]" ) {
|
||||
var id = Module.STDWEB_PRIVATE.register_raw_value( value );
|
||||
Module.HEAPU8[ address + 12 ] = 15;
|
||||
Module.HEAP32[ address / 4 ] = id;
|
||||
} else {
|
||||
var refid = Module.STDWEB_PRIVATE.acquire_rust_reference( value );
|
||||
Module.HEAPU8[ address + 12 ] = 9;
|
||||
Module.HEAP32[ address / 4 ] = refid;
|
||||
}
|
||||
};
|
||||
|
||||
// New browsers and recent Node
|
||||
var cachedDecoder = ( typeof TextDecoder === "function"
|
||||
? new TextDecoder( "utf-8" )
|
||||
// Old Node (before v11)
|
||||
: ( typeof util === "object" && util && typeof util.TextDecoder === "function"
|
||||
? new util.TextDecoder( "utf-8" )
|
||||
// Old browsers
|
||||
: null ) );
|
||||
|
||||
if ( cachedDecoder != null ) {
|
||||
Module.STDWEB_PRIVATE.to_js_string = function to_js_string( index, length ) {
|
||||
return cachedDecoder.decode( Module.HEAPU8.subarray( index, index + length ) );
|
||||
};
|
||||
|
||||
} else {
|
||||
// This is ported from Rust's stdlib; it's faster than
|
||||
// the string conversion from Emscripten.
|
||||
Module.STDWEB_PRIVATE.to_js_string = function to_js_string( index, length ) {
|
||||
var HEAPU8 = Module.HEAPU8;
|
||||
index = index|0;
|
||||
length = length|0;
|
||||
var end = (index|0) + (length|0);
|
||||
var output = "";
|
||||
while( index < end ) {
|
||||
var x = HEAPU8[ index++ ];
|
||||
if( x < 128 ) {
|
||||
output += String.fromCharCode( x );
|
||||
continue;
|
||||
}
|
||||
var init = (x & (0x7F >> 2));
|
||||
var y = 0;
|
||||
if( index < end ) {
|
||||
y = HEAPU8[ index++ ];
|
||||
}
|
||||
var ch = (init << 6) | (y & 63);
|
||||
if( x >= 0xE0 ) {
|
||||
var z = 0;
|
||||
if( index < end ) {
|
||||
z = HEAPU8[ index++ ];
|
||||
}
|
||||
var y_z = ((y & 63) << 6) | (z & 63);
|
||||
ch = init << 12 | y_z;
|
||||
if( x >= 0xF0 ) {
|
||||
var w = 0;
|
||||
if( index < end ) {
|
||||
w = HEAPU8[ index++ ];
|
||||
}
|
||||
ch = (init & 7) << 18 | ((y_z << 6) | (w & 63));
|
||||
|
||||
output += String.fromCharCode( 0xD7C0 + (ch >> 10) );
|
||||
ch = 0xDC00 + (ch & 0x3FF);
|
||||
}
|
||||
}
|
||||
output += String.fromCharCode( ch );
|
||||
continue;
|
||||
}
|
||||
return output;
|
||||
};
|
||||
}
|
||||
|
||||
Module.STDWEB_PRIVATE.id_to_ref_map = {};
|
||||
Module.STDWEB_PRIVATE.id_to_refcount_map = {};
|
||||
Module.STDWEB_PRIVATE.ref_to_id_map = new WeakMap();
|
||||
// Not all types can be stored in a WeakMap
|
||||
Module.STDWEB_PRIVATE.ref_to_id_map_fallback = new Map();
|
||||
Module.STDWEB_PRIVATE.last_refid = 1;
|
||||
|
||||
Module.STDWEB_PRIVATE.id_to_raw_value_map = {};
|
||||
Module.STDWEB_PRIVATE.last_raw_value_id = 1;
|
||||
|
||||
Module.STDWEB_PRIVATE.acquire_rust_reference = function( reference ) {
|
||||
if( reference === undefined || reference === null ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var id_to_refcount_map = Module.STDWEB_PRIVATE.id_to_refcount_map;
|
||||
var id_to_ref_map = Module.STDWEB_PRIVATE.id_to_ref_map;
|
||||
var ref_to_id_map = Module.STDWEB_PRIVATE.ref_to_id_map;
|
||||
var ref_to_id_map_fallback = Module.STDWEB_PRIVATE.ref_to_id_map_fallback;
|
||||
|
||||
var refid = ref_to_id_map.get( reference );
|
||||
if( refid === undefined ) {
|
||||
refid = ref_to_id_map_fallback.get( reference );
|
||||
}
|
||||
if( refid === undefined ) {
|
||||
refid = Module.STDWEB_PRIVATE.last_refid++;
|
||||
try {
|
||||
ref_to_id_map.set( reference, refid );
|
||||
} catch (e) {
|
||||
ref_to_id_map_fallback.set( reference, refid );
|
||||
}
|
||||
}
|
||||
|
||||
if( refid in id_to_ref_map ) {
|
||||
id_to_refcount_map[ refid ]++;
|
||||
} else {
|
||||
id_to_ref_map[ refid ] = reference;
|
||||
id_to_refcount_map[ refid ] = 1;
|
||||
}
|
||||
|
||||
return refid;
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.acquire_js_reference = function( refid ) {
|
||||
return Module.STDWEB_PRIVATE.id_to_ref_map[ refid ];
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.increment_refcount = function( refid ) {
|
||||
Module.STDWEB_PRIVATE.id_to_refcount_map[ refid ]++;
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.decrement_refcount = function( refid ) {
|
||||
var id_to_refcount_map = Module.STDWEB_PRIVATE.id_to_refcount_map;
|
||||
if( 0 == --id_to_refcount_map[ refid ] ) {
|
||||
var id_to_ref_map = Module.STDWEB_PRIVATE.id_to_ref_map;
|
||||
var ref_to_id_map_fallback = Module.STDWEB_PRIVATE.ref_to_id_map_fallback;
|
||||
var reference = id_to_ref_map[ refid ];
|
||||
delete id_to_ref_map[ refid ];
|
||||
delete id_to_refcount_map[ refid ];
|
||||
ref_to_id_map_fallback.delete(reference);
|
||||
}
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.register_raw_value = function( value ) {
|
||||
var id = Module.STDWEB_PRIVATE.last_raw_value_id++;
|
||||
Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ] = value;
|
||||
return id;
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.unregister_raw_value = function( id ) {
|
||||
delete Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ];
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.get_raw_value = function( id ) {
|
||||
return Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ];
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.alloc = function alloc( size ) {
|
||||
return Module.web_malloc( size );
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.dyncall = function( signature, ptr, args ) {
|
||||
return Module.web_table.get( ptr ).apply( null, args );
|
||||
};
|
||||
|
||||
// This is based on code from Emscripten's preamble.js.
|
||||
Module.STDWEB_PRIVATE.utf8_len = function utf8_len( str ) {
|
||||
var len = 0;
|
||||
for( var i = 0; i < str.length; ++i ) {
|
||||
// Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8.
|
||||
// See http://unicode.org/faq/utf_bom.html#utf16-3
|
||||
var u = str.charCodeAt( i ); // possibly a lead surrogate
|
||||
if( u >= 0xD800 && u <= 0xDFFF ) {
|
||||
u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt( ++i ) & 0x3FF);
|
||||
}
|
||||
|
||||
if( u <= 0x7F ) {
|
||||
++len;
|
||||
} else if( u <= 0x7FF ) {
|
||||
len += 2;
|
||||
} else if( u <= 0xFFFF ) {
|
||||
len += 3;
|
||||
} else if( u <= 0x1FFFFF ) {
|
||||
len += 4;
|
||||
} else if( u <= 0x3FFFFFF ) {
|
||||
len += 5;
|
||||
} else {
|
||||
len += 6;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.prepare_any_arg = function( value ) {
|
||||
var arg = Module.STDWEB_PRIVATE.alloc( 16 );
|
||||
Module.STDWEB_PRIVATE.from_js( arg, value );
|
||||
return arg;
|
||||
};
|
||||
|
||||
Module.STDWEB_PRIVATE.acquire_tmp = function( dummy ) {
|
||||
var value = Module.STDWEB_PRIVATE.tmp;
|
||||
Module.STDWEB_PRIVATE.tmp = null;
|
||||
return value;
|
||||
};
|
||||
|
||||
|
||||
|
||||
var HEAP8 = null;
|
||||
var HEAP16 = null;
|
||||
var HEAP32 = null;
|
||||
var HEAPU8 = null;
|
||||
var HEAPU16 = null;
|
||||
var HEAPU32 = null;
|
||||
var HEAPF32 = null;
|
||||
var HEAPF64 = null;
|
||||
|
||||
Object.defineProperty( Module, 'exports', { value: {} } );
|
||||
|
||||
function __web_on_grow() {
|
||||
var buffer = Module.instance.exports.memory.buffer;
|
||||
HEAP8 = new Int8Array( buffer );
|
||||
HEAP16 = new Int16Array( buffer );
|
||||
HEAP32 = new Int32Array( buffer );
|
||||
HEAPU8 = new Uint8Array( buffer );
|
||||
HEAPU16 = new Uint16Array( buffer );
|
||||
HEAPU32 = new Uint32Array( buffer );
|
||||
HEAPF32 = new Float32Array( buffer );
|
||||
HEAPF64 = new Float64Array( buffer );
|
||||
Module.HEAP8 = HEAP8;
|
||||
Module.HEAP16 = HEAP16;
|
||||
Module.HEAP32 = HEAP32;
|
||||
Module.HEAPU8 = HEAPU8;
|
||||
Module.HEAPU16 = HEAPU16;
|
||||
Module.HEAPU32 = HEAPU32;
|
||||
Module.HEAPF32 = HEAPF32;
|
||||
Module.HEAPF64 = HEAPF64;
|
||||
}
|
||||
|
||||
return {
|
||||
imports: {
|
||||
env: {
|
||||
"__cargo_web_snippet_09675c7ed2827e045dc760aeac3d286437cfbe5e": function($0, $1, $2, $3) {
|
||||
$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);$3 = Module.STDWEB_PRIVATE.to_js($3);Module.STDWEB_PRIVATE.from_js($0, (function(){try{return{value:function(){return($1).setAttribute(($2),($3));}(),success:true};}catch(error){return{error:error,success:false};}})());
|
||||
},
|
||||
"__cargo_web_snippet_0e54fd9c163fcf648ce0a395fde4500fd167a40b": function($0) {
|
||||
var r = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (r instanceof DOMException) && (r.name === "InvalidCharacterError");
|
||||
},
|
||||
"__cargo_web_snippet_1edaec034bdcb0a749c6d5de76c29f6371afb5a0": function($0) {
|
||||
var o = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (o instanceof Event && o.type === "input");
|
||||
},
|
||||
"__cargo_web_snippet_2908dbb08792df5e699e324eec3e29fd6a57c2c9": function($0) {
|
||||
var o = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (o instanceof HTMLInputElement);
|
||||
},
|
||||
"__cargo_web_snippet_3c5e83d16a83fc7147ec91e2506438012952f55a": function($0) {
|
||||
var o = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (o instanceof Element);
|
||||
},
|
||||
"__cargo_web_snippet_614a3dd2adb7e9eac4a0ec6e59d37f87e0521c3b": function($0, $1) {
|
||||
$1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).error;})());
|
||||
},
|
||||
"__cargo_web_snippet_6fcce0aae651e2d748e085ff1f800f87625ff8c8": function($0) {
|
||||
Module.STDWEB_PRIVATE.from_js($0, (function(){return document;})());
|
||||
},
|
||||
"__cargo_web_snippet_72fc447820458c720c68d0d8e078ede631edd723": function($0, $1, $2) {
|
||||
console.error( 'Panic location:', Module.STDWEB_PRIVATE.to_js_string( $0, $1 ) + ':' + $2 );
|
||||
},
|
||||
"__cargo_web_snippet_80d6d56760c65e49b7be8b6b01c1ea861b046bf0": function($0) {
|
||||
Module.STDWEB_PRIVATE.decrement_refcount( $0 );
|
||||
},
|
||||
"__cargo_web_snippet_91749aeb589cd0f9b17cbc01b2872ba709817982": function($0, $1, $2) {
|
||||
$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){try{return{value:function(){return($1).createElement(($2));}(),success:true};}catch(error){return{error:error,success:false};}})());
|
||||
},
|
||||
"__cargo_web_snippet_97495987af1720d8a9a923fa4683a7b683e3acd6": function($0, $1) {
|
||||
console.error( 'Panic error message:', Module.STDWEB_PRIVATE.to_js_string( $0, $1 ) );
|
||||
},
|
||||
"__cargo_web_snippet_99c4eefdc8d4cc724135163b8c8665a1f3de99e4": function($0, $1, $2, $3) {
|
||||
$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);$3 = Module.STDWEB_PRIVATE.to_js($3);Module.STDWEB_PRIVATE.from_js($0, (function(){var listener=($1);($2).addEventListener(($3),listener);return listener;})());
|
||||
},
|
||||
"__cargo_web_snippet_9f22d4ca7bc938409787341b7db181f8dd41e6df": function($0) {
|
||||
Module.STDWEB_PRIVATE.increment_refcount( $0 );
|
||||
},
|
||||
"__cargo_web_snippet_ab05f53189dacccf2d365ad26daa407d4f7abea9": function($0, $1) {
|
||||
$1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).value;})());
|
||||
},
|
||||
"__cargo_web_snippet_afafe9a462a05084fec65cacc7d6598e145ff3e3": function($0, $1, $2) {
|
||||
$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).createTextNode(($2));})());
|
||||
},
|
||||
"__cargo_web_snippet_b06dde4acf09433b5190a4b001259fe5d4abcbc2": function($0, $1) {
|
||||
$1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).success;})());
|
||||
},
|
||||
"__cargo_web_snippet_d5e30f74cb752784e06bd97a37b1f89b6c3433a7": function($0, $1, $2) {
|
||||
$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).getElementById(($2));})());
|
||||
},
|
||||
"__cargo_web_snippet_dc2fd915bd92f9e9c6a3bd15174f1414eee3dbaf": function() {
|
||||
console.error( 'Encountered a panic!' );
|
||||
},
|
||||
"__cargo_web_snippet_e741b9d9071097746386b2c2ec044a2bc73e688c": function($0, $1) {
|
||||
$0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0).appendChild(($1));
|
||||
},
|
||||
"__cargo_web_snippet_e9638d6405ab65f78daf4a5af9c9de14ecf1e2ec": function($0) {
|
||||
$0 = Module.STDWEB_PRIVATE.to_js($0);Module.STDWEB_PRIVATE.unregister_raw_value(($0));
|
||||
},
|
||||
"__cargo_web_snippet_f765b15a1a1b5cd266e922e6fca98dd570f17edc": function($0, $1) {
|
||||
$0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0).textContent=($1);
|
||||
},
|
||||
"__cargo_web_snippet_ff5103e6cc179d13b4c7a785bdce2708fd559fc0": function($0) {
|
||||
Module.STDWEB_PRIVATE.tmp = Module.STDWEB_PRIVATE.to_js( $0 );
|
||||
},
|
||||
"__web_on_grow": __web_on_grow
|
||||
}
|
||||
},
|
||||
initialize: function( instance ) {
|
||||
Object.defineProperty( Module, 'instance', { value: instance } );
|
||||
Object.defineProperty( Module, 'web_malloc', { value: Module.instance.exports.__web_malloc } );
|
||||
Object.defineProperty( Module, 'web_free', { value: Module.instance.exports.__web_free } );
|
||||
Object.defineProperty( Module, 'web_table', { value: Module.instance.exports.__indirect_function_table } );
|
||||
|
||||
|
||||
__web_on_grow();
|
||||
Module.instance.exports.main();
|
||||
|
||||
return Module.exports;
|
||||
}
|
||||
};
|
||||
}
|
||||
));
|
||||
}));
|
BIN
content/enterprise/prototype/helloworld.wasm
Normal file
BIN
content/enterprise/prototype/helloworld.wasm
Normal file
Binary file not shown.
52
content/enterprise/prototype/index.md
Normal file
52
content/enterprise/prototype/index.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
+++
|
||||
title = "enterprise: a new ui framework"
|
||||
date = 2020-02-11
|
||||
draft = true
|
||||
template = "post.html"
|
||||
|
||||
[taxonomies]
|
||||
tags = []
|
||||
|
||||
[extra]
|
||||
toc = true
|
||||
+++
|
||||
|
||||
This past weekend, while on my trip to Minneapolis, I completed a very early prototype of "enterprise", a new UI framework I've been kind of envisioning over the past couple of weeks. While the UI framework is mainly targeted at web apps, the hope is that with a bit more effort, native UIs can be produced with almost no changes to existing applications. Before I begin to describe how it works, I'd like to acknowledge [Nathan Ringo][1] for his massively helpful feedback in both the ideation and the implementation process.
|
||||
|
||||
## Goals of the project
|
||||
|
||||
* **Complete separation of business logic from UI.** Theoretically, one could completely retarget the application to a completely different platform (mobile, web, native, something new that will pop up in 5 years), without changing any of the core logic. As enterprise grows to include a backend, this should be true of the backend as well. It does this by introducing [DSL][2]s that are completely architecture-independent.
|
||||
* **Frontend relationships should be as static as possible.**
|
||||
|
||||
## Prototype
|
||||
|
||||
The prototype for experimenting is a simple "Hello, world" application. If you've looked at any web framework before, this is probably one of the simplest examples of bindings: type something into a box and watch as the text magically populates with whatever you wrote in the box. If you're using a WASM-compatible browser with JavaScript enabled, you should be able to try out the demo in real-time:
|
||||
|
||||
<div id="app"></div>
|
||||
<script type="text/javascript" src="helloworld.js"></script>
|
||||
|
||||
OK, you say, but I could implement this in 3 lines of JavaScript.
|
||||
|
||||
```js
|
||||
inputEl.addEventListener("change", () => {
|
||||
spanEl.innerText = inputEl.value;
|
||||
});
|
||||
```
|
||||
|
||||
Surely, this works, but it doesn't scale. If you try to write a page full of these kind of bindings directly using JavaScript, you're either going to start running into bugs or building up a pile of unmaintainable spaghetti code.
|
||||
|
||||
So how does enterprise represent this? Well, the enterprise DSL has no concrete syntax yet, but if it did, it would look something like this:
|
||||
|
||||
```
|
||||
model {
|
||||
name: String,
|
||||
}
|
||||
|
||||
view {
|
||||
<TextBox bind:value="name" />
|
||||
Hello, {name}!
|
||||
}
|
||||
```
|
||||
|
||||
[1]: https://remexre.xyz
|
||||
[2]: https://en.wikipedia.org/wiki/Domain-specific_language
|
|
@ -15,6 +15,7 @@
|
|||
Posted
|
||||
{% if page.extra.author %}by {{ page.extra.author }}{% endif %}
|
||||
on {{ page.date }}
|
||||
- {{ page.reading_time }} min read
|
||||
</small>
|
||||
{% endif %}
|
||||
|
||||
|
|
Loading…
Reference in a new issue