Move resume to external repo

This commit is contained in:
b1ek 2023-03-19 12:30:29 +10:00
commit 9d414158e7
Signed by: blek
GPG Key ID: 14546221E3595D0C
23 changed files with 816 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
node_modules
yarn.lock
package-lock.json
dist
*.log
*.tmp

37
package.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "resume",
"version": "1.0.0",
"main": "src/resume.js",
"source": "src/index.html",
"author": "blek",
"license": "MIT",
"devDependencies": {
"assert": "^2.0.0",
"events": "^3.1.0",
"parcel": "^2.8.3",
"parcel-namer-without-hash": "^0.0.1",
"path-browserify": "^1.0.0",
"process": "^0.11.10",
"punycode": "^1.4.1",
"querystring-es3": "^0.2.1",
"stream-browserify": "^3.0.0",
"url": "^0.11.0",
"util": "^0.12.3"
},
"dependencies": {
"@parcel/fs": "^2.8.3",
"copy-to-clipboard": "^3.3.3",
"file-saver": "^2.0.5",
"memfs": "^3.4.13",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"unionfs": "^4.4.0",
"xterm": "^5.1.0",
"xterm-for-react": "^1.0.4",
"xterm-js-shell": "^1.1.3"
},
"scripts": {
"start": "parcel",
"build": "parcel build --no-source-maps"
}
}

29
src/Console.js Normal file
View File

@ -0,0 +1,29 @@
import React from 'react';
import { XTerm } from 'xterm-for-react'
export class Console extends React.Component {
constructor(props) {
super(props);
this.terminal = React.createRef(null);
}
render() {
return <div style={{padding: '8px'}}>
<XTerm
ref={this.terminal}
options={{
theme: {
background: '#212121',
brightGreen: '#15a179'
},
convertEol: true
}}
/>
</div>;
}
componentDidMount() {
require('./emulator')(this.terminal.current);
this.terminal.current.terminal.focus();
}
}

View File

@ -0,0 +1,36 @@
import { Terminal } from 'xterm';
const fs = require('../fs');
/**
*
* @param { string[] } argv
* @param { Terminal } terminal
*/
module.exports = (argv, terminal) => {
if (argv.indexOf('--help') != -1) {
terminal.writeln(`Usage: ${argv[0]} [files] [-n]`);
terminal.writeln(' -n --number show lines numbers');
terminal.writeln(' --help show this help');
terminal.writeln('Read files into stdout');
return;
}
const numbers = (argv.indexOf('-n') != -1) || (argv.indexOf('--number') != -1);
let files = argv.filter(x => { return !x.startsWith('-') });
files.shift();
files.forEach(file => {
if (!fs.existsSync(file)) {
terminal.writeln(`${argv[0]}: ${file}: no such file or directory`);
return;
}
const lines = fs.readFileSync(file).toString().split('\n');
if (numbers) {
lines.forEach((line, i) => {
terminal.write('\033[35m' + i + ' |\033[0m ');
})
} else {
terminal.write(lines.join('\n'));
}
})
}

View File

@ -0,0 +1,25 @@
import { Terminal } from 'xterm';
/**
*
* @param {string[]} argv
* @param {Terminal} terminal
*/
module.exports = (argv, terminal) => {
if (argv.indexOf('--help') != -1) {
terminal.writeln(`Usage: ${argv[0]} [--help]`);
terminal.writeln('Lists all available commands.');
return;
}
const cmds = Object.keys(require('../commands'));
let i = 0;
cmds.forEach(x => {
if (x == argv[0]) return;
if (i == 4) {
terminal.writeln('');
i = 0;
} else i++;
terminal.write('\033[1;32m' + x + '\033[0m ');
});
terminal.writeln('');
}

View File

@ -0,0 +1,43 @@
import { Terminal } from 'xterm';
import { saveAs } from 'file-saver';
const fs = require('../fs');
/**
*
* @param { string[] } argv
* @param { Terminal } terminal
*/
module.exports = (argv, terminal) => {
if (argv.indexOf('--help') != -1 || argv.length == 1) {
terminal.write(
`Usage: ${argv[0]} [FILES] [-z]
Export file from filesystem to your device.
-z Compress in zip format (not supported)`);
return;
}
let files = [...argv];
files.shift();
if (argv.indexOf('-z') != -1) {
terminal.write(`${argv[0]}: cannot zip '${files.join(' ').replace('\'', "\\'")}': not supported\n`);
return;
}
try {
var isFileSaverSupported = !!new Blob;
} catch (e) {}
if (!isFileSaverSupported) {
terminal.write(`${argv[0]}: Your browser does not support this feature.`);
return;
}
if (!fs.existsSync(argv[1])) {
terminal.write(`${argv[0]}: cannot open ${argv[1]}: no such file or directory\n`);
return;
}
saveAs(new Blob([fs.readFileSync(argv[1])]), argv[1]);
}

View File

@ -0,0 +1,37 @@
import { Terminal } from 'xterm';
import { saveAs } from 'file-saver';
const fs = require('../fs');
/**
*
* @param { string[] } argv
* @param { Terminal } terminal
*/
module.exports = (argv, terminal) => {
if (argv.indexOf('--help') != -1) {
terminal.write(
`Usage: ${argv[0]} [DESTANATION]\n
Import files from your system to this filesystem.
`
);
return;
}
let el = document.getElementById('upload_file_btn');
if (el == null) {
el = document.createElement('input');
el.style.display = 'none';
el.type = 'file';
el.id = 'upload_file_btn';
el.setAttribute('multiple', 'multiple');
document.body.appendChild(el);
}
const dir = argv[1] || '.';
el.click();
let files = el.files;
global.f = files
}

View File

@ -0,0 +1,18 @@
let cmds = {
'cat': require('./cat'),
'cmds': require('./cmds'),
'cmdls': require('./cmds'),
'help': require('./cmds'),
'ls': require('./ls'),
'skills': require('./skills'),
'mkdir': require('./mkdir'),
'wget': require('./wget'),
'export_file': require('./export_file'),
'import_file': require('./import_file'),
'zsh': require('./zsh'),
// alias l='ls -l'
'l': (a,t) => {require('./ls')([...a, '-l'], t)},
};
module.exports = cmds;

View File

@ -0,0 +1,67 @@
const { Terminal } = require('xterm');
const fs = require('../fs');
/**
*
* @param {string[]} argv
* @param {Terminal} terminal
*/
module.exports = (argv, terminal) => {
if (argv.indexOf('--help') != -1) {
terminal.writeln(`Usage: ${argv[0]} [dirs] [-a|--all] [-l]`);
terminal.writeln('Lists files in directories');
terminal.writeln(' -a --all List all files (including those who start with .)');
terminal.writeln(' -l Use long listing format');
return;
}
const has_arg = (arg) => {return argv.indexOf(arg) != -1};
let directories = [...argv];
const all = (has_arg('-a') || has_arg('--all'));
const long_format = has_arg('-l');
directories.shift();
// remove .* files if -a not specified
if (!all)
directories = directories.filter(x => !x.startsWith('.'));
// remove arguments
directories = directories.filter(x => !x.startsWith('-'));
if (directories.length == 0) directories = ['.'];
// remove dublicates
directories = [...new Set(directories)];
directories.forEach((dir, i) => {
if (!fs.lstatSync(dir).isDirectory()) {
terminal.write(dir);
return;
}
if (directories.length != 1) {
terminal.writeln(dir + ':');
terminal.writeln('');
}
if (!fs.existsSync(dir)) {
terminal.writeln(`${argv[0]}: cannot access '${dir}': No such file or directory`);
return;
}
let files = fs.readdirSync(dir);
files.forEach((file, i) => {
if (!long_format)
terminal.write(file + '\033[0m ');
else
terminal.writeln('drwx-xr-x 1 nobody nobody 4.0K Jan 1 13 01:00 ' + file);
if ((i+1) % 5 == 0)
terminal.writeln('');
});
})
terminal.writeln('');
}

View File

@ -0,0 +1,26 @@
import { Terminal } from 'xterm';
const fs = require('../fs');
/**
* @param {string[]} argv
* @param {Terminal} terminal
*/
module.exports = (argv, terminal) => {
if (argv.indexOf('--help') != -1) {
terminal.write(`
Usage: ${argv[0]} [DIRECTORY] [-p]
Create a directory
-p Create parent directories
`);
return;
}
const parents = argv.indexOf('-p') != -1;
const dir = argv[1];
try {
fs.mkdirSync(dir, {recursive: parents});
} catch (err) {
terminal.write(`${argv[0]}: can't create ${dir}: ${err}`);
}
}

View File

@ -0,0 +1,34 @@
const fs = require('../fs');
module.exports = (argv, terminal) => {
if (argv.indexOf('--help') != -1) {
terminal.writeln(`Usage: ${argv[0]}\nShows my skills info`);
return;
}
if (argv[1] != undefined) {
const file = 'skills/' + argv[1];
if (!fs.existsSync(file))
return;
terminal.write(fs.readFileSync(file));
return;
}
terminal.write(`
My skills
\x1b[1;32m[#####] 100% Web dev (fullstack)\x1b[0m
I like doing backend mostly, but sometimes i do
frontend projects like this one.
Learn more: \x1b[4;32mskills web\x1b[0m
\x1b[1;33m[## ] 40% Native C/C++/Rust\x1b[0m
Not really my zone of work but its really fun to
make something with these in spare time.
Learn more: \x1b[4;32mskills nt\x1b[0m
\x1b[1;38:5:244m[ ] 0% Being cis\x1b[0m
Never liked it
`);
}

View File

@ -0,0 +1,66 @@
import { Terminal } from 'xterm';
const fs = require('../fs');
/**
* @param {string[]} argv
* @param {Terminal} terminal
*/
module.exports = async (argv, terminal) => {
if (argv.indexOf('--help') != -1) {
terminal.write(`
Usage: ${argv[0]} [URL] [-O out.file] [-V] [-Q] [-s]
--help Show this help
-O [FILE] Specify output file
-V Verbose mode
-Q Quiet mode (default)
`);
return;
}
const url = argv[1];
let filepath = url.split('/')[url.split('/').length - 1];
if (argv.indexOf('-O') != -1) {
if (argv.indexOf('-O') + 1 == argv.length) {
terminal.writeln(`${argv[0]}: missing output file`);
return;
}
filepath = argv[argv.indexOf('-O') + 1];
}
function progress(p) {
let total = p.total;
if (total == 0)
total = '?'
// terminal.write(`\rDownloading... ${p.loaded}/${total}`);
}
function write(file) {
if (filepath == '-') {
terminal.write(file);
} else {
fs.writeFileSync(filepath, new Buffer(file));
}
}
let file;
let req = new XMLHttpRequest();
req.open('GET', 'http://cors.blek.codes/' + url, true);
req.responseType = 'arraybuffer';
req.onprogress = progress;
req.onload = (e) => {
if (e.type == 'load') {
write(req.response);
}
};
req.send();
const delay = t => new Promise(resolve => setTimeout(resolve, t));
while(req.readyState == 1) await delay(250);
return;
}

View File

@ -0,0 +1,8 @@
const { Terminal } = require("xterm");
/**
* @param {Terminal} terminal
*/
module.exports = (argv, terminal) => {
window.location.href = 'https://youtu.be/dQw4w9WgXcQ?t=3'
}

22
src/emulator/files.js Normal file
View File

@ -0,0 +1,22 @@
module.exports = {
'/skills/web': `
\x1b[1mMy web experience\x1b[0m
I don't really know anything about web development,
but I have some projects which are presented below:
blek! Site: Second rewrite of my website. https://github.com/b1ek/blekSite
blek! ID: An auth server. https://github.com/b1ek/blekID
blek! Bin: A pastebin alternative. https://git.blek.codes/blek/bin
homepage.js: Third rewrite of my website. https://git.blek.codes/blek/homepage.js
`,
'/skills/nt': `
Me native dev experience
It isn't much but i do have a couple of projects
(most of my native work is toy projects)
CuteSchedule A school project that is
supposed to be a interactive https://github.com/b1ek/CuteSchedule
schedule menu hanging on
a TV
f2bin Convert files into C code https://github.com/b1ek/f2bin`
}

8
src/emulator/fs.js Normal file
View File

@ -0,0 +1,8 @@
const { fs, vol } = require('memfs');
fs.writeFileSync('README.md', 'uwu');
const { ufs } = require('unionfs');
ufs.use(fs).use(vol.fromJSON(require('./files')));
ufs.constants = fs.constants;
module.exports = ufs;

11
src/emulator/index.js Normal file
View File

@ -0,0 +1,11 @@
module.exports = (dom) => {
const terminal = dom.terminal;
terminal.writeln('Welcome to my online resume!')
terminal.writeln('Type \033[1;32mhelp\033[0m for list of commands')
terminal.writeln('Use \033[1;33mAlt+C/V\033[0m to copy or paste');
terminal.writeln('');
require('./zsh')(terminal, dom);
require('./pastebuffer')(terminal, dom);
}

View File

@ -0,0 +1,37 @@
import { Terminal } from "xterm"
import copy from 'copy-to-clipboard';
/** @type { Terminal } */
let terminal;
const zsh = require('./zsh');
/**
*
* @param {{ key: string, domEvent: KeyboardEvent }} e
*/
async function keyHandler(e) {
const dom = e.domEvent;
if (!dom.altKey)
return;
if (dom.key.length != 1)
return;
switch (dom.key.toLowerCase()) {
case 'c':
if (!terminal.hasSelection()) break;
copy(terminal.getSelection())
break;
case 'v':
zsh.pr_char(prompt("Paste your text:"), {key: 'a'})
}
}
module.exports = (t, d) => {
terminal = t;
terminal.onKey(keyHandler);
}

230
src/emulator/zsh.js Normal file
View File

@ -0,0 +1,230 @@
import { Terminal } from 'xterm';
import { XTerm } from 'xterm-for-react';
const fs = require('./fs');
global.fs = fs;
const cmds = require('./commands');
/**
* @type { Terminal }
*/
let terminal;
/**
* @type { XTerm }
*/
let dom;
const prompt = '\033[1;32muser@blek.codes \033[36m~ $ \033[0m';
let cmd = '';
let lastcmd = window.sessionStorage.getItem('last_cmd') || '';
function text_prompt() {
return prompt.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
}
/**
*
* @param { string } char
* @param { KeyboardEvent } dom
*/
function pr_char(char, dom) {
if (dom.key.length != 1) return;
cmd += char;
terminal.write(char);
}
function exec_file(f) {
const exists = fs.existsSync(f);
if (!exists) {
terminal.write('zsh: no such file or directory: ' + f);
return;
}
const executable = fs.accessSync(f, fs.constants.X_OK);
if (!executable) {
terminal.writeln('zsh: permission denied: ' + f);
return;
}
terminal.writeln('This is an online resume. It is not big enough to have a script runtime.\n');
return;
}
async function exec_cmd() {
let c = cmd;
const command = c.split(' ')[0];
reset_cmd(c);
lastcmd = c;
window.sessionStorage.setItem('last_cmd', c);
if (command == '') {
print_prompt();
return;
}
// if path
if (command.match(/^((\.|\.\.)\/|\/).+$/gm)) {
exec_file(command);
terminal.writeln('');
print_prompt();
return;
}
if (cmds[command] != undefined) {
const startY = terminal.buffer.normal.cursorY
await cmds[command](c.split(' '), terminal);
await (new Promise(resolve => setTimeout(resolve, 10)));
if (terminal.buffer.active.cursorX != 0 && startY != terminal.buffer.active.cursorY) {
terminal.write('\033[30;47m%\033[0m\n');
}
print_prompt();
return;
}
terminal.writeln('zsh: command not found: ' + command);
print_prompt();
}
function print_prompt() {
terminal.write(prompt);
}
function reprint_prompt() {
terminal.write('\033[2K\r');
print_prompt();
}
function reset_cmd() {
cmd = '';
terminal.writeln('');
}
function cbackspace() {
let exploded = cmd.substring(0, cmd.length - 2).split(' ');
if (exploded.length == 1) {
reprint_prompt();
cmd = '';
return;
}
exploded.pop();
cmd = exploded.join(' ') + ' ';
reprint_prompt();
terminal.write(cmd);
return;
}
function backspace(isCtrl) {
if (terminal.buffer.active.cursorX <= text_prompt().length) return;
if (isCtrl) {
return cbackspace();
}
terminal.write('\b \b');
cmd = cmd.substring(0, cmd.length - 1);
}
/** @param { KeyboardEvent } dom */
async function control_char(id, dom) {
switch (id) {
// backspace
case 8:
backspace(dom.ctrlKey);
break;
// enter
case 13:
exec_cmd();
break;
case 38:
if (lastcmd == '') break;
cmd = lastcmd;
reprint_prompt();
terminal.write(lastcmd);
break;
// Ctrl+c
case 67:
if (dom.ctrlKey) {
terminal.write('^C');
reset_cmd();
print_prompt();
break;
}
case 86:
if (dom.altKey) break;
case 82:
// Why it checks if letter is 'r':
// For some reason, ctrl+(v|r|d) executed this code.
// This is a simple fix
// (same works for any other code like this in this function)
if (dom.ctrlKey && dom.key.toLowerCase() == 'r') {
if (Math.random() < 0.1) terminal.write('uwu');
window.location.reload();
break;
}
case 68:
if (dom.ctrlKey && dom.key.toLowerCase() == 'd') {
terminal.writeln('');
pr_char = () => {};
exec_cmd = pr_char;
control_char = () => {};
break;
}
default:
const wr = (t) => {
terminal.write(t);
cmd += t;
}
if (dom.ctrlKey && (dom.key.length == 1)) {
wr('^' + dom.key.toUpperCase());
break;
}
wr('<');
if (dom.ctrlKey) wr('C');
if (dom.altKey) wr('A');
if (dom.shiftKey) wr('S');
wr(`${id}>`)
break;
}
}
function key(e) {
/** @type {KeyboardEvent} */
const dom = e.domEvent;
if (dom.key.length == 1 && !(dom.ctrlKey || dom.altKey)) {
pr_char(e.domEvent.key, dom);
} else {
control_char(e.domEvent.keyCode, dom)
}
}
function register(t, d) {
terminal = t;
dom = d;
terminal.onKey(key);
terminal.write(prompt);
}
register.pr_char = pr_char;
module.exports = register;

13
src/index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title>resume.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="rootstyle.css" />
</head>
<body>
<div id="resume_js_app"></div>
<script src="resume.js" type="module"></script>
</body>
</html>

43
src/resume.css Normal file
View File

@ -0,0 +1,43 @@
div#resume_js_app {
background: #212121;
width:1000px;
height:800px;
border: 1px solid #e1e1e1;
border-radius: 6px;
font-family: monospace;
box-shadow: 0 2px 1px #303030A0;
color: #e1e1e1 !important;
padding: 8px 2px;
transition: 150ms ease;
}
div#resume_js_app:hover {
box-shadow: 0 2px 3px #303030;
}
div#resume_js_app p.js_loading_indicator {
padding: 0; margin: 0;
position: relative;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: fit-content;
text-align: center;
}
div#resume_js_app * {
color: #e1e1e1;
font-family: 'Source Code Pro', monospace !important;
}
div#resume_js_app a, div#resume_js_app a:visited {
color: rgb(36, 85, 126) !important;
text-decoration: none;
font-weight: 500;
transition: 150ms ease;
text-shadow: 0 0 0px #24557e30;
}
div#resume_js_app a:hover {
text-shadow: 0 0 4px #24557e30;
}
div#resume_js_app table * {
border:0;
text-align: left;
}

13
src/resume.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<title>resume.js</title>
<meta charset="utf-8">
<link rel="stylesheet" href="/resume.css">
</head>
<body>
<div id="resume_js_app"></div>
<script src="/resume.js" type="module"></script>
</body>
</html>

5
src/resume.js Normal file
View File

@ -0,0 +1,5 @@
import { Console } from './Console';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('resume_js_app'));
root.render(<Console />)

1
src/rootstyle.css Symbolic link
View File

@ -0,0 +1 @@
../../../public/static/ui/resume.css