added more documentation
This commit is contained in:
		
							parent
							
								
									c0405e344c
								
							
						
					
					
						commit
						65e38dc898
					
				
					 11 changed files with 328 additions and 295 deletions
				
			
		
							
								
								
									
										2
									
								
								.env
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.env
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
git remote | grep codeberg || git remote add codeberg git@codeberg.org:xrsh/xrsh-com.git
 | 
			
		||||
git remote | grep c-frame  || git remote add c-frame git@github.com:c-frame/xrsh-com.git
 | 
			
		||||
							
								
								
									
										41
									
								
								README.awk
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										41
									
								
								README.awk
									
										
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
#!/usr/bin/env -S awk -f
 | 
			
		||||
# a no-nonsense source-to-markdown generator which scans for:
 | 
			
		||||
#
 | 
			
		||||
# /**
 | 
			
		||||
#  * # foo
 | 
			
		||||
#  *
 | 
			
		||||
#  * this is markdown $(cat bar.md)
 | 
			
		||||
#  */
 | 
			
		||||
#
 | 
			
		||||
#  var foo; //  comment with 2 leading spaces is markdown too $(date)
 | 
			
		||||
#
 | 
			
		||||
# easily refactorable to hash-based languages (py/bash/perl/lua e.g.) 
 | 
			
		||||
# by changing the regexes
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
BEGIN{
 | 
			
		||||
  # printf README.md until '# Component List'
 | 
			
		||||
  system("grep -B9999 '# Component List' README.md")
 | 
			
		||||
  print ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/\$\(/                   { cmd=$0; 
 | 
			
		||||
                           gsub(/^.*\$\(/,"",cmd); 
 | 
			
		||||
                           gsub(/\).*/,"",cmd);
 | 
			
		||||
                           cmd | getline stdout; close(cmd);
 | 
			
		||||
                           sub(/\$\(.*\)/,stdout);
 | 
			
		||||
                         } 
 | 
			
		||||
/\/\*\*/                 { doc=1; sub(/^.*\/\*/,""); }
 | 
			
		||||
doc && /\*\//            { doc=0;
 | 
			
		||||
                           sub(/[[:space:]]*\*\/.*/,"");
 | 
			
		||||
                           sub(/^[[:space:]]*\*[[:space:]]?/,"");
 | 
			
		||||
                           print
 | 
			
		||||
                         }
 | 
			
		||||
doc && /^[[:space:]]*\*/ { sub(/^[[:space:]]*\*[[:space:]]?/,""); 
 | 
			
		||||
                           print 
 | 
			
		||||
                         }
 | 
			
		||||
#!doc && /\/\/  /         { sub(".*//  ",""); 
 | 
			
		||||
#                           sub("# ","\n# ");
 | 
			
		||||
#                           sub("> ","\n> ");
 | 
			
		||||
#                           print
 | 
			
		||||
#                         }
 | 
			
		||||
							
								
								
									
										149
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										149
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -17,6 +17,10 @@ Characteristics:
 | 
			
		|||
<a-entity helloworld="foo:1" class="cubes" name="box">  
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
See component list below
 | 
			
		||||
 | 
			
		||||
> this README.md is generated by running `echo "$(./README.awk com/*.js)" > README.md`
 | 
			
		||||
 | 
			
		||||
## Funding
 | 
			
		||||
 | 
			
		||||
This project is partially funded through [NGI0 Entrust](https://nlnet.nl/entrust), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/xrsh).
 | 
			
		||||
| 
						 | 
				
			
			@ -24,3 +28,148 @@ This project is partially funded through [NGI0 Entrust](https://nlnet.nl/entrust
 | 
			
		|||
[<img src="https://nlnet.nl/logo/banner.png" alt="NLnet foundation logo" width="20%" />](https://nlnet.nl)
 | 
			
		||||
[<img src="https://nlnet.nl/image/logos/NGI0_tag.svg" alt="NGI Zero Logo" width="20%" />](https://nlnet.nl/entrust)
 | 
			
		||||
 | 
			
		||||
# Component List
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [data_events](com/data_events.js)
 | 
			
		||||
 | 
			
		||||
allows components to react to data changes 
 | 
			
		||||
 | 
			
		||||
```html 
 | 
			
		||||
 <script>
 | 
			
		||||
   AFRAME.registerComponent('mycom',{
 | 
			
		||||
     init: function(){ this.data.foo = 1 }, 
 | 
			
		||||
     event: {
 | 
			
		||||
       foo:   (e) => alert("I was updated!")
 | 
			
		||||
     }
 | 
			
		||||
   })
 | 
			
		||||
 </script>
 | 
			
		||||
 | 
			
		||||
 <a-entity mycom data_events/>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [html-as-texture-in-xr](com/html-as-texture-in-xr.js)
 | 
			
		||||
 | 
			
		||||
shows domid **only** in immersive mode 
 | 
			
		||||
(wrapper around [aframe-htmlmesh](https://ada.is/aframe-htmlmesh/)
 | 
			
		||||
 | 
			
		||||
It also sets class 'XR' to the (HTML) body-element in immersive mode.
 | 
			
		||||
This allows CSS (in [dom component](com/dom.js)) to visually update accordingly.
 | 
			
		||||
 | 
			
		||||
> depends on [AFRAME.utils.require](com/require.js)
 | 
			
		||||
 | 
			
		||||
```html 
 | 
			
		||||
 <style type="text/css">
 | 
			
		||||
   .XR #foo { color:red; }
 | 
			
		||||
 </style>
 | 
			
		||||
 | 
			
		||||
 <a-entity html-as-texture-in-xr="domid: #foo">
 | 
			
		||||
   <b id="foo">hello</b>
 | 
			
		||||
 </a-entitiy>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| property     | type               |
 | 
			
		||||
|--------------|--------------------|
 | 
			
		||||
| `domid`      | `string`           |
 | 
			
		||||
 | 
			
		||||
| event        | target     | info                                 |
 | 
			
		||||
|--------------|------------|--------------------------------------|
 | 
			
		||||
| `3D`         | a-scene    | fired when going into immersive mode |
 | 
			
		||||
| `2D`         | a-scene    | fired when leaving immersive mode    |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [isoterminal](com/isoterminal.js)
 | 
			
		||||
 | 
			
		||||
Renders a windowed terminal in both (non)immersive mode.
 | 
			
		||||
It displays an interactive javascript console or boots into 
 | 
			
		||||
a Linux ISO image (via WASM).
 | 
			
		||||
 | 
			
		||||
```html 
 | 
			
		||||
  <a-entity isoterminal="iso: xrsh.iso" position="0 1.6 -0.3"></a-entity>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
> depends on [AFRAME.utils.require](com/require.js)
 | 
			
		||||
 | 
			
		||||
| property         | type      | default                | info |
 | 
			
		||||
|------------------|-----------|------------------------|------|
 | 
			
		||||
| `iso`            | `string`  | https`//forgejo.isvery.ninja/assets/xrsh-buildroot/main/xrsh.iso" | |
 | 
			
		||||
| `overlayfs`      | `string`  | *WORK-IN-PROGRESS* | |
 | 
			
		||||
| `width`          | `number`  |  800   ||
 | 
			
		||||
| `height`         | `number`  |  600   ||
 | 
			
		||||
| `depth`          | `number`  |  0.03  ||
 | 
			
		||||
| `lineHeight`     | `number`  |  18    ||
 | 
			
		||||
| `prompt`         | `boolean` |  true  | boot straight into ISO or give user choice |
 | 
			
		||||
| `padding`        | `number`` |  18    | |
 | 
			
		||||
| `maximized`      | `boolean` | false  | |
 | 
			
		||||
| `minimized`      | `boolean` | false  | |
 | 
			
		||||
| `muteUntilPrompt`| `boolean` | true   | mute stdout until a prompt is detected in ISO |
 | 
			
		||||
| `HUD`            | `boolean` | false  | link to camera movement |
 | 
			
		||||
| `transparent`    | `boolean` | false  | heavy, needs good gpu |
 | 
			
		||||
| `memory`         | `number`  | 60     | VM memory (in MB) [NOTE` quest or smartphone webworker might crash > 40mb ] |
 | 
			
		||||
| `bufferLatency`  | `number`  | 1      | in ms` bufferlatency from webworker to term (batch-update every char to texture) |
 | 
			
		||||
| `debug`          | `boolean` | false  | |
 | 
			
		||||
| `emulator`       | `string`  | fbterm | terminal emulator |
 | 
			
		||||
 | 
			
		||||
> for more info see [xrsh.isvery.ninja](https://xrsh.isvery.ninja)
 | 
			
		||||
 | 
			
		||||
Component design:
 | 
			
		||||
```
 | 
			
		||||
                  css/html template                                                                                
 | 
			
		||||
                                                                                                                    
 | 
			
		||||
                    ┌─────────┐   ┌────────────┐                  exit-AR                                
 | 
			
		||||
           ┌───────►│ com/dom ┼──►│ com/window ├───────────────── exit-VR  ◄─┐                           
 | 
			
		||||
           │        └─────────┘   └───────────┬┘                             │                           
 | 
			
		||||
           │                                  │                              │                           
 | 
			
		||||
┌──────────┴────────┐                         │   ┌───────────┐    ┌─────────────────────────────┐
 | 
			
		||||
│  com/isoterminal  ├────────────────────────────►│com/term.js│    │com/html-as-texture-in-XR.js │
 | 
			
		||||
└────────┬─┬────────┘                         │   └──┬─────┬▲─┘    └─────────────────────────────┘
 | 
			
		||||
         │ │        ┌────────┐             ┌──▼──────▼──────┐                     ││        │                       
 | 
			
		||||
         │ └───────►│ plane  ├─────►text───┼►div#isoterminal│◄────────────────── enter-VR   │                       
 | 
			
		||||
         │          └────────┘             └────────────────┘                    enter-AR ◄─┘                       
 | 
			
		||||
         │                                   │                                                     
 | 
			
		||||
         │                                   │                                                                   
 | 
			
		||||
         │                             ISOTerminal.js                                                                   
 | 
			
		||||
         │                ┌───────────────────────────┐           
 | 
			
		||||
         │                │ com/isoterminal/worker.js ├            
 | 
			
		||||
         │                └──────────────┌────────────┤            
 | 
			
		||||
         │                     │         │ v86.js     │            
 | 
			
		||||
         │                     │         │ feat/*.js  │            
 | 
			
		||||
         │                     │         │ libv86.js  │            
 | 
			
		||||
         │                     │         └────────────┘            
 | 
			
		||||
         │                     │         
 | 
			
		||||
         └─────────────────────┘    
 | 
			
		||||
                                                                                                                    
 | 
			
		||||
NOTE: For convenience reasons, events are forwarded between com/isoterminal.js, worker.js and ISOTerminal
 | 
			
		||||
      Instead of a melting pot of different functionnames, events are flowing through everything (ISOTerminal.emit())
 | 
			
		||||
```       
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [pastedrop](com/pastedrop.js)
 | 
			
		||||
 | 
			
		||||
detects user copy/paste and file dragdrop action
 | 
			
		||||
and clipboard functions
 | 
			
		||||
 | 
			
		||||
```html 
 | 
			
		||||
  <a-entity pastedrop/>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| event        | target | info                                                                                               |
 | 
			
		||||
|--------------|--------|------------------------------------------|
 | 
			
		||||
| `pasteFile`  | self   | always translates input to a File object |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## [require](com/require('').js)
 | 
			
		||||
 | 
			
		||||
automatically requires dependencies 
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
await AFRAME.utils.require( this.dependencies )               (*) autoload missing components
 | 
			
		||||
await AFRAME.utils.require( this.el.getAttributeNames() )     (*) autoload missing components
 | 
			
		||||
await AFRAME.utils.require({foo: "https://foo.com/aframe/components/foo.js"},this)
 | 
			
		||||
await AFRAME.utils.require(["./app/foo.js","foo.css"],this)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
> (*) = prefixes baseURL AFRAME.utils.require.baseURL ('./com/' e.g.)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * ## data_events
 | 
			
		||||
/**
 | 
			
		||||
 * ## [data_events](com/data_events.js)
 | 
			
		||||
 *
 | 
			
		||||
 * allows components to react to data changes 
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
 * ## dom
 | 
			
		||||
 * ## [dom](com/dom.js)
 | 
			
		||||
 *
 | 
			
		||||
 * instances reactive DOM component from AFRAME component's `dom` metadata 
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -36,10 +36,6 @@ if( !AFRAME.components.dom ){
 | 
			
		|||
 | 
			
		||||
  AFRAME.registerComponent('dom',{
 | 
			
		||||
 | 
			
		||||
    requires: {
 | 
			
		||||
      "requestAnimationFrameXR": "com/requestAnimationFrameXR.js"
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    init: function(){
 | 
			
		||||
      Object.values(this.el.components)
 | 
			
		||||
      .map( (c) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,35 @@
 | 
			
		|||
/**
 | 
			
		||||
 * ## [html-as-texture-in-xr](com/html-as-texture-in-xr.js)
 | 
			
		||||
 *
 | 
			
		||||
 * shows domid **only** in immersive mode 
 | 
			
		||||
 * (wrapper around [aframe-htmlmesh](https://ada.is/aframe-htmlmesh/)
 | 
			
		||||
 *
 | 
			
		||||
 * It also sets class 'XR' to the (HTML) body-element in immersive mode.
 | 
			
		||||
 * This allows CSS (in [dom component](com/dom.js)) to visually update accordingly.
 | 
			
		||||
 *
 | 
			
		||||
 * > depends on [AFRAME.utils.require](com/require.js)
 | 
			
		||||
 *
 | 
			
		||||
 * ```html 
 | 
			
		||||
 *  <style type="text/css">
 | 
			
		||||
 *    .XR #foo { color:red; }
 | 
			
		||||
 *  </style>
 | 
			
		||||
 *
 | 
			
		||||
 *  <a-entity html-as-texture-in-xr="domid: #foo">
 | 
			
		||||
 *    <b id="foo">hello</b>
 | 
			
		||||
 *  </a-entitiy>
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
 * | property     | type               |
 | 
			
		||||
 * |--------------|--------------------|
 | 
			
		||||
 * | `domid`      | `string`           |
 | 
			
		||||
 *
 | 
			
		||||
 * | event        | target     | info                                 |
 | 
			
		||||
 * |--------------|------------|--------------------------------------|
 | 
			
		||||
 * | `3D`         | a-scene    | fired when going into immersive mode |
 | 
			
		||||
 * | `2D`         | a-scene    | fired when leaving immersive mode    |
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
if( !AFRAME.components['html-as-texture-in-xr'] ){
 | 
			
		||||
 | 
			
		||||
  AFRAME.registerComponent('html-as-texture-in-xr', {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,40 @@
 | 
			
		|||
/* 
 | 
			
		||||
/** 
 | 
			
		||||
 * ## [isoterminal](com/isoterminal.js)
 | 
			
		||||
 *
 | 
			
		||||
 * Renders a windowed terminal in both (non)immersive mode.
 | 
			
		||||
 * It displays an interactive javascript console or boots into 
 | 
			
		||||
 * a Linux ISO image (via WASM).
 | 
			
		||||
 *
 | 
			
		||||
 * ```html 
 | 
			
		||||
 *   <a-entity isoterminal="iso: xrsh.iso" position="0 1.6 -0.3"></a-entity>
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
 * > depends on [AFRAME.utils.require](com/require.js)
 | 
			
		||||
 *
 | 
			
		||||
 * | property         | type      | default                | info |
 | 
			
		||||
 * |------------------|-----------|------------------------|------|
 | 
			
		||||
 * | `iso`            | `string`  | https`//forgejo.isvery.ninja/assets/xrsh-buildroot/main/xrsh.iso" | |
 | 
			
		||||
 * | `overlayfs`      | `string`  | *WORK-IN-PROGRESS* | |
 | 
			
		||||
 * | `width`          | `number`  |  800   ||
 | 
			
		||||
 * | `height`         | `number`  |  600   ||
 | 
			
		||||
 * | `depth`          | `number`  |  0.03  ||
 | 
			
		||||
 * | `lineHeight`     | `number`  |  18    ||
 | 
			
		||||
 * | `prompt`         | `boolean` |  true  | boot straight into ISO or give user choice |
 | 
			
		||||
 * | `padding`        | `number`` |  18    | |
 | 
			
		||||
 * | `maximized`      | `boolean` | false  | |
 | 
			
		||||
 * | `minimized`      | `boolean` | false  | |
 | 
			
		||||
 * | `muteUntilPrompt`| `boolean` | true   | mute stdout until a prompt is detected in ISO |
 | 
			
		||||
 * | `HUD`            | `boolean` | false  | link to camera movement |
 | 
			
		||||
 * | `transparent`    | `boolean` | false  | heavy, needs good gpu |
 | 
			
		||||
 * | `memory`         | `number`  | 60     | VM memory (in MB) [NOTE` quest or smartphone webworker might crash > 40mb ] |
 | 
			
		||||
 * | `bufferLatency`  | `number`  | 1      | in ms` bufferlatency from webworker to term (batch-update every char to texture) |
 | 
			
		||||
 * | `debug`          | `boolean` | false  | |
 | 
			
		||||
 * | `emulator`       | `string`  | fbterm | terminal emulator |
 | 
			
		||||
 *
 | 
			
		||||
 * > for more info see [xrsh.isvery.ninja](https://xrsh.isvery.ninja)
 | 
			
		||||
 *
 | 
			
		||||
 * Component design:
 | 
			
		||||
 * ```
 | 
			
		||||
 *                   css/html template                                                                                
 | 
			
		||||
 *                                                                                                                     
 | 
			
		||||
 *                     ┌─────────┐   ┌────────────┐                  exit-AR                                
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +62,7 @@
 | 
			
		|||
 *                                                                                                                     
 | 
			
		||||
 * NOTE: For convenience reasons, events are forwarded between com/isoterminal.js, worker.js and ISOTerminal
 | 
			
		||||
 *       Instead of a melting pot of different functionnames, events are flowing through everything (ISOTerminal.emit())
 | 
			
		||||
 * ```       
 | 
			
		||||
 */ 
 | 
			
		||||
 | 
			
		||||
if( typeof AFRAME != 'undefined '){
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,18 @@
 | 
			
		|||
/** 
 | 
			
		||||
 * ## [pastedrop](com/pastedrop.js)
 | 
			
		||||
 *
 | 
			
		||||
 * detects user copy/paste and file dragdrop action
 | 
			
		||||
 * and clipboard functions
 | 
			
		||||
 *
 | 
			
		||||
 * ```html 
 | 
			
		||||
 *   <a-entity pastedrop/>
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
 * | event        | target | info                                                                                               |
 | 
			
		||||
 * |--------------|--------|------------------------------------------|
 | 
			
		||||
 * | `pasteFile`  | self   | always translates input to a File object |
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
AFRAME.registerComponent('pastedrop', {
 | 
			
		||||
  schema: { 
 | 
			
		||||
    foo: { type:"string"}
 | 
			
		||||
| 
						 | 
				
			
			@ -22,16 +37,16 @@ AFRAME.registerComponent('pastedrop', {
 | 
			
		|||
    })
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  //getClipboard: function(){
 | 
			
		||||
  //  navigator.clipboard.readText()
 | 
			
		||||
  //  .then( async (base64) => {
 | 
			
		||||
  //    let mimetype  = base64.replace(/;base64,.*/,'')
 | 
			
		||||
  //    let data = base64.replace(/.*;base64,/,'')
 | 
			
		||||
  //    let type = this.textHeuristic(data)
 | 
			
		||||
  //    const term = document.querySelector('[isoterminal]').components.isoterminal.term
 | 
			
		||||
  //      this.el.emit('pasteFile',{}) /*TODO* data incompatible */
 | 
			
		||||
  //   })
 | 
			
		||||
  //},
 | 
			
		||||
  getClipboard: function(){
 | 
			
		||||
    navigator.clipboard.readText()
 | 
			
		||||
    .then( async (base64) => {
 | 
			
		||||
      let mimetype  = base64.replace(/;base64,.*/,'')
 | 
			
		||||
      let data = base64.replace(/.*;base64,/,'')
 | 
			
		||||
      let type = this.textHeuristic(data)
 | 
			
		||||
      const term = document.querySelector('[isoterminal]').components.isoterminal.term
 | 
			
		||||
        this.el.emit('pasteFile',{}) /*TODO* data incompatible */
 | 
			
		||||
     })
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  onDrop: function(e){
 | 
			
		||||
    e.preventDefault()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,17 @@
 | 
			
		|||
// usage: 
 | 
			
		||||
//
 | 
			
		||||
// await AFRAME.utils.require( this.dependencies )               (*) autoload missing components
 | 
			
		||||
// await AFRAME.utils.require( this.el.getAttributeNames() )     (*) autoload missing components
 | 
			
		||||
// await AFRAME.utils.require({foo: "https://foo.com/aframe/components/foo.js"},this)
 | 
			
		||||
// await AFRAME.utils.require(["./app/foo.js","foo.css"],this)
 | 
			
		||||
// 
 | 
			
		||||
// (*) = prefixes baseURL AFRAME.utils.require.baseURL ('./com/' e.g.)
 | 
			
		||||
//
 | 
			
		||||
/**
 | 
			
		||||
 * ## [require](com/require('').js)
 | 
			
		||||
 *
 | 
			
		||||
 * automatically requires dependencies 
 | 
			
		||||
 * 
 | 
			
		||||
 * ```javascript
 | 
			
		||||
 * await AFRAME.utils.require( this.dependencies )               (*) autoload missing components
 | 
			
		||||
 * await AFRAME.utils.require( this.el.getAttributeNames() )     (*) autoload missing components
 | 
			
		||||
 * await AFRAME.utils.require({foo: "https://foo.com/aframe/components/foo.js"},this)
 | 
			
		||||
 * await AFRAME.utils.require(["./app/foo.js","foo.css"],this)
 | 
			
		||||
 * ```
 | 
			
		||||
 * 
 | 
			
		||||
 * > (*) = prefixes baseURL AFRAME.utils.require.baseURL ('./com/' e.g.)
 | 
			
		||||
 */ 
 | 
			
		||||
AFRAME.utils.require = function(arr_or_obj,opts){
 | 
			
		||||
  opts            = opts || {}
 | 
			
		||||
  let i           = 0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,28 @@
 | 
			
		|||
 * ## [window](com/window.js)
 | 
			
		||||
 *
 | 
			
		||||
 * wraps a draggable window around a dom id or [dom](com/dom.js) component.
 | 
			
		||||
 *
 | 
			
		||||
 * ```html 
 | 
			
		||||
 *   <a-entity window="dom: #mydiv"/>
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
 * > depends on [AFRAME.utils.require](com/require.js)
 | 
			
		||||
 *
 | 
			
		||||
 * | property         | type      | default                | info |
 | 
			
		||||
 * |------------------|-----------|------------------------|------|
 | 
			
		||||
 * | `title`          |`string`   | ""                     |      |
 | 
			
		||||
 * | `width`          |`string`   |                        |      |
 | 
			
		||||
 * | `height`         |`string`   | 260px                  |      |
 | 
			
		||||
 * | `uid`            |`string`   |                        |      |
 | 
			
		||||
 * | `attach`         |`selector` |                        |      |
 | 
			
		||||
 * | `dom`            |`selector` |                        |      |
 | 
			
		||||
 * | `max`            |`boolean`  | false                  |      |
 | 
			
		||||
 * | `min`            |`boolean`  | false                  |      |
 | 
			
		||||
 * | `x`              |`string`   | "center"               |      |
 | 
			
		||||
 * | `y`              |`string`   | "center"               |      |
 | 
			
		||||
 * | `class`          |`array`    | []                     |      |
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
AFRAME.registerComponent('window', {
 | 
			
		||||
  schema:{
 | 
			
		||||
    title:     {type:'string',"default":"title"},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										268
									
								
								com/xterm.js
									
										
									
									
									
								
							
							
						
						
									
										268
									
								
								com/xterm.js
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,268 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * MIT License
 | 
			
		||||
 * 
 | 
			
		||||
 * Copyright (c) 2019 
 | 
			
		||||
 * 
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 * 
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
 * copies or substantial portions of the Software.
 | 
			
		||||
 * 
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
 * SOFTWARE.
 | 
			
		||||
 *
 | 
			
		||||
 * 2019 Mauve Ranger
 | 
			
		||||
 * 2024 Leon van Kammen
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
let terminalInstance = 0
 | 
			
		||||
 | 
			
		||||
const TERMINAL_THEME = {
 | 
			
		||||
  theme_foreground: {
 | 
			
		||||
    // 'default': '#ffffff'
 | 
			
		||||
  },
 | 
			
		||||
  theme_background: {
 | 
			
		||||
    // 'default': '#000'
 | 
			
		||||
  },
 | 
			
		||||
  theme_cursor: {
 | 
			
		||||
    // 'default': '#ffffff'
 | 
			
		||||
  },
 | 
			
		||||
  theme_selection: {
 | 
			
		||||
    // 'default': 'rgba(255, 255, 255, 0.3)'
 | 
			
		||||
  },
 | 
			
		||||
  theme_black: {
 | 
			
		||||
    // 'default': '#000000'
 | 
			
		||||
  },
 | 
			
		||||
  theme_red: {
 | 
			
		||||
    // 'default': '#e06c75'
 | 
			
		||||
  },
 | 
			
		||||
  theme_brightRed: {
 | 
			
		||||
    // 'default': '#e06c75'
 | 
			
		||||
  },
 | 
			
		||||
  theme_green: {
 | 
			
		||||
    // 'default': '#A4EFA1'
 | 
			
		||||
  },
 | 
			
		||||
  theme_brightGreen: {
 | 
			
		||||
    // 'default': '#A4EFA1'
 | 
			
		||||
  },
 | 
			
		||||
  theme_brightYellow: {
 | 
			
		||||
    // 'default': '#EDDC96'
 | 
			
		||||
  },
 | 
			
		||||
  theme_yellow: {
 | 
			
		||||
    // 'default': '#EDDC96'
 | 
			
		||||
  },
 | 
			
		||||
  theme_magenta: {
 | 
			
		||||
    // 'default': '#e39ef7'
 | 
			
		||||
  },
 | 
			
		||||
  theme_brightMagenta: {
 | 
			
		||||
    // 'default': '#e39ef7'
 | 
			
		||||
  },
 | 
			
		||||
  theme_cyan: {
 | 
			
		||||
    // 'default': '#5fcbd8'
 | 
			
		||||
  },
 | 
			
		||||
  theme_brightBlue: {
 | 
			
		||||
    // 'default': '#5fcbd8'
 | 
			
		||||
  },
 | 
			
		||||
  theme_brightCyan: {
 | 
			
		||||
    // 'default': '#5fcbd8'
 | 
			
		||||
  },
 | 
			
		||||
  theme_blue: {
 | 
			
		||||
    // 'default': '#5fcbd8'
 | 
			
		||||
  },
 | 
			
		||||
  theme_white: {
 | 
			
		||||
    // 'default': '#d0d0d0'
 | 
			
		||||
  },
 | 
			
		||||
  theme_brightBlack: {
 | 
			
		||||
    // 'default': '#808080'
 | 
			
		||||
  },
 | 
			
		||||
  theme_brightWhite: {
 | 
			
		||||
    // 'default': '#ffffff'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AFRAME.registerComponent('xterm', {
 | 
			
		||||
  schema: Object.assign({
 | 
			
		||||
    XRrenderer: { type: 'string', default: 'canvas', },
 | 
			
		||||
    cols: { type: 'number', default: 110, },
 | 
			
		||||
    rows: { type: 'number', default: Math.floor( (window.innerHeight * 0.7 ) * 0.054 ) },
 | 
			
		||||
    canvasLatency:{ type:'number', default: 200 }
 | 
			
		||||
  }, TERMINAL_THEME),
 | 
			
		||||
 | 
			
		||||
  write: function(message) {
 | 
			
		||||
    this.term.write(message)
 | 
			
		||||
  },
 | 
			
		||||
  init: function () {
 | 
			
		||||
    const terminalElement = document.createElement('div')
 | 
			
		||||
    terminalElement.setAttribute('style', `
 | 
			
		||||
      width:  800px; 
 | 
			
		||||
      height: ${Math.floor( 800 * 0.527 )}px;
 | 
			
		||||
      overflow: hidden;
 | 
			
		||||
    `)
 | 
			
		||||
 | 
			
		||||
    this.el.terminalElement = terminalElement
 | 
			
		||||
 | 
			
		||||
    if( this.data.XRrenderer == 'canvas' ){
 | 
			
		||||
      // setup slightly bigger black backdrop (this.el.getObject3D("mesh")) 
 | 
			
		||||
      // and terminal text (this.el.planeText.getObject("mesh"))
 | 
			
		||||
      const w = 2;
 | 
			
		||||
      const h = (this.data.rows*5/this.data.cols)
 | 
			
		||||
      this.el.setAttribute("geometry",`primitive: box; width:${w}; height:${h}; depth: -0.12`)
 | 
			
		||||
      this.el.setAttribute("material","shader:flat; color:black; opacity:0.5; transparent:true; ")
 | 
			
		||||
      this.el.planeText = document.createElement('a-entity')
 | 
			
		||||
      this.el.planeText.setAttribute("geometry",`primitive: plane; width:${w}; height:${h}`)
 | 
			
		||||
      this.el.appendChild(this.el.planeText)
 | 
			
		||||
 | 
			
		||||
      // we switch between dom/canvas rendering because canvas looks pixely in nonimmersive mode
 | 
			
		||||
      this.el.sceneEl.addEventListener('enter-vr', this.enterImmersive.bind(this) )
 | 
			
		||||
      this.el.sceneEl.addEventListener('enter-ar', this.enterImmersive.bind(this) )
 | 
			
		||||
      this.el.sceneEl.addEventListener('exit-vr',  this.exitImmersive.bind(this) )
 | 
			
		||||
      this.el.sceneEl.addEventListener('exit-ar',  this.exitImmersive.bind(this) )
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.tick = AFRAME.utils.throttleLeadingAndTrailing( () => {
 | 
			
		||||
      if( this.el.sceneEl.renderer.xr.isPresenting ){
 | 
			
		||||
        // workaround
 | 
			
		||||
        // xterm relies on window.requestAnimationFrame (which is not called WebXR immersive mode)
 | 
			
		||||
        //this.term._core.viewport._innerRefresh()
 | 
			
		||||
        this.term._core.renderer._renderDebouncer._innerRefresh() 
 | 
			
		||||
      }
 | 
			
		||||
    }, this.data.canvasLatency)
 | 
			
		||||
 | 
			
		||||
    // Build up a theme object
 | 
			
		||||
    const theme = Object.keys(this.data).reduce((theme, key) => {
 | 
			
		||||
      if (!key.startsWith('theme_')) return theme
 | 
			
		||||
      const data = this.data[key]
 | 
			
		||||
      if(!data) return theme
 | 
			
		||||
      theme[key.slice('theme_'.length)] = data
 | 
			
		||||
      return theme
 | 
			
		||||
    }, {})
 | 
			
		||||
 | 
			
		||||
    this.fontSize = 14
 | 
			
		||||
 | 
			
		||||
    const term = this.term = new Terminal({
 | 
			
		||||
      logLevel:"off",
 | 
			
		||||
      theme: theme,
 | 
			
		||||
      allowTransparency: true,
 | 
			
		||||
      cursorBlink: true,
 | 
			
		||||
      disableStdin: false,
 | 
			
		||||
      rows: this.data.rows,
 | 
			
		||||
      cols: this.data.cols,
 | 
			
		||||
      fontFamily: 'Cousine, monospace',
 | 
			
		||||
      fontSize: this.fontSize,
 | 
			
		||||
      lineHeight: 1.15,
 | 
			
		||||
      useFlowControl: true,
 | 
			
		||||
      rendererType: this.renderType // 'dom' // 'canvas' 
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.term.open(terminalElement)
 | 
			
		||||
    this.term.focus()
 | 
			
		||||
    this.setRenderType('dom')
 | 
			
		||||
    
 | 
			
		||||
    terminalElement.querySelector('.xterm-viewport').style.background = 'transparent'
 | 
			
		||||
 | 
			
		||||
    // now we can scale  canvases to the parent element
 | 
			
		||||
    const $screen = terminalElement.querySelector('.xterm-screen') 
 | 
			
		||||
    $screen.style.width = '100%'
 | 
			
		||||
 | 
			
		||||
    term.on('refresh', AFRAME.utils.throttleLeadingAndTrailing( () => this.update(), 150 ) )
 | 
			
		||||
    term.on('data', (data) => {
 | 
			
		||||
      this.el.emit('xterm-input', data)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.el.addEventListener('serial-output-byte', (e) => {
 | 
			
		||||
      const byte = e.detail
 | 
			
		||||
      var chr = String.fromCharCode(byte);
 | 
			
		||||
      this.term.write(chr)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    this.el.addEventListener('serial-output-string', (e) => {
 | 
			
		||||
      this.term.write(e.detail) 
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  update: function(){ 
 | 
			
		||||
    if( this.renderType == 'canvas' ){
 | 
			
		||||
      const material = this.el.planeText.getObject3D('mesh').material
 | 
			
		||||
      if (!material.map ) return 
 | 
			
		||||
      if( this.cursorCanvas ) this.canvasContext.drawImage(this.cursorCanvas, 0,0)
 | 
			
		||||
      else console.log("no cursorCanvas")
 | 
			
		||||
      material.map.needsUpdate = true
 | 
			
		||||
      //material.needsUpdate = true
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  setRenderType: function(type){
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if( type.match(/(dom|canvas)/) ){
 | 
			
		||||
 | 
			
		||||
      if( type == 'dom'){
 | 
			
		||||
        this.el.dom.appendChild(this.el.terminalElement)
 | 
			
		||||
        this.term.setOption('fontSize', this.fontSize )
 | 
			
		||||
        this.term.setOption('rendererType',type )
 | 
			
		||||
        this.renderType = type
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if( type == 'canvas'){
 | 
			
		||||
        this.el.appendChild(this.el.terminalElement)
 | 
			
		||||
        this.term.setOption('fontSize', this.fontSize * 3 )
 | 
			
		||||
        this.term.setOption('rendererType',type )
 | 
			
		||||
        this.renderType = type
 | 
			
		||||
        this.update()
 | 
			
		||||
        setTimeout( () => {
 | 
			
		||||
          this.canvas = this.el.terminalElement.querySelector('.xterm-text-layer')
 | 
			
		||||
          this.canvas.id = "xterm-canvas"
 | 
			
		||||
          this.canvasContext = this.canvas.getContext('2d')
 | 
			
		||||
          this.cursorCanvas = this.el.terminalElement.querySelector('.xterm-cursor-layer')
 | 
			
		||||
          // Create a texture from the canvas
 | 
			
		||||
          const canvasTexture = new THREE.Texture(this.canvas)
 | 
			
		||||
          //canvasTexture.minFilter = THREE.NearestFilter //LinearFilter
 | 
			
		||||
          //canvasTexture.magFilter = THREE.LinearMipMapLinearFilter //THREE.NearestFilter //LinearFilter
 | 
			
		||||
          canvasTexture.needsUpdate = true; // Ensure the texture updates
 | 
			
		||||
          let plane = this.el.planeText.getObject3D("mesh") //this.el.getObject3D('mesh')
 | 
			
		||||
          if( plane.material ) plane.material.dispose() 
 | 
			
		||||
          plane.material = new THREE.MeshBasicMaterial({
 | 
			
		||||
              map: canvasTexture,  // Set the texture from the canvas
 | 
			
		||||
              transparent: false,   // Set transparency
 | 
			
		||||
              //side: THREE.DoubleSide // Set to double-sided rendering
 | 
			
		||||
              //blending: THREE.AdditiveBlending
 | 
			
		||||
          });
 | 
			
		||||
          this.el.object3D.scale.x = 0.2
 | 
			
		||||
          this.el.object3D.scale.y = 0.2 
 | 
			
		||||
          this.el.object3D.scale.z = 0.2 
 | 
			
		||||
        },100)
 | 
			
		||||
      }
 | 
			
		||||
        
 | 
			
		||||
      this.el.terminalElement.style.opacity = type == 'canvas' ? 0 : 1
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  enterImmersive: function(){
 | 
			
		||||
    if( this.mode == 'immersive' ) return
 | 
			
		||||
    this.el.object3D.visible = true
 | 
			
		||||
    this.mode = "immersive"
 | 
			
		||||
    this.setRenderType('canvas')
 | 
			
		||||
    this.term.focus()
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  exitImmersive: function(){
 | 
			
		||||
    if( this.mode == 'nonimmersive' ) return
 | 
			
		||||
    this.el.object3D.visible = false
 | 
			
		||||
    this.mode = "nonimmersive"
 | 
			
		||||
    this.setRenderType('dom')
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue