Konsole updates
This commit is contained in:
parent
ae279b478b
commit
88f2a716b9
@ -1,216 +1,43 @@
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import '../misc/konsole';
|
||||
import {onMounted} from 'vue';
|
||||
|
||||
const hostname = 'virtual';
|
||||
let history = [];
|
||||
let historyIndex = 0;
|
||||
let prompt;
|
||||
let input;
|
||||
let output;
|
||||
|
||||
function focus() {
|
||||
input.focus();
|
||||
}
|
||||
|
||||
function disable() {
|
||||
input.disabled = true;
|
||||
prompt.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
function enable() {
|
||||
input.disabled = false;
|
||||
input.focus();
|
||||
prompt.style.visibility = 'visible';
|
||||
}
|
||||
|
||||
function banner() {
|
||||
stdOut(`Konsole 0.2.0 LTS virtual tty1<br><br>${hostname} login: root<br>password:<br><br>`);
|
||||
}
|
||||
|
||||
function process(command) {
|
||||
(Array.isArray(command) ? command.join(' ') : command).split(';').filter(c => !!c).forEach(c => {
|
||||
const parts = c.split(' ').filter(c => !!c);
|
||||
if(window.cli[parts[0]] == undefined || window.cli[parts[0]].run == undefined) {
|
||||
stdErr(`${parts[0]}: command not found`);
|
||||
} else {
|
||||
try {
|
||||
const out = window.cli[parts[0]].run(parts.slice(1));
|
||||
if(!!out) stdOut(out);
|
||||
} catch(err) {
|
||||
console.error(err)
|
||||
stdErr(`${parts[0]}: exited with a non-zero status`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function stdErr(text) {
|
||||
const p = document.createElement('p');
|
||||
p.classList.add('console-output-line');
|
||||
p.classList.add('console-output-error');
|
||||
p.innerText = text;
|
||||
output.appendChild(p);
|
||||
}
|
||||
|
||||
function stdOut(text, html=true) {
|
||||
const p = document.createElement('p');
|
||||
p.classList.add('console-output-line');
|
||||
p[html ? 'innerHTML' : 'innerText'] = text;
|
||||
output.appendChild(p);
|
||||
}
|
||||
|
||||
function stdIn(event) {
|
||||
if(event.key == "Enter") {
|
||||
disable();
|
||||
let inputValue = input.value;
|
||||
input.value = '';
|
||||
stdOut(`root@localhost:~ # ${inputValue}`, false);
|
||||
if(!!inputValue) {
|
||||
history.push(inputValue);
|
||||
historyIndex = history.length;
|
||||
process(inputValue)
|
||||
}
|
||||
enable();
|
||||
} else if(event.key == 'Up' || event.key == 'ArrowUp') {
|
||||
if(historyIndex > 0) historyIndex--;
|
||||
input.value = historyIndex == history.length ? '' : history[historyIndex];
|
||||
setTimeout(() => {
|
||||
const end = input.value.length;
|
||||
input.setSelectionRange(end, end);
|
||||
input.focus();
|
||||
}, 1)
|
||||
} else if(event.key == 'Down' || event.key == 'ArrowDown') {
|
||||
if(historyIndex < history.length) historyIndex++;
|
||||
input.value = historyIndex == history.length ? '' : history[historyIndex];
|
||||
setTimeout(() => {
|
||||
const end = input.value.length;
|
||||
input.setSelectionRange(end, end);
|
||||
input.focus();
|
||||
}, 1)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
prompt = document.getElementsByClassName('console-input-prompt')[0];
|
||||
input = document.getElementsByClassName('console-input-field')[0];
|
||||
output = document.getElementsByClassName('console-output')[0];
|
||||
banner();
|
||||
(<any>window).cli?.build('#konsole');
|
||||
});
|
||||
|
||||
window.cli = {};
|
||||
window.cli['clear'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Clear console output';
|
||||
},
|
||||
run: args => {
|
||||
output.innerHTML = '';
|
||||
}
|
||||
}
|
||||
window.cli['echo'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Output text to console';
|
||||
},
|
||||
run: args => {
|
||||
return args.join(' ');
|
||||
}
|
||||
}
|
||||
window.cli['exit'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'End session';
|
||||
},
|
||||
run: args => {
|
||||
process('clear');
|
||||
history = [];
|
||||
historyIndex = 0;
|
||||
banner();
|
||||
}
|
||||
}
|
||||
window.cli['help'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Display all commands';
|
||||
},
|
||||
run: args => {
|
||||
return Object.keys(window.cli).map(command => `${command} - ${window.cli[command].help()}`).join('<br>') + '<br><br>';
|
||||
}
|
||||
}
|
||||
window.cli['hostname'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Get computer hostname';
|
||||
},
|
||||
run: args => {
|
||||
return 'localhost'
|
||||
}
|
||||
}
|
||||
window.cli['man'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Command manual';
|
||||
},
|
||||
run: args => {
|
||||
return window.cli[args[0]].help();
|
||||
}
|
||||
}
|
||||
window.cli['whoami'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Get username';
|
||||
},
|
||||
run: args => {
|
||||
return 'root'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.console {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.cli-container {
|
||||
padding: 1rem;
|
||||
background: #333;
|
||||
font-family: monospace !important;
|
||||
overflow-y: auto;
|
||||
min-height: 150px;
|
||||
max-height: 300px;
|
||||
|
||||
.console-output {
|
||||
.cli-stdout {
|
||||
flex-grow: 1;
|
||||
color: #0f0;
|
||||
}
|
||||
|
||||
.console-output-line {
|
||||
.cli-stdout-line {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.console-input {
|
||||
.cli-stdin {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
|
||||
.console-input-prompt {
|
||||
.cli-stdin-prompt {
|
||||
padding-right: 0.55em;
|
||||
text-wrap: nowrap;
|
||||
color: #0f0;
|
||||
}
|
||||
|
||||
.console-input-field {
|
||||
.cli-stdin-input {
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 1rem;
|
||||
@ -255,12 +82,5 @@ window.cli['whoami'] = {
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div class="console">
|
||||
<div class="console-output"></div>
|
||||
<div class="console-input" @click=" focus()">
|
||||
<div class="console-input-prompt">root@{{hostname}}:~ #</div>
|
||||
<label for="console-input-field" class="hidden-label"><!-- Accessibility -->CLI Input</label>
|
||||
<input id="console-input-field" class="console-input-field" type="text" @keydown="stdIn($event)">
|
||||
</div>
|
||||
</div>
|
||||
<div id="konsole"></div>
|
||||
</template>
|
||||
|
@ -1,11 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
export interface Project {
|
||||
icon?: string;
|
||||
name: string;
|
||||
description: string;
|
||||
link: string;
|
||||
source?: string;
|
||||
}
|
||||
import type {Project} from '@/models/project';
|
||||
|
||||
defineProps({
|
||||
projects: {type: Array as () => Project[], required: true}
|
||||
|
@ -1,3 +1,3 @@
|
||||
export const environment = {
|
||||
postMailKey: (<any>window)?.env?.APP_POSTMAIL_KEY || import.meta.env.APP_POSTMAIL_ACCESS_TOKEN,
|
||||
postMailKey: (<any>window)?.env?.APP_POSTMAIL_KEY || (<any>import.meta).env.APP_POSTMAIL_ACCESS_TOKEN,
|
||||
}
|
||||
|
315
src/misc/konsole.js
Normal file
315
src/misc/konsole.js
Normal file
@ -0,0 +1,315 @@
|
||||
window.cli = {
|
||||
// Element references
|
||||
_input: null,
|
||||
_parent: null,
|
||||
_prompt: null,
|
||||
_output: null,
|
||||
|
||||
// CLI State
|
||||
_history: [],
|
||||
_index: 0,
|
||||
pwd: '/',
|
||||
hostname: 'virtual',
|
||||
env: {},
|
||||
exec: {},
|
||||
filesystem: {},
|
||||
user: 'root',
|
||||
version: '0.3.0',
|
||||
|
||||
build: (elementId) => {
|
||||
window.cli._parent = document.querySelector(elementId);
|
||||
if(!window.cli._parent)
|
||||
throw new Error(`Could not create konsole, element "${elementId}" does not exist`);
|
||||
|
||||
window.cli._parent.innerHTML = `
|
||||
<div class="cli-container">
|
||||
<div class="cli-stdout"></div>
|
||||
<div class="cli-stdin" onclick="window.cli._input.focus()">
|
||||
<label for="${elementId}-cli-stdin-input" class="cli-stdin-prompt">${window.cli.prompt()}</label>
|
||||
<input id="${elementId}-cli-stdin-input" class="cli-stdin-input" type="text" />
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
window.cli._input = document.querySelector(elementId + ' .cli-stdin-input');
|
||||
window.cli._prompt = document.querySelector(elementId + ' .cli-stdin-prompt');
|
||||
window.cli._output = document.querySelector(elementId + ' .cli-stdout');
|
||||
window.cli._input.addEventListener('keyup', (e) => {
|
||||
if(e.key == "Enter") {
|
||||
window.cli.disable();
|
||||
window.cli.stdOut(`${window.cli.prompt()} ${window.cli._input.value}`);
|
||||
if(!!window.cli._input.value) {
|
||||
window.cli._history.push(window.cli._input.value);
|
||||
window.cli._index = window.cli._history.length;
|
||||
window.cli.stdIn(window.cli._input.value)
|
||||
}
|
||||
window.cli._input.value = '';
|
||||
window.cli.enable();
|
||||
} else if(e.key == 'Up' || e.key == 'ArrowUp') {
|
||||
if(window.cli._index > 0) window.cli._index--;
|
||||
window.cli._input.value = window.cli._index == window.cli._history.length ? '' : window.cli._history[window.cli._index];
|
||||
setTimeout(() => {
|
||||
const end = window.cli._input.value.length;
|
||||
window.cli._input.setSelectionRange(end, end);
|
||||
window.cli._input.focus();
|
||||
}, 1)
|
||||
} else if(e.key == 'Down' || e.key == 'ArrowDown') {
|
||||
if(window.cli._index < window.cli._history.length) window.cli._index++;
|
||||
window.cli._input.value = window.cli._index == window.cli._history.length ? '' : window.cli._history[window.cli._index];
|
||||
setTimeout(() => {
|
||||
const end = window.cli._input.value.length;
|
||||
window.cli._input.setSelectionRange(end, end);
|
||||
window.cli._input.focus();
|
||||
}, 1)
|
||||
}
|
||||
});
|
||||
setTimeout(() => window.cli.exec['banner'].run(), 1);
|
||||
},
|
||||
disable: () => {
|
||||
window.cli._input.disabled = true;
|
||||
window.cli._prompt.style.visibility = 'hidden';
|
||||
},
|
||||
enable: () => {
|
||||
window.cli._input.disabled = false;
|
||||
window.cli._input.focus();
|
||||
window.cli._prompt.style.visibility = 'visible';
|
||||
},
|
||||
|
||||
path: (path=window.cli.pwd) => {
|
||||
let p = path[0] == '/'? path : (window.cli.pwd + (window.cli.pwd.endsWith('/') ? '' : '/') + path.replace('./', ''))
|
||||
.replaceAll('//', '/');
|
||||
const parts = p.split('/').filter(p => !!p);
|
||||
for(let i = 0; i < parts.length; i++) {
|
||||
if(parts[i] == '..') {
|
||||
i--;
|
||||
parts.splice(i, 2);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return '/' + (parts.length ? parts.join('/') : '');
|
||||
},
|
||||
prompt: () => `${window.cli.user}@${window.cli.hostname}:${window.cli.pwd}${window.cli.user == 'root' ? '#' : '$'}`,
|
||||
fs: (path, set) => {
|
||||
let target = window.cli.filesystem;
|
||||
const parts = window.cli.path(path).split('/').filter(p => !!p);
|
||||
parts.forEach((p, i, arr) => {
|
||||
if(!target[p] && set !== undefined) {
|
||||
if(i + 1 != arr.length) target[p] = {};
|
||||
else target[p] = set == null ? undefined : set;
|
||||
}
|
||||
target = target[p];
|
||||
});
|
||||
return target;
|
||||
},
|
||||
stdErr: (text) => {
|
||||
const p = document.createElement('p');
|
||||
p.classList.add('cli-stdout-line');
|
||||
p.classList.add('cli-stdout-error');
|
||||
p.innerText = text;
|
||||
window.cli._output.appendChild(p);
|
||||
},
|
||||
stdIn:(command) => {
|
||||
(Array.isArray(command) ? command.join(' ') : command).split(';').filter(c => !!c).forEach(c => {
|
||||
const parts = c.match(/(?:[^\s"]+|"[^"]*")+/g);
|
||||
if(!parts) return;
|
||||
|
||||
const exec = window.cli.exec[parts[0]];
|
||||
if(!exec?.run) {
|
||||
window.cli.stdErr(`${parts[0]}: command not found`);
|
||||
} else {
|
||||
try {
|
||||
const args = parts.slice(1).map(a => (a[0] == '"' || a[0] == "'") ? a.slice(1, -1) : a);
|
||||
const out = exec.run(args);
|
||||
if(!!out) window.cli.stdOut(out);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
window.cli.stdErr(err.message || `${parts[0]}: exited with a non-zero status`);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
stdOut: (text, html=false) => {
|
||||
const p = document.createElement('p');
|
||||
p.classList.add('cli-stdout-line');
|
||||
p[html ? 'innerHTML' : 'innerText'] = text;
|
||||
window.cli._output.appendChild(p);
|
||||
},
|
||||
};
|
||||
|
||||
window.cli.exec['banner'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Display login banner';
|
||||
},
|
||||
run: args => {
|
||||
window.cli.stdOut(`Konsole ${window.cli.version} LTS ${window.cli.hostname} tty1\n\n${window.cli.hostname} login: ${window.cli.user}\npassword:\n\n`);
|
||||
}
|
||||
}
|
||||
window.cli.exec['cd'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Clear console output';
|
||||
},
|
||||
run: args => {
|
||||
const path = window.cli.fs(args[0]);
|
||||
if(!path) throw new Error(`cd: \'${args[0]}\': No such file or directory`)
|
||||
|
||||
window.cli.pwd = window.cli.path(args[0]);
|
||||
window.cli._prompt.innerText = window.cli.prompt();
|
||||
}
|
||||
}
|
||||
window.cli.exec['clear'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Clear console output';
|
||||
},
|
||||
run: args => {
|
||||
window.cli._output.innerHTML = '';
|
||||
}
|
||||
}
|
||||
window.cli.exec['echo'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Output text to console';
|
||||
},
|
||||
run: args => {
|
||||
return args.join(' ');
|
||||
}
|
||||
}
|
||||
window.cli.exec['exit'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'End session';
|
||||
},
|
||||
run: args => {
|
||||
window.cli.stdIn('clear');
|
||||
window.cli._history = [];
|
||||
window.cli._index = 0;
|
||||
window.cli.stdIn('banner');
|
||||
}
|
||||
}
|
||||
window.cli.exec['help'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Display all commands';
|
||||
},
|
||||
run: args => {
|
||||
return `Konsole v${window.cli.version} - A prototype bash emulator written by Zakary Timson\n\n` +
|
||||
Object.keys(window.cli.exec).map(command => `${command} - ${window.cli.exec[command].help()}`).join('\n') + '\n\n';
|
||||
}
|
||||
}
|
||||
window.cli.exec['hostname'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Get computer hostname';
|
||||
},
|
||||
run: args => {
|
||||
return window.cli.hostname;
|
||||
}
|
||||
}
|
||||
window.cli.exec['ls'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Display directory contents';
|
||||
},
|
||||
run: args => {
|
||||
const target = window.cli.fs(args[0]);
|
||||
if(!target) throw new Error(`ls: cannot access \'${args[0]}\': No such file or directory`)
|
||||
return Object.keys(target).reduce((acc, p) => acc + `${p}\n`, '');
|
||||
}
|
||||
}
|
||||
window.cli.exec['man'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'View command\'s manual';
|
||||
},
|
||||
run: args => {
|
||||
return window.cli.exec[args[0]].help();
|
||||
}
|
||||
}
|
||||
window.cli.exec['mkdir'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Create new directory';
|
||||
},
|
||||
run: args => {
|
||||
if(!args[0]) throw new Error('mkdir: missing operand');
|
||||
window.cli.fs(args[0], {});
|
||||
}
|
||||
}
|
||||
window.cli.exec['pwd'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Get present working directory';
|
||||
},
|
||||
run: args => {
|
||||
return window.cli.pwd;
|
||||
}
|
||||
}
|
||||
window.cli.exec['rm'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Delete file or directory';
|
||||
},
|
||||
run: args => {
|
||||
if(!args[0]) throw new Error('rm: missing operand');
|
||||
window.cli.fs(args[0], null);
|
||||
}
|
||||
}
|
||||
window.cli.exec['shower-thought'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Random shower thought';
|
||||
},
|
||||
run: args => {
|
||||
const motd = [
|
||||
'Why do kamikaze pilots wear helmets?',
|
||||
'How are giraffes real, but unicorns made up',
|
||||
'When you are a kid you don\'t realize you are also watching your parents grow up',
|
||||
'Some one at Google was like "Yea, just have someone drive down every road on earth!"',
|
||||
'The number of people older than you never goes up',
|
||||
'When you brush your teeth you are cleaning your skeleton',
|
||||
'Pregnancy is like a group project where one person get\'s stuck with all the work',
|
||||
'If the universe wasn\'t infinite it would be even scarier',
|
||||
'Either we are alone in the universe or we are not. both are terrifying',
|
||||
'The object of golf is to play the least amount of golf.'
|
||||
];
|
||||
return motd[~~(Math.random() * motd.length)];
|
||||
}
|
||||
}
|
||||
window.cli.exec['whoami'] = {
|
||||
autocomplete: () => {
|
||||
return [];
|
||||
},
|
||||
help: () => {
|
||||
return 'Get current user account';
|
||||
},
|
||||
run: args => {
|
||||
return window.cli.user;
|
||||
}
|
||||
}
|
7
src/models/project.ts
Normal file
7
src/models/project.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface Project {
|
||||
icon?: string;
|
||||
name: string;
|
||||
description: string;
|
||||
link: string;
|
||||
source?: string;
|
||||
}
|
@ -2,7 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Home from '@/views/Home.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
history: createWebHistory((<any>import.meta).env.BASE_URL),
|
||||
routes: [
|
||||
{path: '/', name: 'home', component: Home}
|
||||
]
|
||||
|
@ -4,15 +4,16 @@ import Contact from '@/components/contact.vue';
|
||||
import Konsole from '@/components/konsole.vue';
|
||||
import Projects from '@/components/projects.vue';
|
||||
import Refrences from '@/components/refrences.vue';
|
||||
import type {Project as P} from '@/models/project';
|
||||
import {ref} from 'vue';
|
||||
|
||||
const services: Projects[] = [
|
||||
const services: P[] = [
|
||||
{name: 'Formula Manager', icon: 'https://git.zakscode.com/avatars/7ec6bfd66b2bf9bad5c43c75a33f9cb3f6609b05c33a31f5d1e524a567cd09c1?size=280', link: 'https://screenprintingsuppliescanada.com/formulation-manager', description: 'A web & computer application used by FH&Sons to record chemical formulas & distribute them to clients'},
|
||||
{name: 'Map Alliance', icon: 'https://maps.zakscode.com/assets/images/logo.png', link: 'https://maps.zakscode.com', description: 'An online GIS tool which enables users to view, edit & share various "marked-up" maps'},
|
||||
{name: 'Phone Reminders', icon: 'https://phone-reminders.com/phone-reminders.png', link: 'https://phone-reminders.com', description: 'Automatically call & send SMS reminders to clients for events using Google Calendar'},
|
||||
];
|
||||
|
||||
const openSource: Projects[] = [
|
||||
const openSource: P[] = [
|
||||
{name: 'ETF Demo', icon: 'https://git.zakscode.com/repo-avatars/0709db0c51d295d2d29b709865bd95f26e351f72a5c993ca63cd9ec4b4a07f43', link: 'https://etf.zakscode.com', source: 'https://git.zakscode.com/ztimson/etf-demo', description: 'Compare CSV files containing "Electronically Traded Funds" data (Check source for CSV files)'},
|
||||
{name: 'Legio 30', icon: 'https://git.zakscode.com/repo-avatars/f66e3d6f5ff4646b45e859f6bf00c0e0de0621d8a45a47481d53d67b67700f2a', link: 'https://legio-30.org', source: 'https://git.zakscode.com/ztimson/legio-30', description: 'Website for a non-profit Roman re-enactment group from Southern Ontario'},
|
||||
{name: 'Pelican Landing', icon: 'https://git.zakscode.com/ztimson/pelican-landing/raw/branch/develop/src/assets/logo.png', link: 'https://pelican-landing.zakscode.com', source: 'https://git.zakscode.com/ztimson/pelican-landing', description: 'Business website for a hunting & fishing lodge on the Lage of Woods in Northern Ontario '},
|
||||
@ -36,7 +37,7 @@ fetch('https://git.zakscode.com/api/v1/repos/search', {
|
||||
<template>
|
||||
<div class="p-3">
|
||||
<!-- Terminal -->
|
||||
<konsole class="mb-5" style="max-height: 300px" />
|
||||
<konsole class="mb-5" />
|
||||
|
||||
<!-- Steps -->
|
||||
<div class="mb-5 pt-5">
|
||||
@ -58,17 +59,8 @@ fetch('https://git.zakscode.com/api/v1/repos/search', {
|
||||
<h3 class="mb-0">About</h3>
|
||||
<hr class="mb-4">
|
||||
<img alt="Childhood" src="/childhood.jpg" height="150px" width="auto" class="float-end m-3 m-md-0 ml-md-3" style="border-radius: 50%;">
|
||||
<p>
|
||||
Zak Timson is a software engineer with over 10 years of professional experience. Zak has had a love for
|
||||
computers since he was born & taught him self to code at the age of 13. Since then, he has gone to school
|
||||
for computer science & has worked for both small businesses and large corporations as a developer and team lead.
|
||||
</p>
|
||||
<p>
|
||||
Zak specializes in full-stack web development & server infrastructure, and primarily works on large enterprise
|
||||
grade "Software as a service" (SaaS) products. As a software architect & team lead he is able to work with
|
||||
business's to create a road map of their needs, build enterprise grade software solutions that meet those
|
||||
needs & work with clients to host & deliver automatic updates at scale using continuous integration.
|
||||
</p>
|
||||
<p>Zak Timson is a software engineer with over 10 years of professional experience. Zak has had a love for computers since he was born & taught him self to code at the age of 13. Since then, he has gone to school for computer science & has worked for both small businesses and large corporations as a developer and team lead.</p>
|
||||
<p>Zak specializes in full-stack web development & server infrastructure, and primarily works on large enterprise grade "Software as a service" (SaaS) products. As a software architect & team lead he is able to work with business's to create a road map of their needs, build enterprise grade software solutions that meet those needs & work with clients to host & deliver automatic updates at scale using continuous integration.</p>
|
||||
|
||||
<div class="mt-4">
|
||||
<h4 class="mb-3 text-muted">CSV & References</h4>
|
||||
|
@ -10,6 +10,7 @@
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": [
|
||||
"node"
|
||||
|
Loading…
Reference in New Issue
Block a user