mirror of
				https://github.com/pocketpy/pocketpy
				synced 2025-10-26 22:40:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			164 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const MINIMUM_COLS = 2
 | |
| const MINIMUM_ROWS = 1
 | |
| 
 | |
| class FitAddon {
 | |
|   constructor() {}
 | |
| 
 | |
|   activate(terminal) {
 | |
|     this._terminal = terminal
 | |
|   }
 | |
| 
 | |
|   dispose() {}
 | |
| 
 | |
|   fit() {
 | |
|     const dims = this.proposeDimensions()
 | |
|     if (!dims || !this._terminal || isNaN(dims.cols) || isNaN(dims.rows)) {
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     // TODO: Remove reliance on private API
 | |
|     const core = this._terminal._core
 | |
| 
 | |
|     // Force a full render
 | |
|     if (
 | |
|       this._terminal.rows !== dims.rows ||
 | |
|       this._terminal.cols !== dims.cols
 | |
|     ) {
 | |
|       core._renderService.clear()
 | |
|       this._terminal.resize(dims.cols, dims.rows)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   proposeDimensions() {
 | |
|     if (!this._terminal) {
 | |
|       return undefined
 | |
|     }
 | |
| 
 | |
|     if (!this._terminal.element || !this._terminal.element.parentElement) {
 | |
|       return undefined
 | |
|     }
 | |
| 
 | |
|     // TODO: Remove reliance on private API
 | |
|     const core = this._terminal._core
 | |
|     const dims = core._renderService.dimensions
 | |
| 
 | |
|     if (dims.actualCellWidth === 0 || dims.actualCellHeight === 0) {
 | |
|       return undefined
 | |
|     }
 | |
| 
 | |
|     const scrollbarWidth =
 | |
|       this._terminal.options.scrollback === 0 ? 0 : core.viewport.scrollBarWidth
 | |
| 
 | |
|     const parentElementStyle = window.getComputedStyle(
 | |
|       this._terminal.element.parentElement
 | |
|     )
 | |
|     const parentElementHeight = parseInt(
 | |
|       parentElementStyle.getPropertyValue("height")
 | |
|     )
 | |
|     const parentElementWidth = Math.max(
 | |
|       0,
 | |
|       parseInt(parentElementStyle.getPropertyValue("width"))
 | |
|     )
 | |
|     const elementStyle = window.getComputedStyle(this._terminal.element)
 | |
|     const elementPadding = {
 | |
|       top: parseInt(elementStyle.getPropertyValue("padding-top")),
 | |
|       bottom: parseInt(elementStyle.getPropertyValue("padding-bottom")),
 | |
|       right: parseInt(elementStyle.getPropertyValue("padding-right")),
 | |
|       left: parseInt(elementStyle.getPropertyValue("padding-left"))
 | |
|     }
 | |
|     const elementPaddingVer = elementPadding.top + elementPadding.bottom
 | |
|     const elementPaddingHor = elementPadding.right + elementPadding.left
 | |
|     const availableHeight = parentElementHeight - elementPaddingVer
 | |
|     const availableWidth =
 | |
|       parentElementWidth - elementPaddingHor - scrollbarWidth
 | |
|     const geometry = {
 | |
|       cols: Math.max(
 | |
|         MINIMUM_COLS,
 | |
|         Math.floor(availableWidth / dims.actualCellWidth)
 | |
|       ),
 | |
|       rows: Math.max(
 | |
|         MINIMUM_ROWS,
 | |
|         Math.floor(availableHeight / dims.actualCellHeight)
 | |
|       )
 | |
|     }
 | |
|     return geometry
 | |
|   }
 | |
| }
 | |
| 
 | |
| /******************************************************/
 | |
| 
 | |
| const term = new Terminal(
 | |
|   {
 | |
|     cursorBlink: true,
 | |
|     fontSize: 16,
 | |
|     theme: {
 | |
|       background: '#282C34',
 | |
|       foreground: '#ffffff',
 | |
|       cursor: '#ffffff',
 | |
|       cursorAccent: '#282C34',
 | |
|       selection: '#41454E',
 | |
|     },
 | |
|   }
 | |
| );
 | |
| 
 | |
| var command = "";
 | |
| var need_more_lines = false;
 | |
| var stopped = false;
 | |
| var repl = 0;
 | |
| 
 | |
| var Module = {
 | |
|     'print': function(text) { 
 | |
|       term.write(text + "\r\n");
 | |
|     },
 | |
|     'printErr': function(text) {
 | |
|       term.write(text + "\r\n");
 | |
|     },
 | |
|     'onRuntimeInitialized': function(text) {
 | |
|       var vm = Module.ccall('pkpy_new_vm', 'number', ['boolean'], [true]);
 | |
|       repl = Module.ccall('pkpy_new_repl', 'number', ['number'], [vm]);
 | |
|       term.write(need_more_lines ? "... " : ">>> ");
 | |
|     },
 | |
|     'onAbort': function(text) { 
 | |
|       stopped = true;
 | |
|     },
 | |
|   };
 | |
| 
 | |
| function term_init() {
 | |
|     term.open(document.getElementById('terminal'));
 | |
|     const addon = new FitAddon();
 | |
|     term.loadAddon(addon);
 | |
|     addon.fit();
 | |
| 
 | |
|     // refit when window is resized
 | |
|     window.addEventListener('resize', () => {
 | |
|       addon.fit();
 | |
|     });
 | |
| 
 | |
|     term.onData(e => {
 | |
|       if (stopped) return;
 | |
|       switch (e) {
 | |
|         case '\r': // Enter
 | |
|           term.write("\r\n");
 | |
|           need_more_lines = Module.ccall('pkpy_repl_input', 'bool', ['number', 'string'], [repl, command]);
 | |
|           command = '';
 | |
|           term.write(need_more_lines ? "... " : ">>> ");
 | |
|           break;
 | |
|         case '\u007F': // Backspace (DEL)
 | |
|           var cnt = term._core.buffer.x-4;
 | |
|           if(cnt<=0 || command.length==0) break;
 | |
|           // delete the last unicode char
 | |
|           command = command.replace(/.$/u, "");
 | |
|           // clear the whole line
 | |
|           term.write('\b \b'.repeat(cnt));
 | |
|           // re-write the command
 | |
|           term.write(command);
 | |
|           break;
 | |
|         default: // Print all other characters for demo
 | |
|           if (e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7E) || e >= '\u00a0') {
 | |
|             command += e;
 | |
|             term.write(e);
 | |
|           }
 | |
|       }
 | |
|     });
 | |
| }
 |