chore: support for vegvisir 3

This commit is contained in:
Victor Westerlund 2024-09-13 14:40:52 +02:00
parent 43ddf1fdf6
commit 8967049eeb
27 changed files with 230 additions and 228 deletions

View file

@ -5,7 +5,7 @@
--color-accent: rgb(var(--primer-color-accent)); --color-accent: rgb(var(--primer-color-accent));
} }
main { vv-shell {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--padding); gap: var(--padding);
@ -15,7 +15,7 @@ main {
/* ## Divider */ /* ## Divider */
main > hr { vv-shell > hr {
border-color: rgba(255, 255, 255, .1); border-color: rgba(255, 255, 255, .1);
} }

View file

@ -5,7 +5,7 @@
--color-accent: rgb(var(--primer-color-accent)); --color-accent: rgb(var(--primer-color-accent));
} }
main { vv-shell {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--padding); gap: var(--padding);

View file

@ -5,7 +5,7 @@
--color-accent: rgb(var(--primer-color-accent)); --color-accent: rgb(var(--primer-color-accent));
} }
main { vv-shell {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--padding); gap: var(--padding);

View file

@ -5,7 +5,7 @@
--color-accent: rgb(var(--primer-color-accent)); --color-accent: rgb(var(--primer-color-accent));
} }
main { vv-shell {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -14,7 +14,7 @@ main {
/* # Sections */ /* # Sections */
main > svg { vv-shell > svg {
margin: var(--padding) 0; margin: var(--padding) 0;
} }

View file

@ -6,7 +6,7 @@ header {
backdrop-filter: unset; backdrop-filter: unset;
} }
main { vv-shell {
max-width: unset; max-width: unset;
display: grid; display: grid;
justify-items: center; justify-items: center;

View file

@ -4,18 +4,18 @@ body[vv-top-page="/"]::before {
opacity: 0; opacity: 0;
} }
/* # Main styles */ /* # vv-shell styles */
/* ## Picture */ /* ## Picture */
main { vv-shell {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
flex-direction: column-reverse; flex-direction: column-reverse;
} }
main img { vv-shell img {
margin: auto; margin: auto;
width: 25vh; width: 25vh;
pointer-events: none; pointer-events: none;
@ -171,14 +171,14 @@ splash::after {
/* # Size quries */ /* # Size quries */
@media (min-width: 900px) { @media (min-width: 900px) {
main { vv-shell {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
justify-items: center; justify-items: center;
align-items: center; align-items: center;
} }
main img { vv-shell img {
width: 35vh; width: 35vh;
} }
} }

View file

@ -21,7 +21,7 @@ section.search {
margin-bottom: calc(var(--padding) * 2); margin-bottom: calc(var(--padding) * 2);
} }
main[vv-page="/search"] > section.search { vv-shell[vv-page="/search"] > section.search {
display: flex; display: flex;
} }

View file

@ -5,7 +5,7 @@
--color-accent: rgb(var(--primer-color-accent)); --color-accent: rgb(var(--primer-color-accent));
} }
main { vv-shell {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--padding); gap: var(--padding);

View file

@ -260,21 +260,21 @@ header.searchboxActive searchbox {
transform: rotateX(0); transform: rotateX(0);
} }
/* ## Main */ /* ## vv-shell */
main { vv-shell {
position: relative; position: relative;
padding: calc(var(--padding) * 1.5); padding: calc(var(--padding) * 1.5);
width: 100%; width: 100%;
max-width: 1000px; max-width: 1000px;
} }
main > * { vv-shell > * {
transition: 100ms opacity; transition: 100ms opacity;
opacity: 1; opacity: 1;
} }
main.loading > * { vv-shell.loading > * {
opacity: 0; opacity: 0;
} }

View file

@ -1,5 +1,3 @@
new vv.Interactions("about");
const randomIntFromInterval = (min, max) => { const randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min); return Math.floor(Math.random() * (max - min + 1) + min);
} }

View file

@ -1 +0,0 @@
new vv.Interactions("battlestation-retired");

View file

@ -1,19 +1,19 @@
new vv.Interactions("battlestation", { import { Elevent } from "/assets/js/._Elevent.mjs";
toggleGroup: (event) => {
// Collapse self if already active and current target
if (event.target.classList.contains("active")) {
return event.target.classList.remove("active");
}
// Collapse all and open current target new Elevent("click", document.querySelectorAll(".group"), (event) => {
[...event.target.closest(".specs").querySelectorAll(".group")].forEach(element => element.classList.remove("active")); // Collapse self if already active and current target
event.target.classList.add("active"); if (event.target.classList.contains("active")) {
}, return event.target.classList.remove("active");
setSpecActive: (event) => {
event.target.classList.add("active");
event.target.addEventListener("mouseleave", () => event.target.classList.remove("active"));
} }
// Collapse all and open current target
[...event.target.closest(".specs").querySelectorAll(".group")].forEach(element => element.classList.remove("active"));
event.target.classList.add("active");
});
new Elevent("click", document.querySelectorAll(".spec"), (event) => {
event.target.classList.add("active");
event.target.addEventListener("mouseleave", () => event.target.classList.remove("active"));
}); });
// Bind hover listeners for components in the SVGs // Bind hover listeners for components in the SVGs

View file

@ -1,108 +1,108 @@
const EMAIL_CPY_ANIM_DUR_MSECONDS = 1000; import { Elevent } from "/assets/js/._Elevent.mjs";
// Run email copied splash animation // Click to copy email button
const emailCopiedAnimation = () => { {
const CONFETTI_COUNT = 40; const EMAIL_CPY_ANIM_DUR_MSECONDS = 1000;
const CONFETTI_SCALE_PIXELS = 300;
const randomIntFromInterval = (min, max) => { // Run email copied splash animation
return Math.floor(Math.random() * (max - min + 1) + min) const emailCopiedAnimation = () => {
const CONFETTI_COUNT = 40;
const CONFETTI_SCALE_PIXELS = 300;
const randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min)
}
// Create new splash element
const splashElement = document.createElement("splash");
splashElement.innerText = "copied!";
// Set inline display to none to hide this element on pages where the splash element has no override styles defined.
splashElement.style.display = "none";
// Array of box-shadow strings as "confetti"
const confetti = [];
// Generate random confetti
for (let i = 0; i < CONFETTI_COUNT; i++) {
// Random confetti position
const x = randomIntFromInterval(CONFETTI_SCALE_PIXELS * -1, CONFETTI_SCALE_PIXELS);
const y = randomIntFromInterval(CONFETTI_SCALE_PIXELS * -1, CONFETTI_SCALE_PIXELS);
// Random confetti RGB color
const rgb = [
randomIntFromInterval(0, 255),
randomIntFromInterval(0, 255),
randomIntFromInterval(0, 255)
];
// Interpolate random values and append to outer confetti array
confetti.push(`${x}px ${y}px 0 rgb(${rgb.join(",")})`);
}
// Set CSS variable on splash element that in turn will be used by pseudo-element
splashElement.style.setProperty("--confetti", confetti.join(","));
// Start animation by appending the created element to the document body
document.body.appendChild(splashElement);
// Run hide animation
setTimeout(() => {
splashElement.classList.add("hide");
// Selfdestruct element when hide animation finishes
setTimeout(() => splashElement.remove(), 400);
}, EMAIL_CPY_ANIM_DUR_MSECONDS + 100);
} }
// Create new splash element new Elevent("click", document.querySelector(".email"), async () => {
const splashElement = document.createElement("splash");
splashElement.innerText = "copied!";
// Set inline display to none to hide this element on pages where the splash element has no override styles defined.
splashElement.style.display = "none";
// Array of box-shadow strings as "confetti"
const confetti = [];
// Generate random confetti
for (let i = 0; i < CONFETTI_COUNT; i++) {
// Random confetti position
const x = randomIntFromInterval(CONFETTI_SCALE_PIXELS * -1, CONFETTI_SCALE_PIXELS);
const y = randomIntFromInterval(CONFETTI_SCALE_PIXELS * -1, CONFETTI_SCALE_PIXELS);
// Random confetti RGB color
const rgb = [
randomIntFromInterval(0, 255),
randomIntFromInterval(0, 255),
randomIntFromInterval(0, 255)
];
// Interpolate random values and append to outer confetti array
confetti.push(`${x}px ${y}px 0 rgb(${rgb.join(",")})`);
}
// Set CSS variable on splash element that in turn will be used by pseudo-element
splashElement.style.setProperty("--confetti", confetti.join(","));
// Start animation by appending the created element to the document body
document.body.appendChild(splashElement);
// Run hide animation
setTimeout(() => {
splashElement.classList.add("hide");
// Selfdestruct element when hide animation finishes
setTimeout(() => splashElement.remove(), 400);
}, EMAIL_CPY_ANIM_DUR_MSECONDS + 100);
}
new vv.Interactions("index", {
// Copy email address to clipboard
copyEmail: async () => {
try { try {
await navigator.clipboard.writeText("victor@vlw.se"); await navigator.clipboard.writeText("victor@vlw.se");
// Run "email copied" animation! // Run "email copied" animation!
emailCopiedAnimation(); emailCopiedAnimation();
// NOTE: I don't know, spamming the button is kinda fun // NOTE: I don't know, spamming the button is kinda fun
// Prevent interactions with the copy email elements while the animation is running // Prevent interactions with the copy email elements while the animation is running
/*[...document.querySelectorAll("[vv-call='copyEmail']")].forEach(element => { /*[...document.querySelectorAll("[vv-call='copyEmail']")].forEach(element => {
//element.classList.add("lock"); //element.classList.add("lock");
setTimeout(() => element.classList.remove("lock"), EMAIL_CPY_ANIM_DUR_MSECONDS); setTimeout(() => element.classList.remove("lock"), EMAIL_CPY_ANIM_DUR_MSECONDS);
});*/ });*/
} catch (error) { } catch (error) {
console.error(error.message); console.error(error.message);
} }
}, });
// Open the fullscreen menu }
openMenu: () => document.querySelector("menu").classList.add("active"),
// Close the fullscreen menu
closeMenu: () => document.querySelector("menu").classList.remove("active")
});
// Change site accent color on hover of menu items // Change site accent color on hover of menu items
if (window.matchMedia("(hover: hover)")) { {
// Update root CSS variables if (window.matchMedia("(hover: hover)")) {
const updateColor = (rgb = null, hue = 0) => { // Update root CSS variables
if (!rgb) { const updateColor = (rgb = null, hue = 0) => {
document.documentElement.style.removeProperty("--hue-accent"); if (!rgb) {
document.documentElement.style.removeProperty("--primer-color-accent"); document.documentElement.style.removeProperty("--hue-accent");
document.documentElement.style.removeProperty("--color-accent"); document.documentElement.style.removeProperty("--primer-color-accent");
document.documentElement.style.removeProperty("--color-accent");
return; return;
} }
document.documentElement.style.setProperty("--hue-accent", `${hue}deg`); document.documentElement.style.setProperty("--hue-accent", `${hue}deg`);
document.documentElement.style.setProperty("--primer-color-accent", `${rgb}`); document.documentElement.style.setProperty("--primer-color-accent", `${rgb}`);
// Compiled color variable must to be updated to receive the new RGB values // Compiled color variable must to be updated to receive the new RGB values
document.documentElement.style.setProperty("--color-accent", "rgb(var(--primer-color-accent)"); document.documentElement.style.setProperty("--color-accent", "rgb(var(--primer-color-accent)");
}; };
[...document.querySelectorAll("menu li")].forEach(element => { [...document.querySelectorAll("menu li")].forEach(element => {
// Change site accent color to RGB and HUE rotation defined in element dataset // Change site accent color to RGB and HUE rotation defined in element dataset
element.addEventListener("mouseenter", (event) => updateColor(event.target.dataset.rgb, event.target.dataset.hue)); element.addEventListener("mouseenter", (event) => updateColor(event.target.dataset.rgb, event.target.dataset.hue));
// Reset initial accent color and hues // Reset initial accent color and hues
element.addEventListener("mouseleave", () => updateColor()); element.addEventListener("mouseleave", () => updateColor());
}); });
// Reset color on navigation // Reset color on navigation
document.querySelector(vv._env.MAIN).addEventListener(vv.Navigation.events.LOADED, () => updateColor(), { once: true }); vv.Navigation.rootShellElement.addEventListener(vv.Navigation.EVENTS.STARTED, () => updateColor(), { once: true });
} }
}

View file

@ -1 +0,0 @@
new vv.Interactions("search");

View file

@ -1 +0,0 @@
new vv.Interactions("work");

49
assets/js/document.js → assets/js/shells/document.js Executable file → Normal file
View file

@ -1,6 +1,16 @@
new vv.Interactions("document", { import { Elevent } from "/assets/js/._Elevent.mjs";
navigateHome: () => new vv.Navigation("/").navigate(),
closeSearchbox: () => { // Handle search box open/close buttons
{
// Open search box
new Elevent("click", document.querySelector(".searchbox-open"), () => {
document.querySelector("header").classList.add("searchboxActive");
// Select searchbox inner input element
document.querySelector("searchbox input").focus();
});
// Close searchbox
new Elevent("click", document.querySelector(".searchbox-close"), () => {
// Disable search button interaction while animation is running // Disable search button interaction while animation is running
// This is required to prevent conflicts with the :hover "peak" transformation // This is required to prevent conflicts with the :hover "peak" transformation
const searchButtonElement = document.querySelector("header button.search"); const searchButtonElement = document.querySelector("header button.search");
@ -11,28 +21,27 @@ new vv.Interactions("document", {
// Wait for the transform animation to finish // Wait for the transform animation to finish
setTimeout(() => searchButtonElement.style.removeProperty("pointer-events"), transformDuration); setTimeout(() => searchButtonElement.style.removeProperty("pointer-events"), transformDuration);
},
openSearchbox: () => {
document.querySelector("header").classList.add("searchboxActive");
// Select searchbox inner input element
document.querySelector("searchbox input").focus();
}
});
// Crossfade pages on navigation
{
const mainElement = document.querySelector(vv._env.MAIN);
mainElement.addEventListener(vv.Navigation.events.LOADING, () => {
mainElement.classList.add("loading");
}); });
}
mainElement.addEventListener(vv.Navigation.events.LOADED, () => { // Root shell navigation event handlers
// Close searchbox on main page navigation {
const classNameLoading = "loading";
// Top navigation started
new Elevent(vv.Navigation.EVENTS.STARTED, vv.Navigation.rootShellElement, () => {
vv.Navigation.rootShellElement.classList.add(classNameLoading);
// Close searchbox on vv-shell page navigation
document.querySelector("header").classList.remove("searchboxActive"); document.querySelector("header").classList.remove("searchboxActive");
// Wait 200ms for the page fade-in animation to finish // Wait 200ms for the page fade-in animation to finish
setTimeout(() => mainElement.classList.remove("loading"), 200); setTimeout(() => vv.Navigation.rootShellElement.classList.remove("loading"), 200);
});
// Top navigation finished
new Elevent(vv.Navigation.EVENTS.FINISHED, vv.Navigation.rootShellElement, () => {
vv.Navigation.rootShellElement.classList.remove(classNameLoading);
}); });
} }

View file

@ -1,6 +0,0 @@
<style><?= VV::css("pages/error") ?></style>
<canvas></canvas>
<section class="error">
<h1 glitch-text><span>4</span><span>0</span><span>4</span></h1>
</section>
<script type="module"><?= VV::js("pages/error") ?></script>

6
pages/about.php → public/about.php Executable file → Normal file
View file

@ -1,4 +1,4 @@
<style><?= VV::css("pages/about") ?></style> <style><?= VV::css("assets/css/pages/about") ?></style>
<section class="intro"> <section class="intro">
<h2 aria-hidden="true">Hi, I'm</h2> <h2 aria-hidden="true">Hi, I'm</h2>
<h1>Victor Westerlund</h1> <h1>Victor Westerlund</h1>
@ -31,7 +31,7 @@
</section> </section>
<hr> <hr>
<section class="version"> <section class="version">
<p>website version: <?= VV::include("pages/about/version") ?></p> <p>website version: <?= VV::include("public/about/version") ?></p>
</section> </section>
<div class="interests" aria-hidden="true"> <div class="interests" aria-hidden="true">
@ -49,4 +49,4 @@
<p>photography</p> <p>photography</p>
<p>videography</p> <p>videography</p>
</div> </div>
<script><?= VV::js("pages/about") ?></script> <script type="module"><?= VV::js("assets/js/pages/about") ?></script>

View file

@ -1,4 +1,4 @@
<style><?= VV::css("pages/about/battlestation-retired") ?></style> <style><?= VV::css("assets/css/pages/about/battlestation-retired") ?></style>
<section class="title"> <section class="title">
<h1>Retired components</h1> <h1>Retired components</h1>
<p>I'd be happy to send you any component that you find here for "free". The only thing I ask in return is that you pay for shipping.</p> <p>I'd be happy to send you any component that you find here for "free". The only thing I ask in return is that you pay for shipping.</p>
@ -33,4 +33,4 @@
<button class="inline solid">Contact me</button> <button class="inline solid">Contact me</button>
</a> </a>
</section> </section>
<script><?= VV::js("pages/about/battlestation-retired") ?></script> <script><?= VV::js("assets/js/pages/about/battlestation-retired") ?></script>

View file

@ -25,26 +25,26 @@
MbStorageSlotFormfactorEnum MbStorageSlotFormfactorEnum
}; };
require_once Path::root("src/client/API.php"); require_once VV::root("src/client/API.php");
require_once Path::root("api/src/Endpoints.php"); require_once VV::root("api/src/Endpoints.php");
// Load hardware database models // Load hardware database models
require_once Path::root("api/src/databases/models/Battlestation/Mb.php"); require_once VV::root("api/src/databases/models/Battlestation/Mb.php");
require_once Path::root("api/src/databases/models/Battlestation/Cpu.php"); require_once VV::root("api/src/databases/models/Battlestation/Cpu.php");
require_once Path::root("api/src/databases/models/Battlestation/Gpu.php"); require_once VV::root("api/src/databases/models/Battlestation/Gpu.php");
require_once Path::root("api/src/databases/models/Battlestation/Psu.php"); require_once VV::root("api/src/databases/models/Battlestation/Psu.php");
require_once Path::root("api/src/databases/models/Battlestation/Dram.php"); require_once VV::root("api/src/databases/models/Battlestation/Dram.php");
require_once Path::root("api/src/databases/models/Battlestation/Storage.php"); require_once VV::root("api/src/databases/models/Battlestation/Storage.php");
require_once Path::root("api/src/databases/models/Battlestation/Chassis.php"); require_once VV::root("api/src/databases/models/Battlestation/Chassis.php");
// Load hardware config database models // Load hardware config database models
require_once Path::root("api/src/databases/models/Battlestation/Config/MbPsu.php"); require_once VV::root("api/src/databases/models/Battlestation/Config/MbPsu.php");
require_once Path::root("api/src/databases/models/Battlestation/Config/MbGpu.php"); require_once VV::root("api/src/databases/models/Battlestation/Config/MbGpu.php");
require_once Path::root("api/src/databases/models/Battlestation/Config/MbDram.php"); require_once VV::root("api/src/databases/models/Battlestation/Config/MbDram.php");
require_once Path::root("api/src/databases/models/Battlestation/Config/Config.php"); require_once VV::root("api/src/databases/models/Battlestation/Config/Config.php");
require_once Path::root("api/src/databases/models/Battlestation/Config/MbStorage.php"); require_once VV::root("api/src/databases/models/Battlestation/Config/MbStorage.php");
require_once Path::root("api/src/databases/models/Battlestation/Config/ChassisMb.php"); require_once VV::root("api/src/databases/models/Battlestation/Config/ChassisMb.php");
require_once Path::root("api/src/databases/models/Battlestation/Config/MbCpuCooler.php"); require_once VV::root("api/src/databases/models/Battlestation/Config/MbCpuCooler.php");
const GIGA = 0x3B9ACA00; const GIGA = 0x3B9ACA00;
const MEGA = 0xF4240; const MEGA = 0xF4240;
@ -55,7 +55,7 @@
$config = $api->call(Endpoints::BATTLESTATION->value)->get(); $config = $api->call(Endpoints::BATTLESTATION->value)->get();
?> ?>
<style><?= VV::css("pages/about/battlestation") ?></style> <style><?= VV::css("assets/css/pages/about/battlestation") ?></style>
<?php if ($config->ok): ?> <?php if ($config->ok): ?>
<section class="title"> <section class="title">
<h1>Battle&shy;stations</h1> <h1>Battle&shy;stations</h1>
@ -93,12 +93,12 @@
data-drives-twodotfive="<?= count(array_keys(array_column($motherboard["storage"], MbStorageModel::SLOT_FORMFACTOR->value), MbStorageSlotFormfactorEnum::TWODOTFIVE->value)) ?>" data-drives-twodotfive="<?= count(array_keys(array_column($motherboard["storage"], MbStorageModel::SLOT_FORMFACTOR->value), MbStorageSlotFormfactorEnum::TWODOTFIVE->value)) ?>"
data-drives-threedotfive="<?= count(array_keys(array_column($motherboard["storage"], MbStorageModel::SLOT_FORMFACTOR->value), MbStorageSlotFormfactorEnum::THREEDOTFIVE->value)) ?>" data-drives-threedotfive="<?= count(array_keys(array_column($motherboard["storage"], MbStorageModel::SLOT_FORMFACTOR->value), MbStorageSlotFormfactorEnum::THREEDOTFIVE->value)) ?>"
> >
<?= VV::media("battlestation.svg") ?> <?= VV::embed("assets/media/battlestation.svg") ?>
<div class="specs"> <div class="specs">
<?php // Show motherboard details ?> <?php // Show motherboard details ?>
<?php if ($motherboard): ?> <?php if ($motherboard): ?>
<div vv="battlestation" vv-call="setSpecActive" data-target="mb" class="spec"> <div data-target="mb" class="spec">
<p>Motherboard</p> <p>Motherboard</p>
<h3><?= $motherboard[MbModel::VENDOR_NAME->value] ?> <span><?= $motherboard[MbModel::VENDOR_MODEL->value] ?></span></h3> <h3><?= $motherboard[MbModel::VENDOR_NAME->value] ?> <span><?= $motherboard[MbModel::VENDOR_MODEL->value] ?></span></h3>
<div> <div>
@ -149,7 +149,7 @@
ChassisModel::ID->value => $mb_chassis[ChassisMbModel::REF_CHASSIS_ID->value] ChassisModel::ID->value => $mb_chassis[ChassisMbModel::REF_CHASSIS_ID->value]
])->get()->json()[0]; ?> ])->get()->json()[0]; ?>
<div vv="battlestation" vv-call="setSpecActive" data-target="case" class="spec"> <div data-target="case" class="spec">
<p>Case</p> <p>Case</p>
<h3><?= $case[ChassisModel::VENDOR_NAME->value] ?> <span><?= $case[ChassisModel::VENDOR_MODEL->value] ?></span></h3> <h3><?= $case[ChassisModel::VENDOR_NAME->value] ?> <span><?= $case[ChassisModel::VENDOR_MODEL->value] ?></span></h3>
<div> <div>
@ -192,7 +192,7 @@
CpuModel::ID->value => $mb_cpu[MbCpuCoolerModel::REF_CPU_ID->value] CpuModel::ID->value => $mb_cpu[MbCpuCoolerModel::REF_CPU_ID->value]
])->get()->json()[0]; ?> ])->get()->json()[0]; ?>
<div vv="battlestation" vv-call="setSpecActive" data-target="cpu" class="spec"> <div data-target="cpu" class="spec">
<p>CPU</p> <p>CPU</p>
<h3><?= $cpu[CpuModel::VENDOR_NAME->value] ?> <span><?= $cpu[CpuModel::VENDOR_MODEL->value] ?></span></h3> <h3><?= $cpu[CpuModel::VENDOR_NAME->value] ?> <span><?= $cpu[CpuModel::VENDOR_MODEL->value] ?></span></h3>
<div> <div>
@ -257,7 +257,7 @@
GpuModel::ID->value => $mb_gpu[MbGpuModel::REF_GPU_ID->value] GpuModel::ID->value => $mb_gpu[MbGpuModel::REF_GPU_ID->value]
])->get()->json()[0]; ?> ])->get()->json()[0]; ?>
<div vv="battlestation" vv-call="setSpecActive" data-target="gpu" class="spec"> <div data-target="gpu" class="spec">
<p>GPU</p> <p>GPU</p>
<h3><?= $gpu[GpuModel::VENDOR_NAME->value] ?> <span><?= $gpu[GpuModel::VENDOR_CHIP_MODEL->value] ?></span></h3> <h3><?= $gpu[GpuModel::VENDOR_NAME->value] ?> <span><?= $gpu[GpuModel::VENDOR_CHIP_MODEL->value] ?></span></h3>
<div> <div>
@ -304,7 +304,7 @@
PsuModel::ID->value => $mb_psu[MbPsuModel::REF_PSU_ID->value] PsuModel::ID->value => $mb_psu[MbPsuModel::REF_PSU_ID->value]
])->get()->json()[0]; ?> ])->get()->json()[0]; ?>
<div vv="battlestation" vv-call="setSpecActive" data-target="psu" class="spec"> <div data-target="psu" class="spec">
<p>PSU</p> <p>PSU</p>
<h3><?= $psu[PsuModel::VENDOR_NAME->value] ?> <span><?= $psu[PsuModel::VENDOR_MODEL->value] ?></span> <span><?= $psu[PsuModel::POWER->value] ?>W</span></h3> <h3><?= $psu[PsuModel::VENDOR_NAME->value] ?> <span><?= $psu[PsuModel::VENDOR_MODEL->value] ?></span> <span><?= $psu[PsuModel::POWER->value] ?>W</span></h3>
<div> <div>
@ -343,9 +343,9 @@
</div> </div>
<?php endforeach; ?> <?php endforeach; ?>
<div vv="battlestation" vv-call="toggleGroup" class="group"> <div class="group">
<p>DRAM</p> <p>DRAM</p>
<?= VV::media("icons/chevron.svg") ?> <?= VV::embed("assets/media/icons/chevron.svg") ?>
</div> </div>
<div class="collection"> <div class="collection">
@ -357,7 +357,7 @@
DramModel::ID->value => $mb_dram[MbDramModel::REF_DRAM_ID->value] DramModel::ID->value => $mb_dram[MbDramModel::REF_DRAM_ID->value]
])->get()->json()[0]; ?> ])->get()->json()[0]; ?>
<div vv="battlestation" vv-call="setSpecActive" data-target="dram" class="spec"> <div data-target="dram" class="spec">
<p>DRAM - <?= $dram[DramModel::TECHNOLOGY->value] ?></p> <p>DRAM - <?= $dram[DramModel::TECHNOLOGY->value] ?></p>
<h3><?= $dram[DramModel::VENDOR_NAME->value] ?> <h3><?= $dram[DramModel::VENDOR_NAME->value] ?>
<span><?= $dram[DramModel::CAPACITY->value] / GIGA ?>GB</span> <span><?= $dram[DramModel::CAPACITY->value] / GIGA ?>GB</span>
@ -422,9 +422,9 @@
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<div vv="battlestation" vv-call="toggleGroup" class="group"> <div class="group">
<p>Storage</p> <p>Storage</p>
<?= VV::media("icons/chevron.svg") ?> <?= VV::embed("assets/media/icons/chevron.svg") ?>
</div> </div>
<div class="collection"> <div class="collection">
@ -436,7 +436,7 @@
StorageModel::ID->value => $mb_storage[MbStorageModel::REF_STORAGE_ID->value] StorageModel::ID->value => $mb_storage[MbStorageModel::REF_STORAGE_ID->value]
])->get()->json()[0]; ?> ])->get()->json()[0]; ?>
<div vv="battlestation" vv-call="setSpecActive" data-target="drive" class="spec"> <div data-target="drive" class="spec">
<p><?= $storage[StorageModel::DISK_FORMFACTOR->value] ?> <?= $storage[StorageModel::DISK_TYPE->value] ?></p> <p><?= $storage[StorageModel::DISK_FORMFACTOR->value] ?> <?= $storage[StorageModel::DISK_TYPE->value] ?></p>
<h3> <h3>
<?= $storage[StorageModel::VENDOR_NAME->value] ?> <?= $storage[StorageModel::VENDOR_NAME->value] ?>
@ -493,4 +493,4 @@
</section> </section>
<?php endforeach; ?> <?php endforeach; ?>
<?php endif; ?> <?php endif; ?>
<script><?= VV::js("pages/about/battlestation") ?></script> <script type="module"><?= VV::js("assets/js/pages/about/battlestation") ?></script>

4
pages/about/version.php → public/about/version.php Executable file → Normal file
View file

@ -6,11 +6,9 @@
of this website should always track the master branch and pull the latest HEAD of this website should always track the master branch and pull the latest HEAD
without any exceptions. without any exceptions.
*/ */
use Vegvisir\Path;
// Get tags from local git folder // Get tags from local git folder
$dir = scandir(Path::root(".git/refs/tags")); $dir = scandir(VV::root(".git/refs/tags"));
// Get current version number from latest tag // Get current version number from latest tag
$version = end($dir); $version = end($dir);

22
pages/contact.php → public/contact.php Executable file → Normal file
View file

@ -7,37 +7,37 @@
use VLW\API\Databases\VLWdb\Models\Messages\MessagesModel; use VLW\API\Databases\VLWdb\Models\Messages\MessagesModel;
require_once Path::root("src/client/API.php"); require_once VV::root("src/client/API.php");
require_once Path::root("api/src/Endpoints.php"); require_once VV::root("api/src/Endpoints.php");
require_once Path::root("api/src/databases/models/Messages/Messages.php"); require_once VV::root("api/src/databases/models/Messages/Messages.php");
// Connect to VLW API // Connect to VLW API
$api = new API(); $api = new API();
?> ?>
<style><?= VV::css("pages/contact") ?></style> <style><?= VV::css("assets/css/pages/contact") ?></style>
<section> <section>
<h1>Let's chat</h1> <h1>Let's chat</h1>
<p>The best way to get in touch is by email, or with the form on this page. I will try to reply as quickly as possible, probably within a few hours. The time is <i><?= (new DateTime("now", new DateTimeZone($_ENV["time"]["date_time_zone"])))->format("h:i a") ?></i> in Sweden right now.</p> <p>The best way to get in touch is by email, or with the form on this page. I will try to reply as quickly as possible, probably within a few hours. The time is <i><?= (new DateTime("now", new DateTimeZone($_ENV["time"]["date_time_zone"])))->format("h:i a") ?></i> in Sweden right now.</p>
</section> </section>
<section class="social"> <section class="social">
<a href="mailto:victor@vlw.se"><social> <a href="mailto:victor@vlw.se"><social>
<?= VV::media("icons/email.svg") ?> <?= VV::embed("assets/media/icons/email.svg") ?>
<p>e-mail</p> <p>e-mail</p>
</social></a> </social></a>
<a href="https://mastodon.social/@vlwone"><social> <a href="https://mastodon.social/@vlwone"><social>
<?= VV::media("icons/mastodon.svg") ?> <?= VV::embed("assets/media/icons/mastodon.svg") ?>
<p>mastodon</p> <p>mastodon</p>
</social></a> </social></a>
<a href="https://web.libera.chat/#vlw.se"><social> <a href="https://web.libera.chat/#vlw.se"><social>
<?= VV::media("icons/libera.svg") ?> <?= VV::embed("assets/media/icons/libera.svg") ?>
<p>libera.chat</p> <p>libera.chat</p>
</social></a> </social></a>
</section> </section>
<?= VV::media("line.svg") ?> <?= VV::embed("assets/media/line.svg") ?>
<section class="pgp"> <section class="pgp">
<?= VV::media("icons/pin.svg") ?> <?= VV::embed("assets/media/icons/pin.svg") ?>
<h3>encrypt your message with my OpenPGP key.</h3> <h3>encrypt your message with my OpenPGP key.</h3>
<p>my key is also listed on the <a href="https://keys.openpgp.org/search?q=victor%40vlw.se" target="_blank" rel="noopener noreferer">openPGP key server</a> for victor@vlw.se so your e-mail client can automatically retreive it if supported.</p> <p>my key is also listed on the <a href="https://keys.openpgp.org/search?q=victor%40vlw.se" target="_blank" rel="noopener noreferer">openPGP key server</a> for victor@vlw.se so your e-mail client can automatically retreive it if supported.</p>
<div class="buttons"> <div class="buttons">
@ -45,7 +45,7 @@
<a href="https://emailselfdefense.fsf.org/en/" target="_blank" rel="noopener noreferer"><button class="inline">more info</button></a> <a href="https://emailselfdefense.fsf.org/en/" target="_blank" rel="noopener noreferer"><button class="inline">more info</button></a>
</div> </div>
</section> </section>
<?= VV::media("line.svg") ?> <?= VV::embed("assets/media/line.svg") ?>
<?php // Send message on POST request ?> <?php // Send message on POST request ?>
<?php if ($_SERVER["REQUEST_METHOD"] === "POST"): ?> <?php if ($_SERVER["REQUEST_METHOD"] === "POST"): ?>
@ -88,4 +88,4 @@
</form> </form>
</section> </section>
<script><?= VV::js("pages/contact") ?></script> <script><?= VV::js("assets/js/pages/contact") ?></script>

6
public/error.php Normal file
View file

@ -0,0 +1,6 @@
<style><?= VV::css("assets/css/pages/error") ?></style>
<canvas></canvas>
<section class="error">
<h1 glitch-text><span>4</span><span>0</span><span>4</span></h1>
</section>
<script type="module"><?= VV::js("assets/js/pages/error") ?></script>

10
pages/index.php → public/index.php Executable file → Normal file
View file

@ -7,20 +7,20 @@
} }
?> ?>
<style><?= VV::css("pages/index") ?></style> <style><?= VV::css("assets/css/pages/index") ?></style>
<div class="menu"> <div class="menu">
<?= VV::media("line.svg") ?> <?= VV::embed("assets/media/line.svg") ?>
<menu> <menu>
<a href="/work" vv="index" vv-call="navigate"><li data-rgb="<?= RGB::WORK->value ?>" data-hue="90">work</li></a> <a href="/work" vv="index" vv-call="navigate"><li data-rgb="<?= RGB::WORK->value ?>" data-hue="90">work</li></a>
<a href="/about" vv="index" vv-call="navigate"><li data-rgb="<?= RGB::ABOUT->value ?>" data-hue="390">about</li></a> <a href="/about" vv="index" vv-call="navigate"><li data-rgb="<?= RGB::ABOUT->value ?>" data-hue="390">about</li></a>
<a href="/contact" vv="index" vv-call="navigate"><li data-rgb="<?= RGB::CONTACT->value ?>" data-hue="200">contact</li></a> <a href="/contact" vv="index" vv-call="navigate"><li data-rgb="<?= RGB::CONTACT->value ?>" data-hue="200">contact</li></a>
</menu> </menu>
<?= VV::media("line.svg") ?> <?= VV::embed("assets/media/line.svg") ?>
<button class="email" vv="index" vv-call="copyEmail"> <button class="email">
<p>victor@vlw.se</p> <p>victor@vlw.se</p>
<p class="cta">to copy</p> <p class="cta">to copy</p>
</button> </button>
</div> </div>
<img src="/assets/media/gazing.jpg" alt="A portrait of Victor with a pair of cartoon glasses drawn in the shape of two V's over his eyes"/> <img src="/assets/media/gazing.jpg" alt="A portrait of Victor with a pair of cartoon glasses drawn in the shape of two V's over his eyes"/>
<script><?= VV::js("pages/index") ?></script> <script type="module"><?= VV::js("assets/js/pages/index") ?></script>

16
pages/search.php → public/search.php Executable file → Normal file
View file

@ -10,11 +10,11 @@
WorkActionsModel WorkActionsModel
}; };
require_once Path::root("src/client/API.php"); require_once VV::root("src/client/API.php");
require_once Path::root("api/src/Endpoints.php"); require_once VV::root("api/src/Endpoints.php");
require_once Path::root("api/src/databases/models/Work/Work.php"); require_once VV::root("api/src/databases/models/Work/Work.php");
require_once Path::root("api/src/databases/models/Work/WorkActions.php"); require_once VV::root("api/src/databases/models/Work/WorkActions.php");
// Search endpoint query paramter // Search endpoint query paramter
const SEARCH_PARAM = "q"; const SEARCH_PARAM = "q";
@ -26,7 +26,7 @@
$response = $api->call(Endpoints::SEARCH->value)->params([SEARCH_PARAM => $_GET[SEARCH_PARAM]])->get(); $response = $api->call(Endpoints::SEARCH->value)->params([SEARCH_PARAM => $_GET[SEARCH_PARAM]])->get();
?> ?>
<style><?= VV::css("pages/search") ?></style> <style><?= VV::css("assets/css/pages/search") ?></style>
<section class="search"> <section class="search">
<form method="GET"> <form method="GET">
<search> <search>
@ -34,7 +34,7 @@
</search> </search>
<button type="submit" class="inline solid">Search</button> <button type="submit" class="inline solid">Search</button>
</form> </form>
<?= VV::media("line.svg") ?> <?= VV::embed("assets/media/line.svg") ?>
<button class="inline">advanced search options</button> <button class="inline">advanced search options</button>
</section> </section>
@ -89,11 +89,11 @@
</section> </section>
<?php else: ?> <?php else: ?>
<section class="info empty"> <section class="info empty">
<?= VV::media("icons/search.svg") ?> <?= VV::embed("assets/media/icons/search.svg") ?>
<p>Start typing to search</p> <p>Start typing to search</p>
</section> </section>
<?php endif; ?> <?php endif; ?>
<?php endif; ?> <?php endif; ?>
<script><?= VV::js("pages/search") ?></script> <script><?= VV::js("assets/js/pages/search") ?></script>

16
pages/work.php → public/work.php Executable file → Normal file
View file

@ -11,12 +11,12 @@
WorkActionsModel WorkActionsModel
}; };
require_once Path::root("src/client/API.php"); require_once VV::root("src/client/API.php");
require_once Path::root("api/src/Endpoints.php"); require_once VV::root("api/src/Endpoints.php");
require_once Path::root("api/src/databases/models/Work/Work.php"); require_once VV::root("api/src/databases/models/Work/Work.php");
require_once Path::root("api/src/databases/models/Work/WorkTags.php"); require_once VV::root("api/src/databases/models/Work/WorkTags.php");
require_once Path::root("api/src/databases/models/Work/WorkActions.php"); require_once VV::root("api/src/databases/models/Work/WorkActions.php");
// Connect to VLW API // Connect to VLW API
$api = new API(); $api = new API();
@ -31,10 +31,10 @@
} }
?> ?>
<style><?= VV::css("pages/work") ?></style> <style><?= VV::css("assets/css/pages/work") ?></style>
<section class="git"> <section class="git">
<?= VV::media("icons/github.svg") ?> <?= VV::embed("assets/media/icons/github.svg") ?>
<p>Most of my free open-source software is available on GitHub and it's also mirrored on my server</p> <p>Most of my free open-source software is available on GitHub and it's also mirrored on my server</p>
<div class="buttons"> <div class="buttons">
<a href="https://github.com/victorwesterlund"><button class="inline solid">open GitHub</button></a> <a href="https://github.com/victorwesterlund"><button class="inline solid">open GitHub</button></a>
@ -178,4 +178,4 @@
<p>Something went wrong!</p> <p>Something went wrong!</p>
<?php endif; ?> <?php endif; ?>
<script><?= VV::js("pages/work") ?></script> <script><?= VV::js("assets/js/pages/work") ?></script>

20
pages/document.php → shells/document.php Executable file → Normal file
View file

@ -38,8 +38,8 @@
</script> </script>
<?php // Bootstrapping ?> <?php // Bootstrapping ?>
<style><?= VV::css("fonts") ?></style> <style><?= VV::css("assets/css/fonts") ?></style>
<style><?= VV::css("document") ?></style> <style><?= VV::css("assets/css/shells/document") ?></style>
<title>Victor L. Westerlund</title> <title>Victor L. Westerlund</title>
<link rel="icon" href="/assets/media/vw.svg"/> <link rel="icon" href="/assets/media/vw.svg"/>
@ -49,28 +49,28 @@
<nav> <nav>
<p><a href="/" vv="document" vv-call="navigate">victor westerlund</a></p> <p><a href="/" vv="document" vv-call="navigate">victor westerlund</a></p>
</nav> </nav>
<button class="search" vv="document" vv-call="openSearchbox"> <button class="search searchbox-open">
<?= VV::media("icons/search.svg") ?> <?= VV::embed("assets/media/icons/search.svg") ?>
<p>search vlw.se...</p> <p>search vlw.se...</p>
</button> </button>
<button class="logo" vv="document" vv-call="navigateHome"><?= VV::media("vw.svg") ?></button> <button class="logo" vv="/"><?= VV::embed("assets/media/vw.svg") ?></button>
<searchbox> <searchbox>
<input type="search" autocomplete="off" placeholder="search vlw.se..."> <input type="search" autocomplete="off" placeholder="search vlw.se...">
<button class="close" vv="document" vv-call="closeSearchbox"><?= VV::media("icons/close.svg") ?></button> <button class="close searchbox-close"><?= VV::embed("assets/media/icons/close.svg") ?></button>
</searchbox> </searchbox>
</header> </header>
<main></main> <vv-shell></vv-shell>
<search-results> <search-results>
<div class="info empty"> <div class="info empty">
<?= VV::media("icons/search.svg") ?> <?= VV::embed("assets/media/icons/search.svg") ?>
<p>start typing to search</p> <p>start typing to search</p>
</div> </div>
</search-results> </search-results>
<?php // Bootstrapping ?> <?php // Bootstrapping ?>
<script><?= VV::init() ?></script> <?= VV::init() ?>
<script><?= VV::js("document") ?></script> <script type="module"><?= VV::js("assets/js/shells/document") ?></script>
</body> </body>
</html> </html>