index.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. const MINIMUM_COLS = 2
  2. const MINIMUM_ROWS = 1
  3. class FitAddon {
  4. constructor() {}
  5. activate(terminal) {
  6. this._terminal = terminal
  7. }
  8. dispose() {}
  9. fit() {
  10. const dims = this.proposeDimensions()
  11. if (!dims || !this._terminal || isNaN(dims.cols) || isNaN(dims.rows)) {
  12. return
  13. }
  14. // TODO: Remove reliance on private API
  15. const core = this._terminal._core
  16. // Force a full render
  17. if (
  18. this._terminal.rows !== dims.rows ||
  19. this._terminal.cols !== dims.cols
  20. ) {
  21. core._renderService.clear()
  22. this._terminal.resize(dims.cols, dims.rows)
  23. }
  24. }
  25. proposeDimensions() {
  26. if (!this._terminal) {
  27. return undefined
  28. }
  29. if (!this._terminal.element || !this._terminal.element.parentElement) {
  30. return undefined
  31. }
  32. // TODO: Remove reliance on private API
  33. const core = this._terminal._core
  34. const dims = core._renderService.dimensions
  35. if (dims.actualCellWidth === 0 || dims.actualCellHeight === 0) {
  36. return undefined
  37. }
  38. const scrollbarWidth =
  39. this._terminal.options.scrollback === 0 ? 0 : core.viewport.scrollBarWidth
  40. const parentElementStyle = window.getComputedStyle(
  41. this._terminal.element.parentElement
  42. )
  43. const parentElementHeight = parseInt(
  44. parentElementStyle.getPropertyValue("height")
  45. )
  46. const parentElementWidth = Math.max(
  47. 0,
  48. parseInt(parentElementStyle.getPropertyValue("width"))
  49. )
  50. const elementStyle = window.getComputedStyle(this._terminal.element)
  51. const elementPadding = {
  52. top: parseInt(elementStyle.getPropertyValue("padding-top")),
  53. bottom: parseInt(elementStyle.getPropertyValue("padding-bottom")),
  54. right: parseInt(elementStyle.getPropertyValue("padding-right")),
  55. left: parseInt(elementStyle.getPropertyValue("padding-left"))
  56. }
  57. const elementPaddingVer = elementPadding.top + elementPadding.bottom
  58. const elementPaddingHor = elementPadding.right + elementPadding.left
  59. const availableHeight = parentElementHeight - elementPaddingVer
  60. const availableWidth =
  61. parentElementWidth - elementPaddingHor - scrollbarWidth
  62. const geometry = {
  63. cols: Math.max(
  64. MINIMUM_COLS,
  65. Math.floor(availableWidth / dims.actualCellWidth)
  66. ),
  67. rows: Math.max(
  68. MINIMUM_ROWS,
  69. Math.floor(availableHeight / dims.actualCellHeight)
  70. )
  71. }
  72. return geometry
  73. }
  74. }
  75. /******************************************************/
  76. const term = new Terminal(
  77. {
  78. cursorBlink: true,
  79. fontSize: 16,
  80. theme: {
  81. background: '#282C34',
  82. foreground: '#ffffff',
  83. cursor: '#ffffff',
  84. cursorAccent: '#282C34',
  85. selection: '#41454E',
  86. },
  87. }
  88. );
  89. var command = "";
  90. var need_more_lines = false;
  91. var stopped = false;
  92. var repl = 0;
  93. var Module = {
  94. 'print': function(text) {
  95. term.write(text + "\r\n");
  96. },
  97. 'printErr': function(text) {
  98. term.write(text + "\r\n");
  99. },
  100. 'onRuntimeInitialized': function(text) {
  101. var vm = Module.ccall('pkpy_new_vm', 'number', ['boolean'], [true]);
  102. repl = Module.ccall('pkpy_new_repl', 'number', ['number'], [vm]);
  103. term.write(need_more_lines ? "... " : ">>> ");
  104. },
  105. 'onAbort': function(text) {
  106. stopped = true;
  107. },
  108. };
  109. function term_init() {
  110. term.open(document.getElementById('terminal'));
  111. const addon = new FitAddon();
  112. term.loadAddon(addon);
  113. addon.fit();
  114. // refit when window is resized
  115. window.addEventListener('resize', () => {
  116. addon.fit();
  117. });
  118. term.onData(e => {
  119. if (stopped) return;
  120. switch (e) {
  121. case '\r': // Enter
  122. term.write("\r\n");
  123. need_more_lines = Module.ccall('pkpy_repl_input', 'bool', ['number', 'string'], [repl, command]);
  124. command = '';
  125. term.write(need_more_lines ? "... " : ">>> ");
  126. break;
  127. case '\u007F': // Backspace (DEL)
  128. var cnt = term._core.buffer.x-4;
  129. if(cnt<=0 || command.length==0) break;
  130. // delete the last unicode char
  131. command = command.replace(/.$/u, "");
  132. // clear the whole line
  133. term.write('\b \b'.repeat(cnt));
  134. // re-write the command
  135. term.write(command);
  136. break;
  137. default: // Print all other characters for demo
  138. if (e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7E) || e >= '\u00a0') {
  139. command += e;
  140. term.write(e);
  141. }
  142. }
  143. });
  144. }