lecodes-viewer 0.19.0

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.
Files changed (103) hide show
  1. package/README.md +5 -0
  2. package/dist-embed/VERSION.json +5 -0
  3. package/dist-embed/assets/Roboto-Variable-DHm9jpd-.woff2 +0 -0
  4. package/dist-embed/assets/__vite-browser-external-BIHI7g3E.js +1 -0
  5. package/dist-embed/assets/basis-C64VHDVD.js +1 -0
  6. package/dist-embed/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
  7. package/dist-embed/assets/createViewer2d-D12Wy3VF.js +1 -0
  8. package/dist-embed/assets/createViewerLite-CP-WHGLg.js +707 -0
  9. package/dist-embed/assets/creator-gl-BF2CQzeI.wasm +0 -0
  10. package/dist-embed/assets/creator-gl.js-B_0GrQRe.symbols +5379 -0
  11. package/dist-embed/assets/creator-ui-BX5Fvnbt.js +1 -0
  12. package/dist-embed/assets/creator-ui-CzKHLH2Q.wasm +0 -0
  13. package/dist-embed/assets/creator2d-BtQ-1_B0.wasm +0 -0
  14. package/dist-embed/assets/draco-BiISTFcR.js +118 -0
  15. package/dist-embed/assets/draco_decoder-DsQ12WqX.wasm +0 -0
  16. package/dist-embed/assets/host-CWKB1WDd.js +162 -0
  17. package/dist-embed/assets/index-CIK8PnUA.js +2 -0
  18. package/dist-embed/assets/lit-DUE9rhY9.filamat +0 -0
  19. package/dist-embed/assets/loadCreatorUiNode-UAdhjRTd.js +1 -0
  20. package/dist-embed/assets/neutral_ibl128-D-lzdkLN.ktx +0 -0
  21. package/dist-embed/assets/particles-DxYDmI1O.filamat +0 -0
  22. package/dist-embed/assets/particles-quad-DwYo9yCK.filamat +0 -0
  23. package/dist-embed/assets/shadow-C9xJpqlY.filamat +0 -0
  24. package/dist-embed/assets/uberarchive-uXjRYa9p.bin +0 -0
  25. package/dist-embed/assets/unlit-pe4tFY5b.filamat +0 -0
  26. package/dist-embed/assets/unlit-transparent-D9OyaOBR.filamat +0 -0
  27. package/dist-embed/assets/video-eedvhMvS.filamat +0 -0
  28. package/dist-embed/assets/worker-Caf-yYEI.js +2 -0
  29. package/dist-embed/embed.html +48 -0
  30. package/dist-embed/embed.js +187 -0
  31. package/embed.html +48 -0
  32. package/package.json +35 -0
  33. package/src/assets/banner.png +0 -0
  34. package/src/assets/image2.png +0 -0
  35. package/src/assets/landing.mp4 +0 -0
  36. package/src/assets/neutral_ibl128.ktx +0 -0
  37. package/src/assets/neutral_ibl256.ktx +0 -0
  38. package/src/assets/output.mp4 +0 -0
  39. package/src/assets/output2.mp4 +0 -0
  40. package/src/assets/texture.png +0 -0
  41. package/src/assets/vue.svg +1 -0
  42. package/src/canvas/createCanvasHost.ts +2 -0
  43. package/src/createViewer.ts +103 -0
  44. package/src/embed.ts +515 -0
  45. package/src/host.ts +428 -0
  46. package/src/lite-test.ts +160 -0
  47. package/src/main.ts +77 -0
  48. package/src/sourcemapError.ts +2 -0
  49. package/src/ui/CuiProps.gen.ts +103 -0
  50. package/src/ui/UIScreen.ts +486 -0
  51. package/src/ui/createBareUI.ts +32 -0
  52. package/src/ui/createMeasureSystem.ts +220 -0
  53. package/src/ui/createNodeElement.ts +77 -0
  54. package/src/ui/createPagerSystem.ts +345 -0
  55. package/src/ui/createTouchSystem.ts +2 -0
  56. package/src/ui/createUI.ts +806 -0
  57. package/src/ui/creator-ui.js +14 -0
  58. package/src/ui/creator-ui.wasm +0 -0
  59. package/src/ui/defaultStyle.css +176 -0
  60. package/src/ui/layers.ts +9 -0
  61. package/src/ui/mountCanvasUI.ts +213 -0
  62. package/src/ui/nativeViewSystem.ts +91 -0
  63. package/src/ui/presentPlayer.ts +156 -0
  64. package/src/ui/props-apply.ts +403 -0
  65. package/src/ui/router.ts +2 -0
  66. package/src/ui/safeAreaSpec.ts +3 -0
  67. package/src/ui/shells.ts +37 -0
  68. package/src/ui/toasts.ts +2 -0
  69. package/src/ui/transitions.ts +250 -0
  70. package/src/ui/ui.d.ts +78 -0
  71. package/src/ui/vListSystem.ts +212 -0
  72. package/src/ui/viewScale.ts +2 -0
  73. package/src/utils/animate.ts +2 -0
  74. package/src/utils/createUtils.ts +2 -0
  75. package/src/utils/fetch.ts +3 -0
  76. package/src/utils/fonts.ts +2 -0
  77. package/src/utils/globals.ts +2 -0
  78. package/src/utils/imageNode.ts +260 -0
  79. package/src/utils/openFilePicker.ts +2 -0
  80. package/src/utils/svg.ts +2 -0
  81. package/src/utils/videoNode.ts +2 -0
  82. package/src/utils/ws.ts +2 -0
  83. package/src/viewer/createViewer.ts +993 -0
  84. package/src/viewer/creator-gl.js +14 -0
  85. package/src/viewer/creator-gl.js.symbols +5379 -0
  86. package/src/viewer/creator-gl.wasm +0 -0
  87. package/src/viewer/lit.filamat +0 -0
  88. package/src/viewer/mediaplayer.ts +3 -0
  89. package/src/viewer/particles-quad.filamat +0 -0
  90. package/src/viewer/particles.filamat +0 -0
  91. package/src/viewer/physics.ts +140 -0
  92. package/src/viewer/shadow.filamat +0 -0
  93. package/src/viewer/uberarchive.bin +0 -0
  94. package/src/viewer/unlit-transparent.filamat +0 -0
  95. package/src/viewer/unlit.filamat +0 -0
  96. package/src/viewer/validateGlb.ts +262 -0
  97. package/src/viewer/video.filamat +0 -0
  98. package/src/viewer/wasmCrashSymbolizer.ts +133 -0
  99. package/src/viewer2d/createViewer2d.ts +438 -0
  100. package/src/viewer2d/creator2d.js +14 -0
  101. package/src/viewer2d/creator2d.wasm +0 -0
  102. package/src/viewer2d/sprite2d.ts +80 -0
  103. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,14 @@
1
+ var Module = (() => {
2
+
3
+ return (
4
+ async function(moduleArg = {}) {
5
+ var moduleRtn;
6
+
7
+ var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var arguments_=[];var thisProgram="./this.program";var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var wasmMemory;var ABORT=false;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAP64,HEAPU64,HEAPF64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["R"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}var runDependencies=0;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("creator-ui.wasm")}return new URL("creator-ui.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){return{a:wasmImports}}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["Q"];updateMemoryViews();wasmTable=wasmExports["S"];removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(mod,inst)=>{resolve(receiveInstance(mod,inst))})})}wasmBinaryFile??=findWasmBinary();try{var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}catch(e){readyPromiseReject(e);return Promise.reject(e)}}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var exceptionCaught=[];var uncaughtExceptionCount=0;var ___cxa_begin_catch=ptr=>{var info=new ExceptionInfo(ptr);if(!info.get_caught()){info.set_caught(true);uncaughtExceptionCount--}info.set_rethrown(false);exceptionCaught.push(info);___cxa_increment_exception_refcount(ptr);return ___cxa_get_exception_ptr(ptr)};var exceptionLast=0;var ___cxa_end_catch=()=>{_setThrew(0,0);var info=exceptionCaught.pop();___cxa_decrement_exception_refcount(info.excPtr);exceptionLast=0};class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var setTempRet0=val=>__emscripten_tempret_set(val);var findMatchingCatch=args=>{var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0}var info=new ExceptionInfo(thrown);info.set_adjusted_ptr(thrown);var thrownType=info.get_type();if(!thrownType){setTempRet0(0);return thrown}for(var caughtType of args){if(caughtType===0||caughtType===thrownType){break}var adjusted_ptr_addr=info.ptr+16;if(___cxa_can_catch(caughtType,thrownType,adjusted_ptr_addr)){setTempRet0(caughtType);return thrown}}setTempRet0(thrownType);return thrown};var ___cxa_find_matching_catch_2=()=>findMatchingCatch([]);var ___cxa_find_matching_catch_3=arg0=>findMatchingCatch([arg0]);var ___cxa_rethrow=()=>{var info=exceptionCaught.pop();if(!info){abort("no exception to throw")}var ptr=info.excPtr;if(!info.get_rethrown()){exceptionCaught.push(info);info.set_rethrown(true);info.set_caught(false);uncaughtExceptionCount++}exceptionLast=ptr;throw exceptionLast};var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var ___resumeException=ptr=>{if(!exceptionLast){exceptionLast=ptr}throw exceptionLast};var __abort_js=()=>abort("");var abortOnCannotGrowMemory=requestedSize=>{abort("OOM")};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;abortOnCannotGrowMemory(requestedSize)};var _fd_close=fd=>52;var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>num<INT53_MIN||num>INT53_MAX?NaN:Number(num);function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);return 70}var printCharBuffers=[null,[],[]];var UTF8Decoder=new TextDecoder;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;return UTF8Decoder.decode(heapOrArray.buffer?heapOrArray.subarray(idx,endPtr):new Uint8Array(heapOrArray.slice(idx,endPtr)))};var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var UTF8ToString=(ptr,maxBytesToRead)=>{if(!ptr)return"";var maxPtr=ptr+maxBytesToRead;for(var end=ptr;!(end>=maxPtr)&&HEAPU8[end];)++end;return UTF8Decoder.decode(HEAPU8.subarray(ptr,end))};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};var wasmTable;var getWasmTableEntry=funcPtr=>wasmTable.get(funcPtr);var uleb128Encode=(n,target)=>{if(n<128){target.push(n)}else{target.push(n%128|128,n>>7)}};var sigToWasmTypes=sig=>{var typeNames={i:"i32",j:"i64",f:"f32",d:"f64",e:"externref",p:"i32"};var type={parameters:[],results:sig[0]=="v"?[]:[typeNames[sig[0]]]};for(var i=1;i<sig.length;++i){type.parameters.push(typeNames[sig[i]])}return type};var generateFuncType=(sig,target)=>{var sigRet=sig.slice(0,1);var sigParam=sig.slice(1);var typeCodes={i:127,p:127,j:126,f:125,d:124,e:111};target.push(96);uleb128Encode(sigParam.length,target);for(var paramType of sigParam){target.push(typeCodes[paramType])}if(sigRet=="v"){target.push(0)}else{target.push(1,typeCodes[sigRet])}};var convertJsFunctionToWasm=(func,sig)=>{if(typeof WebAssembly.Function=="function"){return new WebAssembly.Function(sigToWasmTypes(sig),func)}var typeSectionBody=[1];generateFuncType(sig,typeSectionBody);var bytes=[0,97,115,109,1,0,0,0,1];uleb128Encode(typeSectionBody.length,bytes);bytes.push(...typeSectionBody);bytes.push(2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);var module=new WebAssembly.Module(new Uint8Array(bytes));var instance=new WebAssembly.Instance(module,{e:{f:func}});var wrappedFunc=instance.exports["f"];return wrappedFunc};var updateTableMap=(offset,count)=>{if(functionsInTableMap){for(var i=offset;i<offset+count;i++){var item=getWasmTableEntry(i);if(item){functionsInTableMap.set(item,i)}}}};var functionsInTableMap;var getFunctionAddress=func=>{if(!functionsInTableMap){functionsInTableMap=new WeakMap;updateTableMap(0,wasmTable.length)}return functionsInTableMap.get(func)||0};var freeTableIndexes=[];var getEmptyTableSlot=()=>{if(freeTableIndexes.length){return freeTableIndexes.pop()}try{wasmTable.grow(1)}catch(err){if(!(err instanceof RangeError)){throw err}throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH."}return wasmTable.length-1};var setWasmTableEntry=(idx,func)=>wasmTable.set(idx,func);var addFunction=(func,sig)=>{var rtn=getFunctionAddress(func);if(rtn){return rtn}var ret=getEmptyTableSlot();try{setWasmTableEntry(ret,func)}catch(err){if(!(err instanceof TypeError)){throw err}var wrapped=convertJsFunctionToWasm(func,sig);setWasmTableEntry(ret,wrapped)}functionsInTableMap.set(func,ret);return ret};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"]}Module["UTF8ToString"]=UTF8ToString;Module["addFunction"]=addFunction;Module["stringToUTF8"]=stringToUTF8;Module["lengthBytesUTF8"]=lengthBytesUTF8;function cuiConsoleError(msg){console.error(UTF8ToString(msg))}var wasmImports={u:___cxa_begin_catch,M:___cxa_end_catch,a:___cxa_find_matching_catch_2,m:___cxa_find_matching_catch_3,N:___cxa_rethrow,t:___cxa_throw,d:___resumeException,E:__abort_js,L:cuiConsoleError,D:_emscripten_resize_heap,G:_fd_close,F:_fd_seek,w:_fd_write,n:invoke_fi,v:invoke_fii,j:invoke_fiif,h:invoke_fiiif,i:invoke_fiiiff,z:invoke_fiiifff,c:invoke_ii,s:invoke_iiffiiiffiiiii,e:invoke_iii,r:invoke_iiii,p:invoke_iiiii,J:invoke_iiiiii,B:invoke_iiiiiiiiffff,K:invoke_iiiiiiiiii,o:invoke_v,b:invoke_vi,A:invoke_vif,I:invoke_viffi,l:invoke_vifi,f:invoke_vii,x:invoke_viif,P:invoke_viiff,k:invoke_viii,O:invoke_viiiffffii,C:invoke_viiiffiiiii,g:invoke_viiii,H:invoke_viiiif,y:invoke_viiiii,q:invoke_viiiiii};var wasmExports=await createWasm();var ___wasm_call_ctors=wasmExports["R"];var _setMeasureFunc=Module["_setMeasureFunc"]=wasmExports["T"];var _getScreenId=Module["_getScreenId"]=wasmExports["U"];var _createNodeRoot=Module["_createNodeRoot"]=wasmExports["V"];var _createNode=Module["_createNode"]=wasmExports["W"];var _cancelSubtreeLoops=Module["_cancelSubtreeLoops"]=wasmExports["X"];var _freeNode=Module["_freeNode"]=wasmExports["Y"];var _removeNode=Module["_removeNode"]=wasmExports["Z"];var _addStringHandler=Module["_addStringHandler"]=wasmExports["_"];var _addFloatHandler=Module["_addFloatHandler"]=wasmExports["$"];var _addUIntHandler=Module["_addUIntHandler"]=wasmExports["aa"];var _addResetHandler=Module["_addResetHandler"]=wasmExports["ba"];var _clearUIHandlers=Module["_clearUIHandlers"]=wasmExports["ca"];var _setMatrixHandler=Module["_setMatrixHandler"]=wasmExports["da"];var _setTransformOriginHandler=Module["_setTransformOriginHandler"]=wasmExports["ea"];var _setGradientHandler=Module["_setGradientHandler"]=wasmExports["fa"];var _setGradientsHandler=Module["_setGradientsHandler"]=wasmExports["ga"];var _setFloat=Module["_setFloat"]=wasmExports["ha"];var _setFloatExt=Module["_setFloatExt"]=wasmExports["ia"];var _getColor=Module["_getColor"]=wasmExports["ja"];var _setString=Module["_setString"]=wasmExports["ka"];var _setStringExt=Module["_setStringExt"]=wasmExports["la"];var _getThemeValue=Module["_getThemeValue"]=wasmExports["ma"];var _resetProp=Module["_resetProp"]=wasmExports["na"];var _resetPropExt=Module["_resetPropExt"]=wasmExports["oa"];var _enableNodeLayer=Module["_enableNodeLayer"]=wasmExports["pa"];var _disableNodeLayer=Module["_disableNodeLayer"]=wasmExports["qa"];var _resetNodeLayer=Module["_resetNodeLayer"]=wasmExports["ra"];var _setNodeLayerLoop=Module["_setNodeLayerLoop"]=wasmExports["sa"];var _cancelNodeLayerLoop=Module["_cancelNodeLayerLoop"]=wasmExports["ta"];var _mergeNodeLayer=Module["_mergeNodeLayer"]=wasmExports["ua"];var _getLayer=Module["_getLayer"]=wasmExports["va"];var _setNodeClass=Module["_setNodeClass"]=wasmExports["wa"];var _setNodePressed=Module["_setNodePressed"]=wasmExports["xa"];var _setNodeFocused=Module["_setNodeFocused"]=wasmExports["ya"];var _calculate=Module["_calculate"]=wasmExports["za"];var _setConditionValue=Module["_setConditionValue"]=wasmExports["Aa"];var _calculateWidget=Module["_calculateWidget"]=wasmExports["Ba"];var _freeWidget=Module["_freeWidget"]=wasmExports["Ca"];var _setSafePaddings=Module["_setSafePaddings"]=wasmExports["Da"];var _overrideRootSafeBottom=Module["_overrideRootSafeBottom"]=wasmExports["Ea"];var _setSafeAreaBars=Module["_setSafeAreaBars"]=wasmExports["Fa"];var _setThemeValue=Module["_setThemeValue"]=wasmExports["Ga"];var _applyTheme=Module["_applyTheme"]=wasmExports["Ha"];var _recalculate=Module["_recalculate"]=wasmExports["Ia"];var _consumeNeedsLayout=Module["_consumeNeedsLayout"]=wasmExports["Ja"];var _getCachedRoot=Module["_getCachedRoot"]=wasmExports["Ka"];var _calculateRoot=Module["_calculateRoot"]=wasmExports["La"];var _collectBegin=Module["_collectBegin"]=wasmExports["Ma"];var _collectRoot=Module["_collectRoot"]=wasmExports["Na"];var _collectCount=Module["_collectCount"]=wasmExports["Oa"];var _collectNodes=Module["_collectNodes"]=wasmExports["Pa"];var _collectRects=Module["_collectRects"]=wasmExports["Qa"];var _markNodeDirty=Module["_markNodeDirty"]=wasmExports["Ra"];var _nodeHasFixedSize=Module["_nodeHasFixedSize"]=wasmExports["Sa"];var _nodeHasMeasure=Module["_nodeHasMeasure"]=wasmExports["Ta"];var _getOffset=Module["_getOffset"]=wasmExports["Ua"];var _getParentNode=Module["_getParentNode"]=wasmExports["Va"];var _getContentHeight=Module["_getContentHeight"]=wasmExports["Wa"];var _getContentWidth=Module["_getContentWidth"]=wasmExports["Xa"];var _getLayoutPointer=Module["_getLayoutPointer"]=wasmExports["Ya"];var _getLayoutDOMPointer=Module["_getLayoutDOMPointer"]=wasmExports["Za"];var _getPaddingPointer=Module["_getPaddingPointer"]=wasmExports["_a"];var _setVListHandlers=Module["_setVListHandlers"]=wasmExports["$a"];var _vlistInit=Module["_vlistInit"]=wasmExports["ab"];var _vlistExists=Module["_vlistExists"]=wasmExports["bb"];var _vlistFree=Module["_vlistFree"]=wasmExports["cb"];var _vlistSetKeys=Module["_vlistSetKeys"]=wasmExports["db"];var _vlistInsertKeys=Module["_vlistInsertKeys"]=wasmExports["eb"];var _vlistRemoveKeys=Module["_vlistRemoveKeys"]=wasmExports["fb"];var _vlistInvalidate=Module["_vlistInvalidate"]=wasmExports["gb"];var _vlistCreateItemRoot=Module["_vlistCreateItemRoot"]=wasmExports["hb"];var _vlistItemMounted=Module["_vlistItemMounted"]=wasmExports["ib"];var _vlistItemUnmounted=Module["_vlistItemUnmounted"]=wasmExports["jb"];var _vlistOnScroll=Module["_vlistOnScroll"]=wasmExports["kb"];var _vlistUpdateLayout=Module["_vlistUpdateLayout"]=wasmExports["lb"];var _vlistToPhysical=Module["_vlistToPhysical"]=wasmExports["mb"];var _vlistOffsetOf=Module["_vlistOffsetOf"]=wasmExports["nb"];var _vlistItemOffsetOf=Module["_vlistItemOffsetOf"]=wasmExports["ob"];var _free=Module["_free"]=wasmExports["pb"];var _malloc=Module["_malloc"]=wasmExports["qb"];var _setThrew=wasmExports["rb"];var __emscripten_tempret_set=wasmExports["sb"];var __emscripten_stack_restore=wasmExports["tb"];var _emscripten_stack_get_current=wasmExports["ub"];var ___cxa_increment_exception_refcount=wasmExports["vb"];var ___cxa_decrement_exception_refcount=wasmExports["wb"];var ___cxa_can_catch=wasmExports["xb"];var ___cxa_get_exception_ptr=wasmExports["yb"];function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiffiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vifi(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiffff(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fi(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiff(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vif(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiiiff(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiiif(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiif(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiffiiiffiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiffffii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiiifff(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viif(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viffi(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiif(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function run(){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}function preInit(){if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}preInit();run();moduleRtn=readyPromise;
8
+
9
+
10
+ return moduleRtn;
11
+ }
12
+ );
13
+ })();
14
+ export default Module;
Binary file
@@ -0,0 +1,176 @@
1
+ /* Background images never tile by default, app-wide — a repeated texture is almost never intended
2
+ and silently breaks a single-image background. Covers everything on the app root, including nodes
3
+ outside a screen (widgets, toasts); the per-scope rules below still add background-position. */
4
+ * {
5
+ background-repeat: no-repeat;
6
+ }
7
+
8
+ .screen {
9
+ pointer-events: none;
10
+ background-repeat: no-repeat;
11
+ background-position: center;
12
+ background-size: cover;
13
+ width: 100%;
14
+ height: 100%;
15
+ overflow: hidden;
16
+ position: absolute;
17
+ top: 0;
18
+ left: 0;
19
+ user-select: none;
20
+ -webkit-user-select: none;
21
+ -webkit-touch-callout: none; /* no iOS long-press callout on images/text (long-press is the app's) */
22
+ text-align: left;
23
+ /* Renderer defaults sourced from the theme (docs/ui-theme-plan.md): --color/--fontFamily are
24
+ set by _creatorUI.setTheme on the app root. Unset --fontFamily leaves font-family invalid →
25
+ inherited, exactly the pre-theme behavior. */
26
+ color: var(--color, white);
27
+ font-family: var(--fontFamily);
28
+ font-size: 14px;
29
+ }
30
+ .screen.interactive {
31
+ pointer-events: all;
32
+ }
33
+ .scrollable {
34
+ overflow-y:auto;
35
+ overflow-x:hidden;
36
+ pointer-events: all;
37
+ }
38
+ .vlist {
39
+ overflow-y:auto;
40
+ overflow-x:hidden;
41
+ pointer-events: all;
42
+ }
43
+ .vlist::before {
44
+ content: "";
45
+ display: block;
46
+ height: var(--scroll-height);
47
+ }
48
+ .scrollable::-webkit-scrollbar {
49
+ width: 2px;
50
+ }
51
+ .scrollable::before {
52
+ content: "";
53
+ display: block;
54
+ height: var(--scroll-height);
55
+ width: var(--scroll-width);
56
+ }
57
+ /* UIPager: a horizontal scroll-snap strip of tab cells; each cell holds that tab's stack of
58
+ page layers, of which only the top is shown. */
59
+ .pager {
60
+ display: flex;
61
+ flex-direction: row;
62
+ pointer-events: all;
63
+ overflow-x: auto;
64
+ overflow-y: hidden;
65
+ scroll-snap-type: x mandatory;
66
+ scrollbar-width: none;
67
+ }
68
+ /* One tab, or the current tab drilled past its root — programmatic paging only. */
69
+ .pager.pager_locked {
70
+ overflow: hidden;
71
+ }
72
+ .pager::-webkit-scrollbar {
73
+ width: 0;
74
+ height: 0;
75
+ }
76
+ .pager-tab {
77
+ flex: 0 0 100%;
78
+ width: 100%;
79
+ height: 100%;
80
+ position: relative;
81
+ overflow: hidden;
82
+ scroll-snap-align: start;
83
+ scroll-snap-stop: always;
84
+ }
85
+ .pager-page {
86
+ position: absolute;
87
+ inset: 0;
88
+ overflow: hidden;
89
+ }
90
+ .pager-page.pager-page_hidden {
91
+ display: none;
92
+ }
93
+ .scrollable_horizontal {
94
+ overflow-x:auto;
95
+ overflow-y:hidden;
96
+ }
97
+ .scrollable_horizontal::-webkit-scrollbar {
98
+ height: 2px;
99
+ }
100
+ ::-webkit-scrollbar-track {
101
+ background: transparent;
102
+ border-radius: 4px;
103
+ }
104
+ ::-webkit-scrollbar-thumb {
105
+ background: rgba(0,0,0,0.8);
106
+ border-radius: 4px;
107
+ }
108
+ ::-webkit-scrollbar-thumb:hover {
109
+ background: #555;
110
+ }
111
+ button {
112
+ border: none;
113
+ outline: none;
114
+ cursor: pointer;
115
+ pointer-events: all;
116
+ background: none;
117
+ padding: 0;
118
+ text-align: left;
119
+ color: inherit;
120
+ /* Form controls don't inherit font by default — a <button> keeps the UA's
121
+ control font (Arial), which the canvas measure system never assumes (it
122
+ measures against the app default, Roboto/the theme font). The mismatch sizes
123
+ a label's box for the narrower font and the wider rendered text then wraps.
124
+ Inherit the app font so measurement and paint agree. */
125
+ font: inherit;
126
+ }
127
+ img {
128
+ object-fit: contain;
129
+ user-select: none;
130
+ user-drag: none;
131
+ -webkit-user-drag: none;
132
+ }
133
+ video {
134
+ object-fit: contain;
135
+ user-select: none;
136
+ -webkit-user-drag: none;
137
+ }
138
+ input, textarea {
139
+ pointer-events: all;
140
+ color: black;
141
+ border: none;
142
+ outline: none;
143
+ padding: 0;
144
+ background: none;
145
+ resize: none;
146
+ /* Same reason as button: inherit the app font so the editable's rendered text
147
+ matches what the measure system sized its box for (default family = Roboto). */
148
+ font: inherit;
149
+ }
150
+ input::placeholder {
151
+ color: var(--placeholder-color, #aaa);
152
+ }
153
+ textarea::placeholder {
154
+ color: var(--placeholder-color, #aaa);
155
+ }
156
+
157
+ .screen * {
158
+ background-repeat: no-repeat;
159
+ background-position: center;
160
+ }
161
+
162
+ .spacer {
163
+ display: none;
164
+ }
165
+
166
+ .widget {
167
+ z-index: 10;
168
+ /* Like .screen: a widget is pointer-transparent by default, so a full-screen widget (a HUD over a
169
+ scene) lets taps on its empty areas fall through to the scene / whatever is beneath. Its
170
+ interactive children (buttons, inputs, scrollables) re-enable pointer-events themselves. A widget
171
+ that wants to catch taps on its whole surface gets .interactive (a root onTouchStart). */
172
+ pointer-events: none;
173
+ }
174
+ .widget.interactive {
175
+ pointer-events: all;
176
+ }
@@ -0,0 +1,9 @@
1
+ // Style-layer numbering shared with creator-ui.cpp (`getLayer`) — the layer number IS the cascade
2
+ // (highest active layer wins). Keep in lockstep with packages/creator-ui/src/creator-ui.cpp:
3
+ // 256 default, 257/258 orientation, 272–59999 user $-classes (allocated by `getLayer` on first
4
+ // sight), 60268/60269 the reserved `$pressed`/`$focused` classes (cascading, toggled via
5
+ // setNodePressed/setNodeFocused), then interaction feedback, then animate layers on top.
6
+ export const LAYER_PRESSED = 60270
7
+ export const LAYER_FOCUSED = 60271
8
+ /** animateTo/animateFrom layers live at ANIMATE_LAYER_BASE + (style.layer ?? 44). */
9
+ export const ANIMATE_LAYER_BASE = 61000
@@ -0,0 +1,213 @@
1
+ // Browser mount adapter for the `lecodes-renderer` package. Produces a `_creatorUI`-shaped renderer (the
2
+ // same contract `createUI` returns) so it drops straight into `mountCreator` via its `uiFactory`
3
+ // param. It reuses the viewer's own creator-ui WASM (Vite-bundled here) for layout, paints the UI to
4
+ // a 2D <canvas> overlaid on the 3D canvas, and forwards pointer events.
5
+
6
+ // @ts-ignore — emscripten glue, no type declarations
7
+ import createModule from "./creator-ui"
8
+ import wasmUrl from "./creator-ui.wasm?url"
9
+ import {
10
+ BrowserHost,
11
+ Canvas2DPainter,
12
+ CreatorUiLayout,
13
+ createCanvasUI,
14
+ loadCreatorUiFromModule,
15
+ } from "lecodes-renderer"
16
+ import { getUrl } from "../utils/fetch"
17
+ import { orientedSafeArea, type SafeAreaSpec } from "./safeAreaSpec"
18
+ import { viewScale } from "./viewScale"
19
+
20
+ // Explicit `any` return: the inferred type references lecodes-renderer internals TS can't name
21
+ // portably (TS2742), and the `_creatorUI` contract is untyped by design (host bridge shape).
22
+ export async function createCanvasUIRenderer(root: HTMLElement, canvasHost?: { getSurface(id: number): { canvas: unknown, width: number, height: number } | null }): Promise<any> {
23
+ const wasmBinary = await fetch(wasmUrl).then((r) => r.arrayBuffer())
24
+ const module = await loadCreatorUiFromModule(createModule, wasmBinary)
25
+ const layout = new CreatorUiLayout(module)
26
+ // Resolve uploaded-asset image srcs ({ _id }) to blob urls via the viewer's fetch-buffer store, and
27
+ // UIImage(canvas) surfaces via the canvas host. SvgSource/File are handled in the host.
28
+ const ui = await createCanvasUI({ host: new BrowserHost({ resolveAssetUrl: (id) => getUrl(id), canvasSurfaces: canvasHost }), layout })
29
+
30
+ const canvas = document.createElement("canvas")
31
+ canvas.style.position = "absolute"
32
+ canvas.style.inset = "0"
33
+ canvas.style.width = "100%"
34
+ canvas.style.height = "100%"
35
+ canvas.style.touchAction = "none"
36
+ root.appendChild(canvas)
37
+
38
+ const ctx = canvas.getContext("2d")!
39
+ // Resolution factor (devicePixelRatio × CSS zoom) the canvas is painted at. The painter bakes it
40
+ // into its transform, so it's recreated whenever the factor changes (a resize onto a different-DPR
41
+ // display, or the simulator's zoom). See docs/simulator-plan.md phase 3.
42
+ const computeFactor = () => (window.devicePixelRatio || 1) * viewScale(canvas)
43
+ let factor = computeFactor()
44
+ let painter = new Canvas2DPainter(ctx, factor)
45
+
46
+ // ---- simulated keyboard (DOM) + key capture ----
47
+ // Core re-lays-out the UI into the area above the keyboard (it is NOT scaled); here we only draw the
48
+ // keyboard panel over the bottom band and keep a hidden <input> focused to capture physical
49
+ // keystrokes → ui.setInputValue.
50
+ const keyboard = buildKeyboard()
51
+ keyboard.el.style.display = "none"
52
+ root.appendChild(keyboard.el)
53
+
54
+ const capture = document.createElement("input")
55
+ capture.setAttribute("autocomplete", "off")
56
+ capture.style.cssText = "position:absolute;left:0;top:0;width:1px;height:1px;opacity:0;pointer-events:none;border:0;padding:0;"
57
+ root.appendChild(capture)
58
+ let captureNodeId = 0
59
+ capture.addEventListener("input", () => {
60
+ if (captureNodeId) ui.setInputValue(captureNodeId, capture.value)
61
+ })
62
+
63
+ ui.onKeyboardChange((state) => {
64
+ if (state) {
65
+ captureNodeId = state.input.nodeId
66
+ capture.type = state.input.type === "password" ? "password" : "text"
67
+ if (capture.value !== state.input.value) capture.value = state.input.value
68
+ capture.focus({ preventScroll: true })
69
+ keyboard.show(state.rect)
70
+ } else {
71
+ captureNodeId = 0
72
+ capture.blur()
73
+ keyboard.el.style.display = "none"
74
+ }
75
+ })
76
+
77
+ let raf = 0
78
+ let last = 0
79
+ // Set whenever the backing store is re-sized: writing canvas.width/height WIPES the canvas, and the
80
+ // renderer's own dirty state doesn't know that. When only the resolution factor moved (same logical
81
+ // size — a DPR/zoom change, or the host's ResizeObserver landing right after the mount-time resize)
82
+ // the layout is unchanged, so needsPaint() stays false and the idle-gate would leave the app on a
83
+ // blank canvas until the next mutation or a REAL resize. Lost pixels are a host fact, not renderer
84
+ // state — track them here and repaint once.
85
+ let pixelsLost = false
86
+ const loop = (now: number) => {
87
+ const dt = last === 0 ? 0 : now - last
88
+ last = now
89
+ // Idle-gate (matches the desktop host): only advance tweens when some are running, and only repaint
90
+ // when something actually changed (a mutation, input, scroll, or an in-flight tween). A static UI
91
+ // reuses the cached render tree and does zero work here instead of rebuilding + repainting every rAF.
92
+ if (ui.hasActiveAnimations()) ui.tick(dt)
93
+ if (pixelsLost || ui.needsPaint()) {
94
+ pixelsLost = false
95
+ ui.paint(painter)
96
+ }
97
+ raf = requestAnimationFrame(loop)
98
+ }
99
+
100
+ // Device-emulation safe-area override (the Simulator's iOS/Android shells). Without a spec the
101
+ // renderer keeps its own orientation defaults (Renderer.safeInsets — they match the DOM host's).
102
+ // Keyed change-detection: setSafeArea relayouts immediately, so only call it when the resolved
103
+ // insets/bars actually moved (a spec change or an orientation flip), not on every resize.
104
+ let safeAreaSpec: SafeAreaSpec | null = null
105
+ let appliedSafeKey = ""
106
+ const applySafeArea = (width: number, height: number) => {
107
+ const oriented = safeAreaSpec ? orientedSafeArea(safeAreaSpec, width > height) : null
108
+ const key = oriented ? JSON.stringify(oriented) : ""
109
+ if (key === appliedSafeKey) return
110
+ appliedSafeKey = key
111
+ ui.setSafeArea(oriented ? oriented.insets : null, oriented?.bars)
112
+ }
113
+
114
+ let cachedWidth = 0, cachedHeight = 0
115
+ const _onResize = (width: number, height: number, hostFactor?: number) => {
116
+ if (width <= 0 || height <= 0) return
117
+ const nextFactor = hostFactor ?? computeFactor()
118
+ if (nextFactor !== factor) { factor = nextFactor; painter = new Canvas2DPainter(ctx, factor) }
119
+ canvas.width = Math.round(width * factor)
120
+ canvas.height = Math.round(height * factor)
121
+ canvas.style.width = width + "px"
122
+ canvas.style.height = height + "px"
123
+ pixelsLost = true // the two writes above cleared the canvas — see the loop
124
+ applySafeArea(width, height) // before resize → the relayout below already sees the new insets
125
+ ui.resize(width, height)
126
+ cachedWidth = width; cachedHeight = height
127
+ }
128
+
129
+ const local = (e: PointerEvent): [number, number] => {
130
+ const r = canvas.getBoundingClientRect()
131
+ // Deliver logical px to the renderer — clientX/rect are VISUAL px under the simulator's CSS zoom.
132
+ const scale = viewScale(canvas)
133
+ return [(e.clientX - r.left) / scale, (e.clientY - r.top) / scale]
134
+ }
135
+ const onDown = (e: PointerEvent) => {
136
+ canvas.setPointerCapture(e.pointerId)
137
+ const [x, y] = local(e)
138
+ ui.pointerDown(e.pointerId, x, y)
139
+ }
140
+ const onMove = (e: PointerEvent) => {
141
+ const [x, y] = local(e)
142
+ ui.pointerMove(e.pointerId, x, y)
143
+ }
144
+ const onUp = (e: PointerEvent) => {
145
+ const [x, y] = local(e)
146
+ ui.pointerUp(e.pointerId, x, y)
147
+ }
148
+ canvas.addEventListener("pointerdown", onDown)
149
+ canvas.addEventListener("pointermove", onMove)
150
+ canvas.addEventListener("pointerup", onUp)
151
+
152
+ // Give the renderer a viewport before user code runs its first openScreen(). clientWidth/Height are
153
+ // layout px (zoom-independent) — the logical viewport; the host's ResizeObserver refreshes with the
154
+ // authoritative factor next tick.
155
+ _onResize(root.clientWidth, root.clientHeight)
156
+ raf = requestAnimationFrame(loop)
157
+
158
+ return Object.assign(ui, {
159
+ _onResize,
160
+ // Host-config: pin the safe-area insets/bars (null restores the renderer defaults).
161
+ _setSafeAreaSpec(spec: SafeAreaSpec | null) {
162
+ safeAreaSpec = spec
163
+ if (cachedWidth && cachedHeight) applySafeArea(cachedWidth, cachedHeight)
164
+ },
165
+ getDisplaySize: (): [number, number] => [cachedWidth, cachedHeight],
166
+ _destroy() {
167
+ cancelAnimationFrame(raf)
168
+ canvas.removeEventListener("pointerdown", onDown)
169
+ canvas.removeEventListener("pointermove", onMove)
170
+ canvas.removeEventListener("pointerup", onUp)
171
+ canvas.remove()
172
+ keyboard.el.remove()
173
+ capture.remove()
174
+ },
175
+ })
176
+ }
177
+
178
+ /**
179
+ * A non-functional on-screen keyboard panel (a few rows of key-like cells). Its only job is to occupy
180
+ * the bottom band so keyboard-related layout bugs surface in the preview. `pointerdown` is swallowed
181
+ * (preventDefault) so tapping it doesn't blur the hidden capture field.
182
+ */
183
+ function buildKeyboard() {
184
+ const el = document.createElement("div")
185
+ // `space-evenly` distributes the leftover band height as even gaps between rows AND above the top /
186
+ // below the bottom row (the requested top/bottom padding). Keys are a fixed height so they read as
187
+ // keys instead of stretching to fill the band.
188
+ el.style.cssText =
189
+ "position:absolute;left:0;right:0;bottom:0;box-sizing:border-box;background:#caced6;" +
190
+ "display:flex;flex-direction:column;justify-content:space-evenly;align-items:stretch;" +
191
+ "padding:6px 5px;z-index:20;user-select:none;-webkit-user-select:none;touch-action:none;"
192
+ const rows = ["10", "9", "7", "5"] // key counts per row (decorative)
193
+ for (const count of rows) {
194
+ const row = document.createElement("div")
195
+ row.style.cssText = "display:flex;justify-content:center;align-items:center;gap:6px;flex:0 0 auto;"
196
+ for (let i = 0; i < Number(count); i++) {
197
+ const key = document.createElement("div")
198
+ key.style.cssText =
199
+ "flex:1 1 0;max-width:32px;height:40px;background:#fff;border-radius:6px;box-shadow:0 1px 0 rgba(0,0,0,0.25);"
200
+ row.appendChild(key)
201
+ }
202
+ el.appendChild(row)
203
+ }
204
+ el.addEventListener("pointerdown", (e) => e.preventDefault())
205
+
206
+ return {
207
+ el,
208
+ show(rect: { x: number; y: number; width: number; height: number }) {
209
+ el.style.height = rect.height + "px"
210
+ el.style.display = "flex"
211
+ },
212
+ }
213
+ }
@@ -0,0 +1,91 @@
1
+ // NativeView on the web host (docs/navigation-presentable-plan.md — registerView + the
2
+ // call/event channel + the "native" layout node). The embedding app registers DOM factories:
3
+ //
4
+ // host._creatorUI._registerView("map", (params, channel) => {
5
+ // const el = document.createElement("div")
6
+ // return { el, call: (method, args) => {...}, destroy: () => {...} }
7
+ // })
8
+ //
9
+ // One SDK NativeView instance = one live DOM instance (keyed by its _viewId), reused across
10
+ // embed/fullscreen/pager mounts — that reuse IS the promotion semantics. An unregistered
11
+ // name renders a labeled placeholder so a bundle stays inspectable on hosts without the plugin.
12
+
13
+ export type WebViewChannel = { emit(event: string, data?: any): void }
14
+ export type WebViewInstanceHandle = {
15
+ el: HTMLElement
16
+ call?(method: string, args: any[]): any | Promise<any>
17
+ destroy?(): void
18
+ }
19
+ export type WebViewFactory = (params: any, channel: WebViewChannel) => WebViewInstanceHandle
20
+
21
+ // The SDK-side NativeView element/descriptor surface the system needs (see sdk NativeView.ts).
22
+ type SdkNativeView = {
23
+ viewName: string
24
+ params: any
25
+ _viewId: number
26
+ _emitViewEvent(event: string, dataJson?: string): void
27
+ }
28
+
29
+ export const createNativeViewSystem = () => {
30
+ const factories = new Map<string, WebViewFactory>()
31
+ const instances = new Map<number, WebViewInstanceHandle>()
32
+
33
+ const registerView = (name: string, factory: WebViewFactory) => { factories.set(name, factory) }
34
+ const isViewSupported = (name: string) => factories.has(name)
35
+
36
+ const placeholder = (name: string): WebViewInstanceHandle => {
37
+ const el = document.createElement("div")
38
+ el.style.cssText = "display:flex;align-items:center;justify-content:center;background:#333;color:#999;font:12px sans-serif"
39
+ el.textContent = `NativeView("${name}") — no web factory registered`
40
+ return { el }
41
+ }
42
+
43
+ /** Resolve (or lazily create) the live instance for an SDK NativeView. */
44
+ const ensureInstance = (view: SdkNativeView): WebViewInstanceHandle => {
45
+ let instance = instances.get(view._viewId)
46
+ if (!instance) {
47
+ const factory = factories.get(view.viewName)
48
+ instance = factory
49
+ ? factory(view.params, { emit: (event, data) => view._emitViewEvent(event, data === undefined ? undefined : JSON.stringify(data)) })
50
+ : placeholder(view.viewName)
51
+ instances.set(view._viewId, instance)
52
+ }
53
+ return instance
54
+ }
55
+
56
+ /** The `_creatorUI.viewCall` bridge — JSON in/out, promise-friendly. */
57
+ const viewCall = (viewId: number, method: string, args: string, onComplete: (result?: string) => void, onError: (err: string) => void) => {
58
+ const instance = instances.get(viewId)
59
+ if (!instance?.call) {
60
+ onError(`NativeView call "${method}": no live instance or no call handler (viewId ${viewId})`)
61
+ return
62
+ }
63
+ try {
64
+ Promise.resolve(instance.call(method, JSON.parse(args))).then(
65
+ (result) => onComplete(result === undefined ? undefined : JSON.stringify(result)),
66
+ (e) => onError(e instanceof Error ? e.message : String(e)),
67
+ )
68
+ } catch (e) {
69
+ onError(e instanceof Error ? e.message : String(e))
70
+ }
71
+ }
72
+
73
+ /** Mount the live instance into a layout cell (the "native" node's DOM element). A view lives
74
+ * in ONE place: mounting here steals it from wherever it was (promotion works the same way
75
+ * in reverse — openView steals it from the cell, close returns it via re-mount). */
76
+ const mountInto = (view: SdkNativeView, cell: HTMLElement) => {
77
+ const { el } = ensureInstance(view)
78
+ el.style.position = "absolute"
79
+ el.style.inset = "0"
80
+ cell.appendChild(el)
81
+ }
82
+
83
+ const destroy = () => {
84
+ for (const i of instances.values()) i.destroy?.()
85
+ instances.clear()
86
+ }
87
+
88
+ return { registerView, isViewSupported, ensureInstance, viewCall, mountInto, destroy }
89
+ }
90
+
91
+ export type NativeViewSystem = ReturnType<typeof createNativeViewSystem>