|
50 | 50 | console.log('nodejs-terminal-v2.0.html'); |
51 | 51 | console.log(); |
52 | 52 | console.json=v=>console.log(JSON.stringify(v,null,4)); |
| 53 | + console.hex=v=>console.log(v,'Hex:',[...v].map(c=>c.charCodeAt(0).toString(16).padStart(2,'0')).join(' ')); |
53 | 54 | var version='v2.0'; |
54 | 55 | var df=true; |
55 | 56 |
|
|
410 | 411 | }//snippet-console |
411 | 412 |
|
412 | 413 |
|
413 | | - btn.test = async function(){console.log('test'); |
414 | | - |
415 | | - |
| 414 | + btn.test = async function(){ |
| 415 | + console.log('test'); |
| 416 | + |
| 417 | + cursor.to(5,1); |
| 418 | + cursor.xy(); |
| 419 | + return; |
| 420 | + |
416 | 421 | term.write.brightGreen('hello'); |
417 | 422 | term.write.red('world'); |
418 | 423 | return; |
|
457 | 462 |
|
458 | 463 | term.writeln(''); |
459 | 464 | term.writeln.green('installing https-server ...'); |
460 | | - spinner(); |
| 465 | + var s = spinner(); |
461 | 466 |
|
462 | 467 | var mod = await import('https://code.ext-code.com/nodejs/servers/https-server/create-archive.m.js'); |
463 | 468 |
|
|
495 | 500 | term.writeln.red('an error ocurred exit code : '+code); |
496 | 501 | } |
497 | 502 |
|
498 | | - spinner.stop(); |
| 503 | + s.stop(); |
| 504 | + term.write('ok.'); |
499 | 505 | await prompt(); |
500 | 506 |
|
501 | 507 | }//https-server |
|
730 | 736 |
|
731 | 737 | }; |
732 | 738 |
|
733 | | - var chr = { |
| 739 | + const chr = { |
734 | 740 |
|
735 | 741 | warn : '\u26A0', |
736 | 742 | cross : '\u274C', |
|
743 | 749 |
|
744 | 750 | }; |
745 | 751 |
|
746 | | - var colors = { |
| 752 | + const colors = { |
747 | 753 |
|
748 | 754 | black : '\x1b[30m', |
749 | 755 | red : '\x1b[31m', |
|
762 | 768 | brightMagenta : '\x1b[1;35m', |
763 | 769 | brightCyan : '\x1b[1;36m', |
764 | 770 | brightWhite : '\x1b[1;37m', |
765 | | - // Custom Mahogany (RGB: 150, 40, 20) |
| 771 | + // Custom Mahogany (RGB:150,40,20) |
766 | 772 | mahogany : '\x1b[38;2;150;40;20m', |
767 | 773 |
|
768 | 774 | // Utility |
|
771 | 777 | }; |
772 | 778 |
|
773 | 779 |
|
774 | | - |
775 | | - |
| 780 | + const cursor = { |
| 781 | + |
| 782 | + to : (x,y)=>`\x1b[${y+1};${x+1}H`, |
| 783 | + up : (n=1)=>`\x1b[${n}A`, |
| 784 | + down : (n=1)=>`\x1b[${n}B`, |
| 785 | + home : '\x1b[H', |
| 786 | + save : '\x1b[s', |
| 787 | + restore : '\x1b[u', |
| 788 | + hide : '\x1b[?25l', |
| 789 | + show : '\x1b[?25h', |
| 790 | + xy : ()=>{ |
| 791 | + // Note: These are 0-indexed, so the top-left is 0,0. |
| 792 | + // ANSI sequences are usually 1-indexed (1,1). |
| 793 | + // Current Column |
| 794 | + var x = term.buffer.active.cursorX; |
| 795 | + // Current Row |
| 796 | + var y = term.buffer.active.cursorY; |
| 797 | + console.log(`cursor is at: ${x}, ${y}`); |
| 798 | + return {x,y}; |
| 799 | + |
| 800 | + },//xy |
| 801 | + |
| 802 | + }; |
| 803 | + // \x1b[K clears from cursor to end of line |
| 804 | + // \x1b[J: Clears the screen from the cursor down. |
| 805 | + |
776 | 806 | var files = {}; |
777 | 807 |
|
778 | 808 | /* |
|
851 | 881 | }//resume |
852 | 882 | term.write.xy = (x,y,txt)=>{ |
853 | 883 |
|
854 | | - term.write('\x1b[s'); // Save current spot |
855 | | - term.write(`\x1b[${y};${x}H`); // Jump to top left |
856 | | - term.write(txt); |
857 | | - term.write('\x1b[u'); // Jump back to where we were |
| 884 | + term.write(`${cursor.save}${cursor.to(x,y)}${txt}${cursor.restore}`); |
858 | 885 |
|
859 | 886 | }//xy |
860 | 887 |
|
861 | | - |
862 | | - |
863 | | - |
| 888 | + term.end_of_text = function(y){ |
| 889 | + // xterm.js uses 0-based indexing for the buffer, |
| 890 | + // but 1-based for ANSI |
| 891 | + var line = term.buffer.active.getLine(y-1); |
| 892 | + if(!line){ |
| 893 | + return 1; |
| 894 | + } |
| 895 | + |
| 896 | + var last = 0; |
| 897 | + for(var i=term.cols-1;i>=0;i--){ |
| 898 | + |
| 899 | + var cell = line.getCell(i); |
| 900 | + // Check if the cell is not empty/whitespace |
| 901 | + if(cell&&cell.getChars().trim()!==''){ |
| 902 | + last = i+1; |
| 903 | + break; |
| 904 | + } |
| 905 | + |
| 906 | + }//for |
| 907 | + return last+1; |
| 908 | + |
| 909 | + }//end_of_text |
| 910 | + |
| 911 | + |
| 912 | + |
864 | 913 | for(let key in colors){ |
865 | 914 |
|
866 | | - if (key === 'reset') continue; |
| 915 | + if(key=='reset')continue; |
867 | 916 |
|
868 | 917 | term.write[key] = (...args)=>term.write(colors[key]+args.join(' ')+colors.reset); |
869 | 918 | term.writeln[key] = (...args)=>term.writeln(colors[key]+args.join(' ')+colors.reset); |
|
894 | 943 |
|
895 | 944 | }); |
896 | 945 |
|
897 | | - |
898 | | - |
| 946 | +/* |
| 947 | +[J~/rt4gcbtwdausp1nng6uyw4vpasf83c-wlvi |
| 948 | +[1G[0J❯ [3G |
| 949 | +[?2004h |
| 950 | +*/ |
| 951 | + |
899 | 952 | var fitAddon = new FitAddon(); |
900 | 953 | term.loadAddon(fitAddon); |
901 | 954 | term.open(terminal); |
902 | 955 | fitAddon.fit(); |
903 | 956 |
|
904 | | - term.write('booting container ... ') |
| 957 | + term.write('booting container ... '); |
| 958 | + var s = spinner(); |
905 | 959 | webcontainer = await WebContainer.boot(); |
906 | 960 | mod.stack.complete; |
907 | | - term.writeln.green('ok'); //24 |
908 | | - |
| 961 | + s.stop(); |
| 962 | + term.writeln.green('ok.'); //24 |
909 | 963 |
|
| 964 | + term.write.xy(30,0,'node'); |
| 965 | + var s1 = spinner({x:35,y:0}); |
| 966 | + term.write.xy(45,0,'npm'); |
| 967 | + var s2 = spinner({x:49,y:0}); |
| 968 | + term.writeln('spawn jsh ... '); |
| 969 | + var s3 = spinner({x:14,y:1}); |
| 970 | + |
910 | 971 | var phase = {}; |
911 | 972 | phase.ct = 1; |
912 | 973 | phase.p1 = result=>{ |
913 | 974 |
|
| 975 | + s1.stop(); |
914 | 976 | var str = '??'; |
915 | 977 | if(result.code==0)str = result.output; |
916 | | - var x = end_of_text(1); |
917 | | - x++; |
918 | | - term.write.xy(x,1,', node '+str) |
919 | | - phase.ct = 2 |
920 | | - phase.p2() |
| 978 | + term.write.xy(35,0,str) |
921 | 979 |
|
922 | 980 | }//p1 |
923 | 981 | phase.p2 = result=>{ |
924 | 982 |
|
925 | | - if(result)phase.p2.result = result; |
926 | | - if(phase.ct!=2)return; |
927 | | - if(!phase.p2.result)return; |
| 983 | + s2.stop(); |
928 | 984 | var str = '??'; |
929 | | - if(phase.p2.result.code==0)str = phase.p2.result.output; |
930 | | - var x = end_of_text(1); |
931 | | - x++; |
932 | | - term.write.xy(x,1,', npm '+str); |
933 | | - phase.ct = 3; |
934 | | - phase.p3(); |
| 985 | + if(result.code==0)str = result.output; |
| 986 | + str = str.replace(/\x1b\[1G/g, '').replace(/\x1b\[0K/g, ''); |
| 987 | + //console.hex(str); |
| 988 | + term.write.xy(49,0,str); |
935 | 989 |
|
936 | 990 | }//p2 |
937 | | - phase.p3 = result=>{console.log('phase.p3',!!result); |
938 | | - |
939 | | - if(result)shell = result; |
940 | | - if(phase.ct!=3)return; |
941 | | - if(!shell)return; |
| 991 | + phase.p3 = result=>{ |
| 992 | + console.log('phase.p3',!!result); |
| 993 | + shell = result; |
| 994 | + s3.stop(); |
| 995 | + term.write.xy(14,1,`${colors.green}ok.${colors.reset}`); |
942 | 996 | shell.output.pipeTo(output); |
943 | 997 | input = shell.input.getWriter(); |
944 | 998 | term.onData(data=>input.write(data)); |
945 | 999 | //term.resize(); |
946 | | - term.focus(); |
| 1000 | + setTimeout(()=>term.focus(),50); |
947 | 1001 |
|
948 | 1002 | }//p3 |
949 | 1003 |
|
950 | 1004 |
|
951 | 1005 | spawn('node -v',true).then(phase.p1); |
952 | 1006 | spawn('npm -v',true).then(phase.p2); |
953 | | - webcontainer.spawn('jsh').then(phase.p3); |
| 1007 | + setTimeout(()=>webcontainer.spawn('jsh').then(phase.p3),50); |
954 | 1008 |
|
955 | 1009 |
|
956 | 1010 |
|
|
964 | 1018 |
|
965 | 1019 |
|
966 | 1020 |
|
967 | | - //term.write('spawn jsh ... ') |
968 | | - //shell = await webcontainer.spawn('jsh'); |
969 | | - //term.writeln.green('ok'); |
970 | 1021 | output = new WritableStream({ |
971 | 1022 | write(data){ |
972 | | - //console.log(data); |
| 1023 | + console.log(data); |
973 | 1024 | var str = data; |
974 | 1025 | str = str.replaceAll('\r',''); |
975 | 1026 | var lines = str.split('\n'); |
|
1107 | 1158 |
|
1108 | 1159 |
|
1109 | 1160 | async function prompt(){ |
1110 | | - |
| 1161 | + console.log('prompt'); |
1111 | 1162 | //await input.write('\u0003'); // Sends Ctrl+C to clear any junk |
1112 | 1163 | await input.write('\n'); // Sends Enter to get a clean prompt |
1113 | 1164 |
|
1114 | 1165 | }//prompt |
1115 | 1166 |
|
1116 | 1167 |
|
1117 | | - spinner.timer = null; |
1118 | | - |
1119 | | - function spinner(msg='Processing'){ |
| 1168 | + function spinner({x,y}={}){ |
1120 | 1169 |
|
1121 | | - const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; |
1122 | | - let i = 0; |
| 1170 | + var spinner = {}; |
| 1171 | + spinner.timer = null; |
| 1172 | + spinner.stop = null; |
| 1173 | + spinner.x = x; |
| 1174 | + spinner.y = y; |
| 1175 | + |
| 1176 | + var frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; |
| 1177 | + var i = 0; |
| 1178 | + // Hide the cursor so it doesn't flicker |
| 1179 | + //term.write(cursor.hide); |
| 1180 | + setTimeout(setup,50); |
| 1181 | + //setup(); |
| 1182 | + |
| 1183 | + return spinner; |
1123 | 1184 |
|
1124 | | - // Hide the cursor so it doesn't flicker |
1125 | | - term.write('\x1b[?25l'); |
1126 | 1185 |
|
1127 | | - spinner.timer = setInterval(() => { |
| 1186 | + function setup(){ |
| 1187 | + |
| 1188 | + if(x===undefined){ |
| 1189 | + ({x,y} = cursor.xy()); |
| 1190 | + spinner.x = x; |
| 1191 | + spinner.y = y; |
| 1192 | + } |
| 1193 | + |
| 1194 | + |
| 1195 | + spinner.timer = setInterval(()=>{ |
1128 | 1196 | // \r moves to start of line, colors.cyan makes it pop |
1129 | | - term.write(`\r${colors.cyan}${frames[i]}${colors.reset} ${msg}...`); |
1130 | | - i = (i + 1) % frames.length; |
1131 | | - },80); |
| 1197 | + term.write(`${cursor.save}${cursor.to(x,y)}${colors.cyan}${frames[i]}${colors.reset}${cursor.restore}`); |
| 1198 | + i = (i+1)%frames.length; |
| 1199 | + |
| 1200 | + },80); |
| 1201 | + |
| 1202 | + |
| 1203 | + spinner.stop = function(){ |
| 1204 | + |
| 1205 | + if(!spinner.timer){ |
| 1206 | + return; |
| 1207 | + } |
| 1208 | + |
| 1209 | + clearInterval(spinner.timer); |
| 1210 | + spinner.timer = null; |
| 1211 | + term.write(`${cursor.save}${cursor.to(x,y)} ${cursor.restore}`); |
| 1212 | + // Show cursor |
| 1213 | + //term.write(cursor.show); |
| 1214 | + |
| 1215 | + }//stop |
| 1216 | + |
| 1217 | + }//setup |
1132 | 1218 |
|
1133 | 1219 | }//spinner |
1134 | 1220 |
|
1135 | 1221 |
|
1136 | | - spinner.stop = function(finalMsg='done!'){ |
1137 | 1222 |
|
1138 | | - if(spinner.timer){ |
1139 | | - clearInterval(spinner.timer); |
1140 | | - spinner.timer = null; |
1141 | | - } |
1142 | | - // Clear the line, show cursor, and print final message |
1143 | | - // \x1b[K clears from cursor to end of line |
1144 | | - term.write(`\r\x1b[K${colors.green}✔${colors.reset} ${finalMsg}\r`); |
1145 | | - // Show cursor |
1146 | | - term.write('\x1b[?25h'); |
1147 | | - |
1148 | | - }//stop |
1149 | 1223 |
|
1150 | 1224 |
|
1151 | | - function end_of_text(y){ |
1152 | | - // xterm.js uses 0-based indexing for the buffer, |
1153 | | - // but 1-based for ANSI |
1154 | | - var line = term.buffer.active.getLine(y-1); |
1155 | | - if(!line){ |
1156 | | - return 1; |
1157 | | - } |
1158 | | - |
1159 | | - var last = 0; |
1160 | | - for(var i=term.cols-1;i>=0;i--){ |
1161 | | - |
1162 | | - var cell = line.getCell(i); |
1163 | | - // Check if the cell is not empty/whitespace |
1164 | | - if(cell&&cell.getChars().trim()!==''){ |
1165 | | - last = i+1; |
1166 | | - break; |
1167 | | - } |
1168 | | - |
1169 | | - }//for |
1170 | | - return last+1; |
1171 | | - |
1172 | | - }//end_of_text |
1173 | | - |
1174 | 1225 |
|
1175 | 1226 | //: |
1176 | 1227 |
|
|
0 commit comments