commit
bc3671ff60
@ -0,0 +1,16 @@ |
|||||||
|
FROM debian:bullseye-slim as builder |
||||||
|
WORKDIR /data |
||||||
|
COPY . . |
||||||
|
RUN apt update && apt install -y npm |
||||||
|
RUN npm install -i package.json \ |
||||||
|
&& npm run build |
||||||
|
|
||||||
|
FROM alpine |
||||||
|
|
||||||
|
RUN apk update \ |
||||||
|
&& apk add lighttpd \ |
||||||
|
&& rm -rf /var/cache/apk/* |
||||||
|
|
||||||
|
COPY --from=builder /data/dist /var/www/localhost/htdocs |
||||||
|
|
||||||
|
CMD ["lighttpd","-D","-f","/etc/lighttpd/lighttpd.conf"] |
@ -0,0 +1,21 @@ |
|||||||
|
MIT License |
||||||
|
|
||||||
|
Copyright (c) 2021 Antoine DAUTRY |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,134 @@ |
|||||||
|
# Resume Terminal |
||||||
|
|
||||||
|
This project is forked from https://github.com/antoine1003/resume-terminal |
||||||
|
|
||||||
|
## About |
||||||
|
|
||||||
|
This projet use [ParcelJS](https://parceljs.org/) as build tool. It is made from scratch, the only library used is for an hidden command `paf` [canvas-confetti](https://github.com/catdad/canvas-confetti). |
||||||
|
|
||||||
|
## get started |
||||||
|
|
||||||
|
### installation |
||||||
|
|
||||||
|
- Install npm: |
||||||
|
```bash |
||||||
|
sudo apt install npm |
||||||
|
``` |
||||||
|
|
||||||
|
- Install dependencies (from project root folder): |
||||||
|
```bash |
||||||
|
npm install |
||||||
|
``` |
||||||
|
|
||||||
|
### usage |
||||||
|
|
||||||
|
- To run in dev mode: ` |
||||||
|
```bash |
||||||
|
npm run dev |
||||||
|
``` |
||||||
|
|
||||||
|
- To build for production: |
||||||
|
```bash |
||||||
|
npm run build |
||||||
|
``` |
||||||
|
|
||||||
|
## config |
||||||
|
|
||||||
|
### commands.json |
||||||
|
|
||||||
|
File `commands.json` contain all commands that just needs to display simple data and doesn't need a JS actions. |
||||||
|
|
||||||
|
For now, there are 4 possible type of steps : |
||||||
|
- list |
||||||
|
- text |
||||||
|
- code |
||||||
|
- table |
||||||
|
|
||||||
|
#### responseType = list |
||||||
|
|
||||||
|
To display a bullet list, the `value` field is an array of string. |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"command": "whois adautry", |
||||||
|
"responseType": "list", |
||||||
|
"value": [ |
||||||
|
"A 27 years old full stack developper", |
||||||
|
"3 years of experiences", |
||||||
|
"Living in Nantes" |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
#### responseType = table |
||||||
|
|
||||||
|
Display a table, this object requires two fields : |
||||||
|
|
||||||
|
- `headers`: Headers of the array |
||||||
|
- `rows`: Array containing rows |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"command": "whereis experiences", |
||||||
|
"responseType": "table", |
||||||
|
"headers": [ |
||||||
|
"Date", |
||||||
|
"Client", |
||||||
|
"Description", |
||||||
|
"Tech" |
||||||
|
], |
||||||
|
"rows": [ |
||||||
|
[ |
||||||
|
"2021", |
||||||
|
"La Poste", |
||||||
|
"Internal tool to schedule techniciens on interventions.", |
||||||
|
"Angular 11, Spring Boot/Batch, Genetic algorithm" |
||||||
|
], |
||||||
|
[ |
||||||
|
"2020", |
||||||
|
"DSI", |
||||||
|
"Maintenance of a timesheet internal tool. Development of plugins for our ProjeQtor instance.", |
||||||
|
"Symfony, Angular 8" |
||||||
|
] |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
#### responseType = text |
||||||
|
|
||||||
|
Just display text contained in `value`. |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"command": "find . -type f -print | xargs grep \"hobby\"", |
||||||
|
"responseType": "text", |
||||||
|
"value": "Bonsoir" |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
#### responseType = code |
||||||
|
|
||||||
|
Display code between `pre` tag, `value` is an array of string, each string is a line. |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"command": "curl https://adautry.fr/user/03101994", |
||||||
|
"responseType": "code", |
||||||
|
"value": [ |
||||||
|
"{", |
||||||
|
" \"name\":\"Antoine DAUTRY\",", |
||||||
|
" \"job\":\"Fullstack developper\",", |
||||||
|
" \"experience\":\"3 years\",", |
||||||
|
" \"city\":\"Nantes\"", |
||||||
|
"}" |
||||||
|
] |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
## Customs commands |
||||||
|
|
||||||
|
In the `app.js` file you can see multiple arrays that stores commands : |
||||||
|
|
||||||
|
- `hiddenCommands`: Commands that are not use in autocompletion (easter egg commands for example) |
||||||
|
- `customCommands`: Commands that needs a specials JS treatments, in my case `dark`/`light` to swith app theme, `get cv` to download my resume, ... |
||||||
|
- `commandsList`: This is the main array used for autocompletion, it stores `customCommands` **and** commands that are listed in the `commands.json` file. |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,34 @@ |
|||||||
|
{ |
||||||
|
"name": "cv-terminal", |
||||||
|
"version": "2.1.2", |
||||||
|
"description": "Nice looking resume.", |
||||||
|
"scripts": { |
||||||
|
"dev": "npx parcel src/index.html", |
||||||
|
"build": "npx parcel build src/index.html" |
||||||
|
}, |
||||||
|
"keywords": [], |
||||||
|
"author": "", |
||||||
|
"license": "ISC", |
||||||
|
"dependencies": { |
||||||
|
"@parcel/config-default": "^2.3.2", |
||||||
|
"canvas-confetti": "^1.5.1", |
||||||
|
"fireworks-js": "^1.3.5", |
||||||
|
"postcss": "^8.3.11" |
||||||
|
}, |
||||||
|
"browserslist": [ |
||||||
|
"defaults" |
||||||
|
], |
||||||
|
"devDependencies": { |
||||||
|
"@parcel/packager-raw-url": "^2.3.2", |
||||||
|
"@parcel/transformer-sass": "^2.3.2", |
||||||
|
"@parcel/transformer-webmanifest": "^2.3.2", |
||||||
|
"cssnano": "^5.0.8", |
||||||
|
"parcel": "^2.3.2", |
||||||
|
"parcel-reporter-static-files-copy": "^1.3.4", |
||||||
|
"prettier": "2.4.1", |
||||||
|
"sass": "^1.43.4" |
||||||
|
}, |
||||||
|
"staticFiles": { |
||||||
|
"staticOutPath": "resources" |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 33 KiB |
@ -0,0 +1,248 @@ |
|||||||
|
/** |
||||||
|
* @typedef Command |
||||||
|
* @property {string} command |
||||||
|
* @property {string} responseType |
||||||
|
* @property {string?} value |
||||||
|
* @property {string[]?} headers |
||||||
|
* @property {string[]?} rows |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @type {Command[]} commands |
||||||
|
*/ |
||||||
|
import commands from "./resources/commands.json"; |
||||||
|
import { getCV, pif, rmRf, setDarkMode } from "./custom-comands"; |
||||||
|
import { dragElement } from "./draggable"; |
||||||
|
|
||||||
|
// Tableau contenant les commandes (utile pour la complétion des commandes)
|
||||||
|
let commandsList = []; |
||||||
|
commands.forEach((c) => { |
||||||
|
commandsList.push(c.command); |
||||||
|
}); |
||||||
|
|
||||||
|
// Commandes qui nécessitent un traitement JS
|
||||||
|
const customCommands = ["clear", "dark", "light", "get cv"]; |
||||||
|
commandsList = commandsList.concat(customCommands); |
||||||
|
|
||||||
|
// Commandes 'easter eggs' non disponibles à l'autocomplétion
|
||||||
|
const hiddenCommands = ["pif", "rm -rf /"]; |
||||||
|
|
||||||
|
// Ajout de la possibilité de déplacer la fenêtre pour les PC
|
||||||
|
if (window.innerWidth > 1024) { |
||||||
|
dragElement(document.querySelector(".terminal")); |
||||||
|
} |
||||||
|
|
||||||
|
// Tableau contenant l'historique des commandes
|
||||||
|
const commandsHistory = []; |
||||||
|
let historyMode = false; |
||||||
|
let historyIndex = -1; |
||||||
|
const terminalBody = document.querySelector(".terminal__body"); |
||||||
|
|
||||||
|
// Ajout de la ligne par défaut
|
||||||
|
addNewLine(); |
||||||
|
|
||||||
|
// Easter egg de décembre, ajout de flocons de neige
|
||||||
|
const now = new Date(); |
||||||
|
if (now.getMonth() === 11) { |
||||||
|
let htmlFlakes = ""; |
||||||
|
for (let i = 0; i < 6; i++) { |
||||||
|
htmlFlakes += `<div class="snowflake">❅</div><div class="snowflake">❆</div>`; |
||||||
|
} |
||||||
|
const html = `<div class="snowflakes" aria-hidden="true">${htmlFlakes}</div>`; |
||||||
|
document.body.append(stringToDom(html)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Retourne le HTML de la réponse pour une commande donnée |
||||||
|
* @param {string} command |
||||||
|
*/ |
||||||
|
function getDomForCommand(command) { |
||||||
|
const commandObj = commands.find((el) => el.command === command); |
||||||
|
let html = ""; |
||||||
|
if (commandObj === undefined) { |
||||||
|
html = `'${ |
||||||
|
command.split(" ")[0] |
||||||
|
}' n’est pas reconnu en tant que commande interne ou externe, un programme exécutable ou un fichier de commandes. Tapez la commande <code>help</code> pour afficher la liste des commandes disponibles.`; |
||||||
|
} else { |
||||||
|
if (commandObj.responseType === "list" && Array.isArray(commandObj.value)) { |
||||||
|
html = "<ul>"; |
||||||
|
html += commandObj.value.map((s) => `<li>${s}</li>`).join(""); |
||||||
|
html += "</ul>"; |
||||||
|
} else if (commandObj.responseType === "text") { |
||||||
|
html = commandObj.value; |
||||||
|
} else if (commandObj.responseType === "table") { |
||||||
|
const headers = commandObj.headers; |
||||||
|
const rows = commandObj.rows; |
||||||
|
const thsHtml = headers.map((h) => `<th>${h}</th>`).join(""); |
||||||
|
const tdsHtml = rows |
||||||
|
.map((r) => `<tr>${r.map((rtd) => `<td>${rtd}</td>`).join("")}</tr>`) |
||||||
|
.join(""); |
||||||
|
html = `<table><thead><tr>${thsHtml}</tr></thead><tbody>${tdsHtml}</tbody></table>`; |
||||||
|
} else if (commandObj.responseType === "code") { |
||||||
|
html = `<pre>${commandObj.value.join("\n")}</pre>`; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return html; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Ajoute une nouvelle ligne input de commande et désactive la précédente. |
||||||
|
* @param {string|null} previousUid uid de la ligne précédente. |
||||||
|
*/ |
||||||
|
function addNewLine(previousUid = null) { |
||||||
|
const uid = Math.random().toString(36).replace("0.", ""); |
||||||
|
// terminal__line
|
||||||
|
const terminalLineEl = document.createElement("div"); |
||||||
|
terminalLineEl.classList.add("terminal__line"); |
||||||
|
|
||||||
|
// terminal__response
|
||||||
|
const terminalResponseEl = document.createElement("div"); |
||||||
|
terminalResponseEl.classList.add("terminal__response"); |
||||||
|
terminalResponseEl.id = `response-${uid}`; |
||||||
|
|
||||||
|
// input text
|
||||||
|
const inputEl = document.createElement("input"); |
||||||
|
inputEl.type = "text"; |
||||||
|
inputEl.id = `input-${uid}`; |
||||||
|
inputEl.autocapitalize = "off"; |
||||||
|
inputEl.dataset.uid = uid; |
||||||
|
inputEl.dataset.active = "1"; // Utile pour le focus
|
||||||
|
inputEl.addEventListener("keydown", onCommandInput); |
||||||
|
|
||||||
|
terminalLineEl.appendChild(inputEl); |
||||||
|
if (previousUid) { |
||||||
|
const previousInputEl = document.getElementById(previousUid); |
||||||
|
if (previousInputEl) { |
||||||
|
previousInputEl.setAttribute("disabled", "true"); |
||||||
|
previousInputEl.removeEventListener("keydown", onCommandInput); |
||||||
|
delete previousInputEl.dataset.active; |
||||||
|
} |
||||||
|
} |
||||||
|
document.getElementById("terminal").appendChild(terminalLineEl); |
||||||
|
document.getElementById("terminal").appendChild(terminalResponseEl); |
||||||
|
|
||||||
|
inputEl.focus(); // Ajoute le focus dès la création du champs
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gère le keydown sur l'input de la commande. |
||||||
|
* @param e |
||||||
|
*/ |
||||||
|
function onCommandInput(e) { |
||||||
|
const commandValue = e.target.value.trim().toLowerCase(); |
||||||
|
if (e.keyCode === 13) { |
||||||
|
// ENTER
|
||||||
|
if (commandValue !== "") { |
||||||
|
historyMode = false; |
||||||
|
const idResponse = `response-${e.target.dataset.uid}`; |
||||||
|
const responseEl = document.getElementById(idResponse); |
||||||
|
let html; |
||||||
|
if ( |
||||||
|
hiddenCommands.includes(commandValue) || |
||||||
|
customCommands.includes(commandValue) |
||||||
|
) { |
||||||
|
html = handleCustomCommands(commandValue); |
||||||
|
} else { |
||||||
|
html = getDomForCommand(commandValue); |
||||||
|
} |
||||||
|
if (responseEl) { |
||||||
|
responseEl.innerHTML = html; |
||||||
|
commandsHistory.push(commandValue); |
||||||
|
addNewLine(e.target.id); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (e.keyCode === 9) { |
||||||
|
// TAB
|
||||||
|
e.preventDefault(); |
||||||
|
if (commandValue === "") { |
||||||
|
this.value = "help"; |
||||||
|
} else { |
||||||
|
const matchingCommand = commandsList.find((c) => |
||||||
|
c.startsWith(commandValue) |
||||||
|
); |
||||||
|
if (matchingCommand) { |
||||||
|
this.value = matchingCommand; |
||||||
|
} |
||||||
|
} |
||||||
|
historyMode = false; |
||||||
|
} else if (e.keyCode === 38 || e.keyCode === 40) { |
||||||
|
// UP / DOWN
|
||||||
|
// Gestion de l'historique
|
||||||
|
if (commandsHistory.length > 0) { |
||||||
|
if (historyMode === false) { |
||||||
|
historyIndex = commandsHistory.length - 1; |
||||||
|
} else { |
||||||
|
if (e.keyCode === 38 && historyIndex !== 0) { |
||||||
|
// UP
|
||||||
|
historyIndex--; |
||||||
|
} else if ( |
||||||
|
e.keyCode === 40 && |
||||||
|
historyIndex !== commandsHistory.length - 1 |
||||||
|
) { |
||||||
|
historyIndex++; |
||||||
|
} |
||||||
|
} |
||||||
|
this.value = commandsHistory[historyIndex]; |
||||||
|
} |
||||||
|
historyMode = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Permet de gérer les commandes cachées (non proposées dans l'autocomplétion) |
||||||
|
* @param {string} command |
||||||
|
* @returns {string|void} Html à afficher dans la réponse de la commande |
||||||
|
*/ |
||||||
|
function handleCustomCommands(command) { |
||||||
|
switch (command) { |
||||||
|
case "pif": |
||||||
|
pif(); |
||||||
|
return "C'est la fête !"; |
||||||
|
case "light": |
||||||
|
if (document.body.classList.length === 0) |
||||||
|
return "Vous êtes déjà en mode clair"; |
||||||
|
setDarkMode(false); |
||||||
|
return "Vous êtes maintenant en mode clair."; |
||||||
|
case "dark": |
||||||
|
if (document.body.classList.length === 1) |
||||||
|
return "Vous êtes déjà en mode sombre"; |
||||||
|
setDarkMode(true); |
||||||
|
return "Vous êtes maintenant en mode sombre."; |
||||||
|
case "get cv": |
||||||
|
getCV(); |
||||||
|
return "Le CV va être téléchargé."; |
||||||
|
case "rm -rf /": |
||||||
|
rmRf(); |
||||||
|
return "w4dhIHZhIFDDiVRFUiAh"; |
||||||
|
case "clear": |
||||||
|
terminalBody.innerHTML = `<div id="terminal"></div>`; |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convert HTML to DOM object |
||||||
|
* @param html |
||||||
|
* @returns {DocumentFragment} |
||||||
|
*/ |
||||||
|
function stringToDom(html) { |
||||||
|
return document.createRange().createContextualFragment(html); |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------
|
||||||
|
// EVENT LISTENNER
|
||||||
|
// ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Ajout du focus sur l'input même si on clique sur le body (pour garder le curseur)
|
||||||
|
document.body.addEventListener("click", function (e) { |
||||||
|
if (e.target.tagName !== "INPUT") { |
||||||
|
const activeInput = document.querySelector("input[data-active]"); |
||||||
|
activeInput.focus(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
document.querySelector(".fake-close").addEventListener("click", function (e) { |
||||||
|
const terminalEl = document.querySelector(".terminal"); |
||||||
|
terminalEl.parentElement.removeChild(terminalEl); |
||||||
|
}); |
After Width: | Height: | Size: 8.9 KiB |
@ -0,0 +1,67 @@ |
|||||||
|
import confetti from "canvas-confetti"; |
||||||
|
import { Fireworks } from "fireworks-js"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Affiche des confettis sur la page |
||||||
|
*/ |
||||||
|
export function pif() { |
||||||
|
const count = 200; |
||||||
|
const defaults = { |
||||||
|
origin: { y: 0.7 }, |
||||||
|
}; |
||||||
|
|
||||||
|
function fire(particleRatio, opts) { |
||||||
|
confetti( |
||||||
|
Object.assign({}, defaults, opts, { |
||||||
|
particleCount: Math.floor(count * particleRatio), |
||||||
|
}) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
fire(0.25, { |
||||||
|
spread: 26, |
||||||
|
startVelocity: 55, |
||||||
|
}); |
||||||
|
fire(0.2, { |
||||||
|
spread: 60, |
||||||
|
}); |
||||||
|
fire(0.35, { |
||||||
|
spread: 100, |
||||||
|
decay: 0.91, |
||||||
|
scalar: 0.8, |
||||||
|
}); |
||||||
|
fire(0.1, { |
||||||
|
spread: 120, |
||||||
|
startVelocity: 25, |
||||||
|
decay: 0.92, |
||||||
|
scalar: 1.2, |
||||||
|
}); |
||||||
|
fire(0.1, { |
||||||
|
spread: 120, |
||||||
|
startVelocity: 45, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export function setDarkMode(value) { |
||||||
|
if (value) { |
||||||
|
document.body.classList.add("dark-mode"); |
||||||
|
} else { |
||||||
|
document.body.classList.remove("dark-mode"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export function getCV() { |
||||||
|
const a = document.createElement("a"); |
||||||
|
a.href = "resources/CV - Greg Lebreton.pdf"; |
||||||
|
a.setAttribute("download", "CV - Greg Lebreton.pdf.pdf"); |
||||||
|
a.click(); |
||||||
|
} |
||||||
|
|
||||||
|
export function rmRf() { |
||||||
|
setDarkMode(true); |
||||||
|
document.body.classList.add("firework"); |
||||||
|
const fireworks = new Fireworks(document.body, { |
||||||
|
mouse: { click: true, move: false, max: 7 }, |
||||||
|
}); |
||||||
|
fireworks.start(); |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
export function dragElement(elmnt) { |
||||||
|
var pos1 = 0, |
||||||
|
pos2 = 0, |
||||||
|
pos3 = 0, |
||||||
|
pos4 = 0; |
||||||
|
const element = document.querySelector(".terminal__header"); |
||||||
|
if (element) { |
||||||
|
// if present, the header is where you move the DIV from:
|
||||||
|
element.onmousedown = dragMouseDown; |
||||||
|
} else { |
||||||
|
// otherwise, move the DIV from anywhere inside the DIV:
|
||||||
|
elmnt.onmousedown = dragMouseDown; |
||||||
|
} |
||||||
|
|
||||||
|
function dragMouseDown(e) { |
||||||
|
e = e || window.event; |
||||||
|
e.preventDefault(); |
||||||
|
// get the mouse cursor position at startup:
|
||||||
|
pos3 = e.clientX; |
||||||
|
pos4 = e.clientY; |
||||||
|
document.onmouseup = closeDragElement; |
||||||
|
// call a function whenever the cursor moves:
|
||||||
|
document.onmousemove = elementDrag; |
||||||
|
} |
||||||
|
|
||||||
|
function elementDrag(e) { |
||||||
|
e = e || window.event; |
||||||
|
e.preventDefault(); |
||||||
|
// calculate the new cursor position:
|
||||||
|
pos1 = pos3 - e.clientX; |
||||||
|
pos2 = pos4 - e.clientY; |
||||||
|
pos3 = e.clientX; |
||||||
|
pos4 = e.clientY; |
||||||
|
// set the element's new position:
|
||||||
|
elmnt.style.top = elmnt.offsetTop - pos2 + "px"; |
||||||
|
elmnt.style.left = elmnt.offsetLeft - pos1 + "px"; |
||||||
|
} |
||||||
|
|
||||||
|
function closeDragElement() { |
||||||
|
// stop moving when mouse button is released:
|
||||||
|
document.onmouseup = null; |
||||||
|
document.onmousemove = null; |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 610 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,60 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="fr"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"/> |
||||||
|
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport"/> |
||||||
|
<meta content="ie=edge" http-equiv="X-UA-Compatible"/> |
||||||
|
<meta content="Greg Lebreton - Devops Engineer" name="title"/> |
||||||
|
<!--meta content="Mon CV sous la forme d'un terminal. Parce qu'il n'y a pas de raisons que seuls les développeurs utilisent un terminal ! :D" name="description"/--> |
||||||
|
<meta content="resume,dautry,developer,fullstack,france" name="keywords"/> |
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/> |
||||||
|
<meta content="Français" name="language"/> |
||||||
|
<!--meta content="Antoine DAUTRY" name="author"/--> |
||||||
|
<title>Terminal CV</title> |
||||||
|
<script src="app.js" type="module" defer></script> |
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com"> |
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Ubuntu+Mono&display=swap" rel="stylesheet"> |
||||||
|
<link href="scss/style.scss" rel="stylesheet"> |
||||||
|
<link crossorigin="anonymous" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" |
||||||
|
integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ==" |
||||||
|
referrerpolicy="no-referrer" rel="stylesheet"/> |
||||||
|
<link href="apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180"> |
||||||
|
<link href="favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"> |
||||||
|
<link href="favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"> |
||||||
|
<link href="site.webmanifest" rel="manifest"> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div class="terminal"> |
||||||
|
<div class="terminal__header"> |
||||||
|
<div class="fake-button fake-close"></div> |
||||||
|
<div class="fake-button fake-minimize"></div> |
||||||
|
<div class="fake-button fake-zoom"></div> |
||||||
|
</div> |
||||||
|
<div class="terminal__body"> |
||||||
|
<div class="terminal__banner"><pre> |
||||||
|
██████╗ ██╗ ██╗ ████████╗███████╗██████╗ ███╗ ███╗██╗███╗ ██╗ █████╗ ██╗ |
||||||
|
██╔════╝██║ ██║ ╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██║████╗ ██║██╔══██╗██║ |
||||||
|
██║ ██║ ██║ ██║ █████╗ ██████╔╝██╔████╔██║██║██╔██╗ ██║███████║██║ |
||||||
|
██║ ╚██╗ ██╔╝ ██║ ██╔══╝ ██╔══██╗██║╚██╔╝██║██║██║╚██╗██║██╔══██║██║ |
||||||
|
╚██████╗ ╚████╔╝ ██║ ███████╗██║ ██║██║ ╚═╝ ██║██║██║ ╚████║██║ ██║███████╗ |
||||||
|
╚═════╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝</pre> |
||||||
|
<div class="terminal__author">Greg Lebreton</div> |
||||||
|
<p>Bienvenue sur mon CV terminal ! Pour afficher les commandes disponibles tapez <code>help</code>. Pour valider chaque commande appuyez sur <em>Entrer</em>, vous pouvez utiliser la touche <em>Tabulation</em> afin de vous aider à compléter une commande.</p> |
||||||
|
</div> |
||||||
|
<div id="terminal"></div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="socials"> |
||||||
|
<!--a href="https://www.linkedin.com/in/antoine-dautry/" target="_blank"--> |
||||||
|
<i class="fab fa-linkedin"></i> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
<!--a href="https://github.com/antoine1003/resume-terminal" id="banner-github" target="_blank"> |
||||||
|
<img alt="Fork me on GitHub" height="149" loading="lazy" |
||||||
|
src="https://github.blog/wp-content/uploads/2008/12/forkme_right_darkblue_121621.png?resize=149%2C149" |
||||||
|
width="149"/> |
||||||
|
</a--> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,87 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"command":"help", |
||||||
|
"responseType":"list", |
||||||
|
"value":[ |
||||||
|
"<code>a-propos</code> : Affiche les informations me concernant", |
||||||
|
"<code>clear</code> : Nettoie le terminal", |
||||||
|
"<code>experiences</code> : Affiche la liste de mes expériences", |
||||||
|
"<code>get cv</code> : Télécharge le CV", |
||||||
|
"<code>help</code> : Affiche l'aide", |
||||||
|
"<code>hobby</code> : Affiche la liste de mes passes temps", |
||||||
|
"<code>projets-perso</code> : Affiche la liste de mes projets personnels", |
||||||
|
"<code>dark/light</code> : Change le thème de la page", |
||||||
|
"<em>Vous pouvez utiliser la touche TAB afin de compléter une commande</em>", |
||||||
|
"<em>Vous pouvez retrouver les anciennes commandes avec les flèches haut et bas.</em>" |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"command":"a-propos", |
||||||
|
"responseType":"code", |
||||||
|
"value":[ |
||||||
|
"{", |
||||||
|
" \"nom\" : \"Gregory Lebreton\",", |
||||||
|
" \"poste\" : \"Ingénieur Devops\",", |
||||||
|
" \"experience\" : \"5\",", |
||||||
|
" \"ville\" : \"Paris, France\"", |
||||||
|
"}" |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"command":"experiences", |
||||||
|
"responseType":"table", |
||||||
|
"headers":[ |
||||||
|
"Date", |
||||||
|
"Client", |
||||||
|
"Description", |
||||||
|
"Tech" |
||||||
|
], |
||||||
|
"rows":[ |
||||||
|
[ |
||||||
|
"06/2019<br/>09/2019", |
||||||
|
"<br/><em>Safran, S.A.E</em>", |
||||||
|
"Mise en place d'une plateforme mettant en relation les<br/>différents acteurs de la DSI sur une plateforme logicielle.", |
||||||
|
"Docker<br/>Kubernetes<br/>Axelor" |
||||||
|
], |
||||||
|
[ |
||||||
|
"12/2017<br/>03/2019", |
||||||
|
"PHP dev<br/><em>Leading Frog</em>", |
||||||
|
"Module PHP permettant l'envoie de cartes postales<br/>numériques avec implémentation API Stripe.", |
||||||
|
"PHP<br/>JavaScript<br/>SQL" |
||||||
|
] |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"command":"hobby", |
||||||
|
"responseType":"list", |
||||||
|
"value":[ |
||||||
|
"Musique: Skateboard, Unity, VR", |
||||||
|
"Programmation: Python, bash, PHP", |
||||||
|
"Autre: Cinéma, Environnement, Famille" |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"command":"projets-perso", |
||||||
|
"responseType":"table", |
||||||
|
"headers":[ |
||||||
|
"Nom", |
||||||
|
"Description", |
||||||
|
"Tech", |
||||||
|
"Liens" |
||||||
|
], |
||||||
|
"rows":[ |
||||||
|
[ |
||||||
|
"Personal website<br/>(2021)", |
||||||
|
"Site web personnel me permettant de montrer mes projets et tester des applicatifs<br/>", |
||||||
|
"PHP/JS", |
||||||
|
"<a href=\"https://www.gregandev.fr\" target=\"blank\">Lien</a>" |
||||||
|
], |
||||||
|
[ |
||||||
|
"GoldeneyeVR<br/>(2020)", |
||||||
|
"Implémentation VR au célèbre jeux de 1997.", |
||||||
|
"C# WPF", |
||||||
|
"<a href=\"https://www.gregandev.fr/index.php?contenu=GoldeneyeProjet\" target=\"blank\">Lien</a>" |
||||||
|
] |
||||||
|
] |
||||||
|
} |
||||||
|
] |
@ -0,0 +1,137 @@ |
|||||||
|
.snowflake { |
||||||
|
color: #fff; |
||||||
|
font-size: 1em; |
||||||
|
font-family: Arial, sans-serif; |
||||||
|
text-shadow: 0 0 5px #000; |
||||||
|
} |
||||||
|
|
||||||
|
@-webkit-keyframes snowflakes-fall { |
||||||
|
0% { |
||||||
|
top: -10% |
||||||
|
} |
||||||
|
100% { |
||||||
|
top: 100% |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@-webkit-keyframes snowflakes-shake { |
||||||
|
0%, 100% { |
||||||
|
-webkit-transform: translateX(0); |
||||||
|
transform: translateX(0) |
||||||
|
} |
||||||
|
50% { |
||||||
|
-webkit-transform: translateX(80px); |
||||||
|
transform: translateX(80px) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@keyframes snowflakes-fall { |
||||||
|
0% { |
||||||
|
top: -10% |
||||||
|
} |
||||||
|
100% { |
||||||
|
top: 100% |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@keyframes snowflakes-shake { |
||||||
|
0%, 100% { |
||||||
|
transform: translateX(0) |
||||||
|
} |
||||||
|
50% { |
||||||
|
transform: translateX(80px) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake { |
||||||
|
position: fixed; |
||||||
|
top: -10%; |
||||||
|
z-index: 9999; |
||||||
|
-webkit-user-select: none; |
||||||
|
-moz-user-select: none; |
||||||
|
-ms-user-select: none; |
||||||
|
user-select: none; |
||||||
|
cursor: default; |
||||||
|
-webkit-animation-name: snowflakes-fall, snowflakes-shake; |
||||||
|
-webkit-animation-duration: 10s, 3s; |
||||||
|
-webkit-animation-timing-function: linear, ease-in-out; |
||||||
|
-webkit-animation-iteration-count: infinite, infinite; |
||||||
|
-webkit-animation-play-state: running, running; |
||||||
|
animation-name: snowflakes-fall, snowflakes-shake; |
||||||
|
animation-duration: 10s, 3s; |
||||||
|
animation-timing-function: linear, ease-in-out; |
||||||
|
animation-iteration-count: infinite, infinite; |
||||||
|
animation-play-state: running, running |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(0) { |
||||||
|
left: 1%; |
||||||
|
-webkit-animation-delay: 0s, 0s; |
||||||
|
animation-delay: 0s, 0s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(1) { |
||||||
|
left: 10%; |
||||||
|
-webkit-animation-delay: 1s, 1s; |
||||||
|
animation-delay: 1s, 1s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(2) { |
||||||
|
left: 20%; |
||||||
|
-webkit-animation-delay: 6s, .5s; |
||||||
|
animation-delay: 6s, .5s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(3) { |
||||||
|
left: 30%; |
||||||
|
-webkit-animation-delay: 4s, 2s; |
||||||
|
animation-delay: 4s, 2s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(4) { |
||||||
|
left: 40%; |
||||||
|
-webkit-animation-delay: 2s, 2s; |
||||||
|
animation-delay: 2s, 2s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(5) { |
||||||
|
left: 50%; |
||||||
|
-webkit-animation-delay: 8s, 3s; |
||||||
|
animation-delay: 8s, 3s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(6) { |
||||||
|
left: 60%; |
||||||
|
-webkit-animation-delay: 6s, 2s; |
||||||
|
animation-delay: 6s, 2s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(7) { |
||||||
|
left: 70%; |
||||||
|
-webkit-animation-delay: 2.5s, 1s; |
||||||
|
animation-delay: 2.5s, 1s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(8) { |
||||||
|
left: 80%; |
||||||
|
-webkit-animation-delay: 1s, 0s; |
||||||
|
animation-delay: 1s, 0s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(9) { |
||||||
|
left: 90%; |
||||||
|
-webkit-animation-delay: 3s, 1.5s; |
||||||
|
animation-delay: 3s, 1.5s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(10) { |
||||||
|
left: 25%; |
||||||
|
-webkit-animation-delay: 2s, 0s; |
||||||
|
animation-delay: 2s, 0s |
||||||
|
} |
||||||
|
|
||||||
|
.snowflake:nth-of-type(11) { |
||||||
|
left: 65%; |
||||||
|
-webkit-animation-delay: 4s, 2.5s; |
||||||
|
animation-delay: 4s, 2.5s |
||||||
|
} |
@ -0,0 +1,314 @@ |
|||||||
|
$border-radius: 5px; |
||||||
|
|
||||||
|
:root { |
||||||
|
--text-color: #fff; |
||||||
|
--text-accent-color: darksalmon; |
||||||
|
--link-color: darkorange; |
||||||
|
--bg-1: #f27121; |
||||||
|
--bg-2: #e94057; |
||||||
|
--bg-3: #8a2387; |
||||||
|
--bg-1-social: #f3a183; |
||||||
|
--bg-2-social: #ec6f66; |
||||||
|
--username-color: cadetblue; |
||||||
|
--terminal-bg: rgba(56, 4, 40, 0.9); |
||||||
|
--terminal-header-bg: #bbb; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
&.dark-mode { |
||||||
|
--text-accent-color: #ffca85; |
||||||
|
--link-color: burlywood; |
||||||
|
--bg-1: #211F20; |
||||||
|
--bg-2: #292D34; |
||||||
|
--bg-3: #213030; |
||||||
|
--bg-1-social: #414141; |
||||||
|
--bg-2-social: #485461; |
||||||
|
--username-color: #858585; |
||||||
|
--terminal-bg: rgb(0 0 0 / 90%); |
||||||
|
--terminal-header-bg: #585252; |
||||||
|
&.firework { |
||||||
|
--terminal-bg: rgb(0 0 0 / 15%); |
||||||
|
} |
||||||
|
} |
||||||
|
box-sizing: border-box; |
||||||
|
margin: 0; |
||||||
|
display: flex; |
||||||
|
justify-content: space-around; |
||||||
|
align-items: center; |
||||||
|
flex-direction: column; |
||||||
|
height: 100vh; |
||||||
|
background: var(--bg-3); /* fallback for old browsers */ |
||||||
|
background: -webkit-linear-gradient( |
||||||
|
to right, |
||||||
|
var(--bg-1), |
||||||
|
var(--bg-2), |
||||||
|
var(--bg-3) |
||||||
|
); /* Chrome 10-25, Safari 5.1-6 */ |
||||||
|
background: linear-gradient( |
||||||
|
to right, |
||||||
|
var(--bg-1), |
||||||
|
var(--bg-2), |
||||||
|
var(--bg-3) |
||||||
|
); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ |
||||||
|
} |
||||||
|
|
||||||
|
ul { |
||||||
|
margin: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.terminal { |
||||||
|
position: absolute; |
||||||
|
resize: both; |
||||||
|
overflow: hidden; |
||||||
|
height: 450px; |
||||||
|
width: min(900px, 90vw); |
||||||
|
|
||||||
|
.terminal__header { |
||||||
|
height: 25px; |
||||||
|
padding: 0 8px; |
||||||
|
background-color: var(--terminal-header-bg); |
||||||
|
margin: 0 auto; |
||||||
|
border-top-right-radius: $border-radius; |
||||||
|
border-top-left-radius: $border-radius; |
||||||
|
cursor: move; |
||||||
|
|
||||||
|
.fake-button { |
||||||
|
height: 10px; |
||||||
|
width: 10px; |
||||||
|
border-radius: 50%; |
||||||
|
border: 1px solid #000; |
||||||
|
position: relative; |
||||||
|
top: 6px; |
||||||
|
left: 6px; |
||||||
|
display: inline-block; |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
&.fake-close { |
||||||
|
left: 6px; |
||||||
|
background-color: #ff3b47; |
||||||
|
border-color: #9d252b; |
||||||
|
} |
||||||
|
|
||||||
|
&.fake-minimize { |
||||||
|
left: 11px; |
||||||
|
background-color: #ffc100; |
||||||
|
border-color: #9d802c; |
||||||
|
} |
||||||
|
|
||||||
|
&.fake-zoom { |
||||||
|
left: 16px; |
||||||
|
background-color: #00d742; |
||||||
|
border-color: #049931; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.terminal__body { |
||||||
|
font-family: "Ubuntu Mono", monospace; |
||||||
|
background: var(--terminal-bg); |
||||||
|
color: var(--text-color); |
||||||
|
padding: 8px; |
||||||
|
overflow-y: scroll; |
||||||
|
overflow-x: hidden; |
||||||
|
box-shadow: rgba(0, 0, 0, 0.2) 0px 12px 28px 0px, |
||||||
|
rgba(0, 0, 0, 0.1) 0px 2px 4px 0px, |
||||||
|
rgba(255, 255, 255, 0.05) 0px 0px 0px 1px inset; |
||||||
|
border-bottom-right-radius: $border-radius; |
||||||
|
border-bottom-left-radius: $border-radius; |
||||||
|
height: calc(100% - 41px); |
||||||
|
|
||||||
|
code { |
||||||
|
color: var(--text-accent-color); |
||||||
|
font-size: 14px; |
||||||
|
} |
||||||
|
|
||||||
|
.terminal__banner { |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
justify-content: center; |
||||||
|
color: var(--text-color); |
||||||
|
|
||||||
|
.terminal__author { |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.terminal__line { |
||||||
|
margin-bottom: 8px; |
||||||
|
|
||||||
|
&::before { |
||||||
|
content: "Greg LEBRETON ~$ "; |
||||||
|
color: var(--username-color); |
||||||
|
} |
||||||
|
|
||||||
|
input[type="text"] { |
||||||
|
background: none; |
||||||
|
border: none; |
||||||
|
font-family: "Ubuntu Mono", monospace; |
||||||
|
color: var(--text-color); |
||||||
|
outline: none; |
||||||
|
font-size: 15px; |
||||||
|
width: calc(100% - 150px); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.terminal__response { |
||||||
|
margin: 8px 0 16px 0; |
||||||
|
|
||||||
|
table { |
||||||
|
border: 1px dashed; |
||||||
|
padding: 4px; |
||||||
|
width: 100%; |
||||||
|
|
||||||
|
a { |
||||||
|
text-decoration: none; |
||||||
|
color: darkorange; |
||||||
|
} |
||||||
|
|
||||||
|
thead { |
||||||
|
th { |
||||||
|
font-weight: normal; |
||||||
|
color: cadetblue; |
||||||
|
border-bottom: 1px solid white; |
||||||
|
padding-bottom: 4px; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
tbody { |
||||||
|
td { |
||||||
|
padding: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
tr:not(:last-child) { |
||||||
|
td { |
||||||
|
border-bottom: 1px solid white; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.socials { |
||||||
|
position: absolute; |
||||||
|
right: 16px; |
||||||
|
bottom: 16px; |
||||||
|
display: flex; |
||||||
|
gap: 16px; |
||||||
|
|
||||||
|
a { |
||||||
|
border-radius: 50%; |
||||||
|
background: var(--bg-2-social); /* fallback for old browsers */ |
||||||
|
background: -webkit-linear-gradient( |
||||||
|
to left, |
||||||
|
var(--bg-1-social), |
||||||
|
var(--bg-2-social) |
||||||
|
); /* Chrome 10-25, Safari 5.1-6 */ |
||||||
|
background: linear-gradient( |
||||||
|
to left, |
||||||
|
var(--bg-1-social), |
||||||
|
var(--bg-2-social) |
||||||
|
); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ |
||||||
|
box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px; |
||||||
|
width: 4em; |
||||||
|
height: 4em; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
text-decoration: none; |
||||||
|
&:hover { |
||||||
|
background: var(--bg-2-social); /* fallback for old browsers */ |
||||||
|
background: -webkit-linear-gradient( |
||||||
|
to right, |
||||||
|
var(--bg-1-social), |
||||||
|
var(--bg-2-social) |
||||||
|
); /* Chrome 10-25, Safari 5.1-6 */ |
||||||
|
background: linear-gradient( |
||||||
|
to right, |
||||||
|
var(--bg-1-social), |
||||||
|
var(--bg-2-social) |
||||||
|
); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ |
||||||
|
box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px; |
||||||
|
width: 4em; |
||||||
|
height: 4em; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
text-decoration: none; |
||||||
|
} |
||||||
|
i { |
||||||
|
color: white; |
||||||
|
font-size: 2em; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#banner-github { |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
right: 0; |
||||||
|
} |
||||||
|
|
||||||
|
@media (max-width: 880px) { |
||||||
|
.terminal .terminal__body { |
||||||
|
.terminal__banner { |
||||||
|
pre { |
||||||
|
font-size: 10px; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@media (max-width: 640px) { |
||||||
|
body { |
||||||
|
align-items: center; |
||||||
|
flex-direction: column; |
||||||
|
justify-content: space-evenly; |
||||||
|
} |
||||||
|
canvas { |
||||||
|
position: fixed; |
||||||
|
top: 0; |
||||||
|
bottom: 0; |
||||||
|
left: 0; |
||||||
|
right: 0; |
||||||
|
z-index: -1; |
||||||
|
} |
||||||
|
|
||||||
|
.terminal { |
||||||
|
position: unset; |
||||||
|
width: unset; |
||||||
|
height: unset; |
||||||
|
resize: none; |
||||||
|
.terminal__body { |
||||||
|
max-width: unset; |
||||||
|
width: 90vw; |
||||||
|
height: 70vh; |
||||||
|
|
||||||
|
.terminal__banner { |
||||||
|
pre { |
||||||
|
font-size: 5px; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
.socials { |
||||||
|
font-size: 13px; |
||||||
|
position: relative; |
||||||
|
bottom: unset; |
||||||
|
right: unset; |
||||||
|
} |
||||||
|
#banner-github { |
||||||
|
img { |
||||||
|
width: 100px; |
||||||
|
height: 100px; |
||||||
|
} |
||||||
|
} |
||||||
|
#version { |
||||||
|
top: 38px; |
||||||
|
right: 38px; |
||||||
|
font-size: 13px; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@import "snowflakes"; |
@ -0,0 +1,19 @@ |
|||||||
|
{ |
||||||
|
"name":"Resume Greg Lebreton", |
||||||
|
"short_name":"Resume", |
||||||
|
"icons":[ |
||||||
|
{ |
||||||
|
"src":"./android-chrome-192x192.png", |
||||||
|
"sizes":"192x192", |
||||||
|
"type":"image/png" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"src":"./android-chrome-512x512.png", |
||||||
|
"sizes":"512x512", |
||||||
|
"type":"image/png" |
||||||
|
} |
||||||
|
], |
||||||
|
"theme_color":"#ffffff", |
||||||
|
"background_color":"#ffffff", |
||||||
|
"display":"standalone" |
||||||
|
} |
Binary file not shown.
Loading…
Reference in new issue