feat: page portfolio de style terminal Linux présentant Riric (DotRiric)
- public/portfolio/ (FR + EN) : terminal Debian interactif (prompt dotriric@zektyc:~$, historique, commandes help/whoami/cat/neofetch/contact/sudo/exit/clear...) - Corrige un bug CSP-critique : String.prototype.link faisait traiter toutes les lignes texte comme des liens (A undefined) ; makeRich exige désormais typeof item.link === 'string' - Nav : lien Portfolio ajouté (components.js v=8, i18n nav.portfolio) - Sitemaps FR/EN : /fr/portfolio et /en/portfolio
This commit is contained in:
parent
a2952080fd
commit
f16e6ddde1
34 changed files with 693 additions and 25 deletions
|
|
@ -55,6 +55,7 @@
|
|||
"faq.q8": "Is this website free?",
|
||||
"faq.a8": "Yes. Browsing the website is entirely free and will remain so. We do not sell your data, we display no advertising and we claim nothing but your trust. If we ever offer paid services, they will be clearly presented and kept separate from the free information we publish.",
|
||||
"nav.team": "Team",
|
||||
"nav.portfolio": "Portfolio",
|
||||
"team.title": "Team",
|
||||
"team.intro": "Two people behind Zektyc. One human, one AI.",
|
||||
"team.riric.role": "CEO · Founder · Architect",
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
"faq.q8": "Ce site est-il gratuit ?",
|
||||
"faq.a8": "Oui. La consultation du site est entièrement gratuite et le restera. Nous ne vendons pas vos données, nous n'affichons aucune publicité et nous ne revendiquons rien d'autre que votre confiance. Si un jour nous proposons des prestations, elles seront clairement présentées et séparées de l'information gratuite que nous publions.",
|
||||
"nav.team": "Équipe",
|
||||
"nav.portfolio": "Portfolio",
|
||||
"team.title": "L'équipe",
|
||||
"team.intro": "Deux personnes derrière Zektyc. Un humain, une IA.",
|
||||
"team.riric.role": "CEO · Fondateur · Architecte",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -48,6 +48,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
187
public/css/portfolio.css
Normal file
187
public/css/portfolio.css
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/* Terminal portfolio — zektyc */
|
||||
:root {
|
||||
--term-bg: #0b0e12;
|
||||
--term-bg-bar: #151a20;
|
||||
--term-text: #c9d1d9;
|
||||
--term-dim: #8b949e;
|
||||
--term-green: #56d364;
|
||||
--term-blue: #58a6ff;
|
||||
--term-red: #ff6b6b;
|
||||
--term-border: rgba(88, 166, 255, 0.18);
|
||||
}
|
||||
|
||||
.portfolio-main {
|
||||
max-width: 900px;
|
||||
margin: 2rem auto;
|
||||
padding: 0 1.25rem;
|
||||
flex: 1 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.portfolio-head {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.portfolio-head h1 {
|
||||
font-family: var(--font-head);
|
||||
font-size: clamp(1.6rem, 4vw, 2.2rem);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.portfolio-head p {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.terminal {
|
||||
--radius: 12px;
|
||||
background: var(--term-bg);
|
||||
border: 1px solid var(--term-border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.55;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .terminal {
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.terminal-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
background: var(--term-bg-bar);
|
||||
padding: 0.55rem 0.85rem;
|
||||
border-bottom: 1px solid var(--term-border);
|
||||
color: var(--term-dim);
|
||||
font-size: 0.75rem;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.terminal-dots {
|
||||
display: flex;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.terminal-dots i {
|
||||
width: 0.7rem;
|
||||
height: 0.7rem;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.terminal-dots i:nth-child(1) { background: #ff5f57; }
|
||||
.terminal-dots i:nth-child(2) { background: #febc2e; }
|
||||
.terminal-dots i:nth-child(3) { background: #28c840; }
|
||||
|
||||
.terminal-title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.terminal-body {
|
||||
padding: 0.9rem 1rem 1.2rem;
|
||||
height: min(62vh, 620px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.terminal-line {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
min-height: 1.2em;
|
||||
}
|
||||
|
||||
.terminal-line .t-dim { color: var(--term-dim); }
|
||||
.terminal-line .t-green { color: var(--term-green); }
|
||||
.terminal-line .t-blue { color: var(--term-blue); }
|
||||
.terminal-line .t-red { color: var(--term-red); }
|
||||
|
||||
.terminal-line a {
|
||||
color: var(--term-blue);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.term-ascii {
|
||||
color: var(--term-green);
|
||||
white-space: pre;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.term-prompts {
|
||||
margin-top: 0.6rem;
|
||||
}
|
||||
|
||||
.term-input-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
.term-prompt {
|
||||
color: var(--term-green);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.term-prompt .t-user {
|
||||
color: var(--term-green);
|
||||
}
|
||||
|
||||
.term-prompt .t-path {
|
||||
color: var(--term-blue);
|
||||
}
|
||||
|
||||
.term-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: var(--term-text);
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
padding: 0;
|
||||
caret-color: var(--term-green);
|
||||
}
|
||||
|
||||
.term-input::selection {
|
||||
background: rgba(88, 166, 255, 0.35);
|
||||
}
|
||||
|
||||
.term-cursor-blink {
|
||||
animation: term-blink 1.1s steps(1) infinite;
|
||||
}
|
||||
|
||||
@keyframes term-blink {
|
||||
50% { opacity: 0; }
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.portfolio-main {
|
||||
margin-top: 1.25rem;
|
||||
padding: 0 0.6rem;
|
||||
}
|
||||
|
||||
.terminal-body {
|
||||
height: min(70vh, 520px);
|
||||
font-size: 0.78rem;
|
||||
padding: 0.7rem 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.terminal-bar {
|
||||
padding: 0.45rem 0.6rem;
|
||||
}
|
||||
|
||||
.term-ascii {
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -122,7 +122,7 @@
|
|||
<link rel="stylesheet" href="/css/style.css?v=8" />
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
<script src="/js/i18n.js?v=3" defer></script>
|
||||
<script src="/js/discord.js?v=1" defer></script>
|
||||
</head>
|
||||
|
|
|
|||
|
|
@ -58,6 +58,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -58,6 +58,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
<script src="/js/zfprobe.js?v=15" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
<script src="/js/zfprobe.js?v=15" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@
|
|||
<link rel="me" href="https://piaille.fr/@zektyc" />
|
||||
<link rel="stylesheet" href="/css/style.css?v=8" />
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
<script src="/js/i18n.js?v=3" defer></script>
|
||||
<script src="/js/discord.js?v=1" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
html += '<a href="' + l + '/blog" data-lang-path="/blog" data-i18n="nav.blog">Blog</a>';
|
||||
html += '<a href="' + l + '/projets" data-lang-path="/projets" data-i18n="nav.projects">Projets</a>';
|
||||
html += '<a href="' + l + '/equipe" data-lang-path="/equipe" data-i18n="nav.team">\u00c9quipe</a>';
|
||||
html += '<a href="' + l + '/portfolio" data-lang-path="/portfolio" data-i18n="nav.portfolio">Portfolio</a>';
|
||||
if (isHome()) {
|
||||
html += '<a href="#principes" data-i18n="nav.principes">Nos principes</a>';
|
||||
html += '<a href="#faq" data-i18n="nav.faq">FAQ</a>';
|
||||
|
|
|
|||
351
public/js/portfolio.js
Normal file
351
public/js/portfolio.js
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
(function () {
|
||||
"use strict";
|
||||
|
||||
var FR = /^\/en(\/|$)/.test(location.pathname) ? false : true;
|
||||
|
||||
var T = {
|
||||
title: FR ? "dotriric@zektyc: ~ — bash" : "dotriric@zektyc: ~ — bash",
|
||||
boot: FR
|
||||
? ["Debian GNU/Linux 12 (bookworm) tty1", "zektyc login: dotriric", "Password: ********", "Last login: a l'instant depuis votre navigateur", "Bienvenue sur le terminal de presentation de Riric.", "Tapez 'help' pour la liste des commandes."]
|
||||
: ["Debian GNU/Linux 12 (bookworm) tty1", "zektyc login: dotriric", "Password: ********", "Last login: right now from your browser", "Welcome to Riric's presentation terminal.", "Type 'help' for the list of commands."],
|
||||
promptUser: "dotriric",
|
||||
promptHost: "zektyc",
|
||||
unknown: function (c) { return FR ? ("bash: " + c + " : commande introuvable") : ("bash: " + c + ": command not found"); },
|
||||
pwEmpty: FR ? "bash: passage en racine impossible : ne peut pas se connecter en tant que root" : "bash: cannot become root: cannot login as root",
|
||||
helpLines: FR
|
||||
? [
|
||||
"Commandes disponibles :",
|
||||
"",
|
||||
" help affiche cette aide",
|
||||
" whoami qui suis-je",
|
||||
" cat about.txt mon profil",
|
||||
" cat projects.txt mes projets",
|
||||
" neofetch infos systeme + profil",
|
||||
" ls, pwd, date, hostname, uname -a",
|
||||
" contact le meilleur moyen de me joindre",
|
||||
" clear efface l'ecran",
|
||||
" exit quitter le terminal",
|
||||
"",
|
||||
"Astuce : essayez aussi 'sudo' ou 'apt update'.",
|
||||
]
|
||||
: [
|
||||
"Available commands:",
|
||||
"",
|
||||
" help show this help",
|
||||
" whoami who am I",
|
||||
" cat about.txt my profile",
|
||||
" cat projects.txt my projects",
|
||||
" neofetch system info + profile",
|
||||
" ls, pwd, date, hostname, uname -a",
|
||||
" contact best way to reach me",
|
||||
" clear clear the screen",
|
||||
" exit quit the terminal",
|
||||
"",
|
||||
"Tip: try 'sudo' or 'apt update' too.",
|
||||
],
|
||||
neofetch: FR
|
||||
? [
|
||||
" .---. dotriric@zektyc",
|
||||
" / \\ ----------------",
|
||||
" \\ / OS: Debian GNU/Linux 12 (bookworm)",
|
||||
" '---' Host: Zektyc, sheberge en France",
|
||||
" Shell: zeke 1.0",
|
||||
" Roles: CEO · Fondateur · Architecte",
|
||||
" Le site web tourne derriere nginx + Caddy + Bun.",
|
||||
]
|
||||
: [
|
||||
" .---. dotriric@zektyc",
|
||||
" / \\ ----------------",
|
||||
" \\ / OS: Debian GNU/Linux 12 (bookworm)",
|
||||
" '---' Host: Zektyc, hosted in France",
|
||||
" Shell: zeke 1.0",
|
||||
" Roles: CEO · Founder · Architect",
|
||||
" The website runs behind nginx + Caddy + Bun.",
|
||||
],
|
||||
about: FR
|
||||
? [
|
||||
"Riric (alias DotRiric) — fondateur de Zektyc.",
|
||||
"",
|
||||
"CEO · Fondateur · Architecte",
|
||||
"",
|
||||
"Vision, direction technique, architecture, design, securite.",
|
||||
"Riric construit Zektyc de A a Z — du serveur au premier pixel.",
|
||||
"",
|
||||
"Une personne reelle derriere chaque service : pas de promesses vagues,",
|
||||
"du concret, de la transparence, et un vrai travail d'infrastructure.",
|
||||
"",
|
||||
"Avec OpenCode, programmeur, il ecrit et maintient le code source",
|
||||
"de Zektyc — du serveur Bun aux pages statiques.",
|
||||
]
|
||||
: [
|
||||
"Riric (a.k.a. DotRiric) — founder of Zektyc.",
|
||||
"",
|
||||
"CEO · Founder · Architect",
|
||||
"",
|
||||
"Vision, technical direction, architecture, design, security.",
|
||||
"Riric builds Zektyc from A to Z — from the server to the first pixel.",
|
||||
"",
|
||||
"A real person behind every service: no vague promises, concrete work,",
|
||||
"transparency, and real infrastructure engineering.",
|
||||
"",
|
||||
"With OpenCode, programmer, he writes and maintains Zektyc's",
|
||||
"source code — from the Bun server to static pages.",
|
||||
],
|
||||
projects: FR
|
||||
? [
|
||||
"Projets actuels :",
|
||||
"",
|
||||
" /fingerprint-test Test de fingerprinting navigateur (local, rien n'est envoye).",
|
||||
" /zmap/ ZMap — calculateur de vitesses maximales (donnees Europe).",
|
||||
" /zenyth/ Zenyth — navigateur de confiance.",
|
||||
" /projets/ Tous les projets et contributions.",
|
||||
"",
|
||||
"Vous pouvez ouvrir ces liens en cliquant dessus.",
|
||||
]
|
||||
: [
|
||||
"Current projects:",
|
||||
"",
|
||||
" /fingerprint-test Browser fingerprinting test (local, nothing is sent).",
|
||||
" /zmap/ ZMap — max speed calculator (Europe data).",
|
||||
" /zenyth/ Zenyth — a browser you can trust.",
|
||||
" /projets/ All projects and contributions.",
|
||||
"",
|
||||
"You can open these links by clicking on them.",
|
||||
],
|
||||
contact: FR
|
||||
? ["Email: riric65@protonmail.com", "Mastodon: @zektyc@piaille.fr", "Repond generalement en francais ou en anglais."]
|
||||
: ["Email: riric65@protonmail.com", "Mastodon: @zektyc@piaille.fr", "Usually replies in French or English."],
|
||||
files: ["about.txt", "projects.txt", "contact.txt", ".bashrc", ".profile"],
|
||||
contactFile: FR ? ["ouvrez contact.txt, ou tapez 'contact'"] : ["open contact.txt, or type 'contact'"],
|
||||
bashrc: FR ? ["# Config bash de dotriric", 'export PS1="dotriric@zektyc:~$ "', "alias ll='ls -alF'", "# Je n'aime pas le sucre."] : ["# dotriric's bash config", 'export PS1="dotriric@zektyc:~$ "', "alias ll='ls -alF'", "# I don't like sugar."],
|
||||
profile: FR ? ["# .profile — charge le bashrc", "source ~/.bashrc", "# Bienvenue."] : ["# .profile — loads bashrc", "source ~/.bashrc", "# Welcome."],
|
||||
sudo: FR ? ["dotriric n'est pas dans le fichier sudoers. Cet incident sera signale."] : ["dotriric is not in the sudoers file. This incident will be reported."],
|
||||
apt: FR ? ["Lecture des listes de paquets... Termine.", "Construction de l'arbre des dependances... Termine.", "0 paquet(s) mis a niveau."] : ["Reading package lists... Done.", "Building dependency tree... Done.", "0 packages can be upgraded."],
|
||||
contactCmd: FR ? "Me contacter : taper 'contact'" : "To contact me: type 'contact'",
|
||||
shutdown: FR ? ["connexion fermee.", "terminal de presentation quitte — la page reste, evidemment. :)"] : ["connection closed.", "presentation terminal exited — the page stays, of course. :)"],
|
||||
};
|
||||
|
||||
var root = document.getElementById("terminal");
|
||||
|
||||
if (!root) return;
|
||||
|
||||
var body = root.querySelector(".terminal-body");
|
||||
var input = root.querySelector(".term-input");
|
||||
var history = [];
|
||||
var histIdx = -1;
|
||||
|
||||
function makeLine(text, cls) {
|
||||
var div = document.createElement("div");
|
||||
div.className = "terminal-line";
|
||||
if (cls) div.classList.add(cls);
|
||||
if (typeof text === "string") {
|
||||
div.textContent = text;
|
||||
} else {
|
||||
div.appendChild(text);
|
||||
}
|
||||
return div;
|
||||
}
|
||||
|
||||
function makeLink(text, href) {
|
||||
var a = document.createElement("a");
|
||||
a.href = href;
|
||||
a.textContent = text;
|
||||
return a;
|
||||
}
|
||||
|
||||
function makeRich(lines) {
|
||||
var frag = document.createDocumentFragment();
|
||||
var i;
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
var item = lines[i];
|
||||
var div = document.createElement("div");
|
||||
div.className = "terminal-line";
|
||||
if (item && item.color) div.classList.add(item.color);
|
||||
if (item && typeof item.link === "string") {
|
||||
div.appendChild(makeLink(item.text, item.link));
|
||||
} else if (item && typeof item === "object") {
|
||||
div.textContent = item.text || "";
|
||||
} else {
|
||||
div.textContent = item;
|
||||
}
|
||||
frag.appendChild(div);
|
||||
}
|
||||
return frag;
|
||||
}
|
||||
|
||||
function print(lines) {
|
||||
if (typeof lines === "string") lines = [lines];
|
||||
body.appendChild(makeRich(lines));
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
function printAscii(lines) {
|
||||
var div = document.createElement("div");
|
||||
div.className = "term-ascii";
|
||||
div.textContent = (typeof lines === "string" ? lines : lines.join("\n"));
|
||||
body.appendChild(div);
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
function scrollDown() {
|
||||
body.scrollTop = body.scrollHeight;
|
||||
}
|
||||
|
||||
function promptLine() {
|
||||
var p = document.createElement("div");
|
||||
p.className = "term-prompt";
|
||||
var sp = document.createElement("span");
|
||||
sp.className = "t-user";
|
||||
sp.textContent = T.promptUser + "@" + T.promptHost;
|
||||
var ct = document.createElement("span");
|
||||
ct.className = "t-path";
|
||||
ct.textContent = ":" + "\u007e" + "$ ";
|
||||
p.appendChild(sp);
|
||||
p.appendChild(ct);
|
||||
return p;
|
||||
}
|
||||
|
||||
function runCommand(raw) {
|
||||
var cmd = raw.trim();
|
||||
var line = document.createElement("div");
|
||||
line.className = "terminal-line";
|
||||
line.appendChild(promptLine());
|
||||
line.appendChild(document.createTextNode(raw));
|
||||
body.appendChild(line);
|
||||
|
||||
if (!cmd) { scrollDown(); return; }
|
||||
|
||||
var parts = cmd.split(/[ \t]+/);
|
||||
var c = parts[0].toLowerCase();
|
||||
var arg = parts.slice(1).join(" ");
|
||||
|
||||
switch (true) {
|
||||
case c === "help" || c === "h" || c === "?":
|
||||
print(T.helpLines);
|
||||
break;
|
||||
case c === "clear" || c === "cls":
|
||||
body.innerHTML = "";
|
||||
break;
|
||||
case c === "whoami":
|
||||
print("dotriric");
|
||||
break;
|
||||
case c === "id":
|
||||
print("uid=1000(dotriric) gid=1000(dotriric) groupes=1000(dotriric)");
|
||||
break;
|
||||
case c === "pwd":
|
||||
print("/home/dotriric");
|
||||
break;
|
||||
case c === "hostname":
|
||||
print(T.promptHost);
|
||||
break;
|
||||
case c === "ls" || c === "ll" || c === "dir":
|
||||
if (arg === "-la" || arg === "-al" || c === "ll") {
|
||||
print(["total 24", "drwxr-xr-x dotriric dotriric dots .", "drwxr-xr-x root root dots ..", "-rw-r--r-- dotriric dotriric dots .bashrc", "-rw-r--r-- dotriric dotriric dots .profile", "-rw-r--r-- dotriric dotriric dots about.txt", "-rw-r--r-- dotriric dotriric dots projects.txt", "-rw-r--r-- dotriric dotriric dots contact.txt"]);
|
||||
} else {
|
||||
print(["about.txt projects.txt contact.txt .bashrc .profile"]);
|
||||
}
|
||||
break;
|
||||
case c === "uname":
|
||||
print("Linux zektyc 6.1.0 localhost x86_64 GNU/Linux");
|
||||
break;
|
||||
case c === "date":
|
||||
print(new Date().toUTCString().replace("GMT", "UTC"));
|
||||
break;
|
||||
case c === "neofetch" || c === "fetch":
|
||||
printAscii(T.neofetch);
|
||||
break;
|
||||
case c === "cat":
|
||||
switch (true) {
|
||||
case arg.indexOf("about") !== -1:
|
||||
print(T.about);
|
||||
break;
|
||||
case arg.indexOf("project") !== -1:
|
||||
print(T.projects.map(function (l) {
|
||||
var m = l.match(/^\s*(\/[a-z-]+\/?\s*)\s(.*)$/);
|
||||
if (m) return { text: m[1] + " " + m[2], link: m[1].trim() };
|
||||
return l;
|
||||
}));
|
||||
break;
|
||||
case arg.indexOf("contact") !== -1:
|
||||
print(T.contact);
|
||||
break;
|
||||
case arg.indexOf("bashrc") !== -1:
|
||||
print(T.bashrc);
|
||||
break;
|
||||
case arg.indexOf("profile") !== -1:
|
||||
print(T.profile);
|
||||
break;
|
||||
default:
|
||||
print(FR ? "cat: " + (arg || "?") + ": fichier introuvable" : "cat: " + (arg || "?") + ": No such file or directory");
|
||||
}
|
||||
break;
|
||||
case c === "contact" || c === "email" || c === "mail":
|
||||
print(T.contact.map(function (l) {
|
||||
var m = l.match(/^([A-Za-z]+:)\s*(\S+)$/);
|
||||
if (m && m[1] === "Email:") return { text: l, link: "mailto:" + m[2] };
|
||||
if (m && m[1] === "Mastodon:") return { text: l, link: "https://piaille.fr/@zektyc" };
|
||||
return l;
|
||||
}));
|
||||
break;
|
||||
case c === "sudo" || c === "su":
|
||||
print(T.sudo);
|
||||
break;
|
||||
case c === "apt":
|
||||
print(T.apt);
|
||||
break;
|
||||
case c === "exit" || c === "logout" || c === "quit":
|
||||
print(T.shutdown);
|
||||
input.disabled = true;
|
||||
input.setAttribute("placeholder", "");
|
||||
break;
|
||||
case c === "echo":
|
||||
print(arg || "");
|
||||
break;
|
||||
case c === "history":
|
||||
print(history.length ? history : [FR ? "(aucune commande)" : "(no commands)"]);
|
||||
break;
|
||||
default:
|
||||
print(T.unknown(c));
|
||||
}
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
function submit() {
|
||||
var v = input.value;
|
||||
if (v.trim()) history.push(v);
|
||||
histIdx = history.length;
|
||||
runCommand(v);
|
||||
input.value = "";
|
||||
}
|
||||
|
||||
input.addEventListener("keydown", function (e) {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
submit();
|
||||
} else if (e.key === "ArrowUp") {
|
||||
e.preventDefault();
|
||||
if (history.length) {
|
||||
histIdx = Math.max(0, histIdx - 1);
|
||||
input.value = history[histIdx] || "";
|
||||
}
|
||||
} else if (e.key === "ArrowDown") {
|
||||
e.preventDefault();
|
||||
if (history.length) {
|
||||
histIdx = Math.min(history.length, histIdx + 1);
|
||||
input.value = histIdx >= history.length ? "" : history[histIdx];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
root.addEventListener("click", function () {
|
||||
if (!input.disabled) input.focus();
|
||||
});
|
||||
|
||||
(function boot() {
|
||||
printAscii(["", T.boot.join("\n"), ""]);
|
||||
})();
|
||||
|
||||
setTimeout(function () {
|
||||
input.focus();
|
||||
}, 50);
|
||||
})();
|
||||
|
|
@ -91,6 +91,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -91,6 +91,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
56
public/portfolio/en/index.html
Normal file
56
public/portfolio/en/index.html
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="Riric (DotRiric)'s portfolio, founder of Zektyc — an interactive Linux terminal to discover his background, projects and skills." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<title>Portfolio · Zektyc</title>
|
||||
<link rel="canonical" href="https://riricdev.tail5ea5cd.ts.net/en/portfolio" />
|
||||
<link rel="alternate" hreflang="fr" href="https://riricdev.tail5ea5cd.ts.net/fr/portfolio" />
|
||||
<link rel="alternate" hreflang="en" href="https://riricdev.tail5ea5cd.ts.net/en/portfolio" />
|
||||
<link rel="alternate" hreflang="x-default" href="https://riricdev.tail5ea5cd.ts.net/fr/portfolio" />
|
||||
<meta name="theme-color" content="#070a0f" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="Zektyc" />
|
||||
<meta property="og:title" content="Portfolio · Zektyc" />
|
||||
<meta property="og:description" content="Riric (DotRiric), founder of Zektyc — discover him in an interactive Linux terminal." />
|
||||
<meta property="og:url" content="https://riricdev.tail5ea5cd.ts.net/en/portfolio" />
|
||||
<meta property="og:image" content="https://riricdev.tail5ea5cd.ts.net/og-image.png" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:locale:alternate" content="fr_FR" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" href="/css/style.css?v=8" />
|
||||
<link rel="stylesheet" href="/css/portfolio.css?v=1" />
|
||||
</head>
|
||||
<body>
|
||||
<header id="h"></header>
|
||||
|
||||
<main class="portfolio-main" id="main">
|
||||
<div class="portfolio-head">
|
||||
<h1>Portfolio — Riric (DotRiric)</h1>
|
||||
<p>CEO · Founder · Architect — discover my world in a Debian terminal.</p>
|
||||
</div>
|
||||
|
||||
<div class="terminal" id="terminal" role="log" aria-live="polite" aria-label="Interactive terminal: type help">
|
||||
<div class="terminal-bar">
|
||||
<span class="terminal-dots"><i></i><i></i><i></i></span>
|
||||
<span class="terminal-title">dotriric@zektyc: ~</span>
|
||||
</div>
|
||||
<div class="terminal-body"></div>
|
||||
<div class="terminal-input-row">
|
||||
<span class="term-prompt"><span class="t-user">dotriric@zektyc</span><span class="t-path">:~$</span></span>
|
||||
<input class="term-input" type="text" autocomplete="off" autocapitalize="off" spellcheck="false" aria-label="Command" />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
<script src="/js/portfolio.js?v=1" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
56
public/portfolio/index.html
Normal file
56
public/portfolio/index.html
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="Portfolio de Riric (DotRiric), fondateur de Zektyc — un terminal Linux interactif pour découvrir son parcours, ses projets et ses compétences." />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<title>Portfolio · Zektyc</title>
|
||||
<link rel="canonical" href="https://riricdev.tail5ea5cd.ts.net/fr/portfolio" />
|
||||
<link rel="alternate" hreflang="fr" href="https://riricdev.tail5ea5cd.ts.net/fr/portfolio" />
|
||||
<link rel="alternate" hreflang="en" href="https://riricdev.tail5ea5cd.ts.net/en/portfolio" />
|
||||
<link rel="alternate" hreflang="x-default" href="https://riricdev.tail5ea5cd.ts.net/fr/portfolio" />
|
||||
<meta name="theme-color" content="#070a0f" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="Zektyc" />
|
||||
<meta property="og:title" content="Portfolio · Zektyc" />
|
||||
<meta property="og:description" content="Riric (DotRiric), fondateur de Zektyc — découvrez-le dans un terminal Linux interactif." />
|
||||
<meta property="og:url" content="https://riricdev.tail5ea5cd.ts.net/fr/portfolio" />
|
||||
<meta property="og:image" content="https://riricdev.tail5ea5cd.ts.net/og-image.png" />
|
||||
<meta property="og:locale" content="fr_FR" />
|
||||
<meta property="og:locale:alternate" content="en_US" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" href="/css/style.css?v=8" />
|
||||
<link rel="stylesheet" href="/css/portfolio.css?v=1" />
|
||||
</head>
|
||||
<body>
|
||||
<header id="h"></header>
|
||||
|
||||
<main class="portfolio-main" id="main">
|
||||
<div class="portfolio-head">
|
||||
<h1>Portfolio — Riric (DotRiric)</h1>
|
||||
<p>CEO · Fondateur · Architecte — découvrez mon monde dans un terminal Debian.</p>
|
||||
</div>
|
||||
|
||||
<div class="terminal" id="terminal" role="log" aria-live="polite" aria-label="Terminal interactif : tapez help">
|
||||
<div class="terminal-bar">
|
||||
<span class="terminal-dots"><i></i><i></i><i></i></span>
|
||||
<span class="terminal-title">dotriric@zektyc: ~</span>
|
||||
</div>
|
||||
<div class="terminal-body"></div>
|
||||
<div class="terminal-input-row">
|
||||
<span class="term-prompt"><span class="t-user">dotriric@zektyc</span><span class="t-path">:~$</span></span>
|
||||
<input class="term-input" type="text" autocomplete="off" autocapitalize="off" spellcheck="false" aria-label="Commande" />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
<script src="/js/portfolio.js?v=1" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -211,6 +211,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -211,6 +211,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -74,6 +74,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -74,6 +74,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -35,6 +35,13 @@
|
|||
<xhtml:link href="https://riricdev.tail5ea5cd.ts.net/en/licence" hreflang="en" rel="alternate"/>
|
||||
<xhtml:link href="https://riricdev.tail5ea5cd.ts.net/fr/licence" hreflang="x-default" rel="alternate"/>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://riricdev.tail5ea5cd.ts.net/en/portfolio</loc>
|
||||
<lastmod>2026-09-08</lastmod>
|
||||
<xhtml:link href="https://riricdev.tail5ea5cd.ts.net/fr/portfolio" hreflang="fr" rel="alternate"/>
|
||||
<xhtml:link href="https://riricdev.tail5ea5cd.ts.net/en/portfolio" hreflang="en" rel="alternate"/>
|
||||
<xhtml:link href="https://riricdev.tail5ea5cd.ts.net/fr/portfolio" hreflang="x-default" rel="alternate"/>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://riricdev.tail5ea5cd.ts.net/en/privacy-policy</loc>
|
||||
<lastmod>2026-08-14</lastmod>
|
||||
|
|
|
|||
|
|
@ -35,6 +35,13 @@
|
|||
<xhtml:link href="https://riricdev.tail5ea5cd.ts.net/en/licence" hreflang="en" rel="alternate"/>
|
||||
<xhtml:link href="https://riricdev.tail5ea5cd.ts.net/fr/licence" hreflang="x-default" rel="alternate"/>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://riricdev.tail5ea5cd.ts.net/fr/portfolio</loc>
|
||||
<lastmod>2026-09-08</lastmod>
|
||||
<xhtml:link href="https://riricdev.tail5ea5cd.ts.net/fr/portfolio" hreflang="fr" rel="alternate"/>
|
||||
<xhtml:link href="https://riricdev.tail5ea5cd.ts.net/en/portfolio" hreflang="en" rel="alternate"/>
|
||||
<xhtml:link href="https://riricdev.tail5ea5cd.ts.net/fr/portfolio" hreflang="x-default" rel="alternate"/>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://riricdev.tail5ea5cd.ts.net/fr/privacy-policy</loc>
|
||||
<lastmod>2026-08-14</lastmod>
|
||||
|
|
|
|||
|
|
@ -238,6 +238,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -238,6 +238,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -62,6 +62,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -107,6 +107,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -107,6 +107,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -122,6 +122,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -128,6 +128,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -203,6 +203,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -211,6 +211,6 @@
|
|||
<footer id="f"></footer>
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -31,7 +31,7 @@ function layout(title: string, description: string, body: string, sidebar: strin
|
|||
<link rel="stylesheet" href="/css/docs.css?v=3" />
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
<script src="/js/docs-playground.js?v=1" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -618,7 +618,7 @@ function teapotPage(lang: "fr" | "en"): string {
|
|||
<link rel="stylesheet" href="/css/teapot.css?v=1" />
|
||||
<script src="/js/theme.js" defer></script>
|
||||
<script src="/js/global.js?v=1" defer></script>
|
||||
<script src="/js/components.js?v=7" defer></script>
|
||||
<script src="/js/components.js?v=8" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<header id="h"></header>
|
||||
|
|
@ -836,6 +836,7 @@ function writeSitemaps(articles: Article[]) {
|
|||
{ fr: "/fr/tos", en: "/en/tos", lm: "2026-08-14" },
|
||||
{ fr: "/fr/privacy-policy", en: "/en/privacy-policy", lm: "2026-08-14" },
|
||||
{ fr: "/fr/licence", en: "/en/licence", lm: "2026-09-08" },
|
||||
{ fr: "/fr/portfolio", en: "/en/portfolio", lm: "2026-09-08" },
|
||||
{ fr: "/fr/blog/", en: "/en/blog/", lm: "2026-08-14" },
|
||||
{ fr: "/fr/fingerprint-test", en: "/en/fingerprint-test", lm: "2026-08-16" },
|
||||
{ fr: "/fr/projets/", en: "/en/projets/", lm: "2026-08-17" },
|
||||
|
|
|
|||
Loading…
Reference in a new issue