Compare commits

..

6 commits

Author SHA1 Message Date
e90bd82887 chore: bump Vegvisir to 3.7.1 (#75)
Reviewed-on: https://codeberg.org/vlw/vlw.se/pulls/75
2026-04-09 12:57:42 +02:00
vlw
990bf289c1 fix: add missing request methods to /ping endpoint (#74)
I forgot to add all available request methods to the ACL table for the `/ping` endpoint in #71. This PR fixes that.

Reviewed-on: https://codeberg.org/vlw/vlw.se/pulls/74
Co-authored-by: vlw <victor@vlw.se>
Co-committed-by: vlw <victor@vlw.se>
2026-04-06 11:38:01 +02:00
57884d4a25 feat: add "quick links" icon buttons in header (#73)
In this PR we add icon buttons in the header to quickly access the my contact page, Forgejo-, and Codeberg profile. I drew a Forgejo and Codeberg logo that I'm not 100% satisfied with. The buttons have a `title` attribute now which explains what the buttons represent. However, I would like to reuse the tooltip hover effect from the contact and about page. These effects are now repeating themselves on both pages, so we should merge them into a partial that can be included on each page, or just load it with the shell element.

<video src="/attachments/12429d26-cbfa-4e63-8ecd-fb1d1d270932" title="Inspelning 2026-04-05 140702" controls></video>
_The `title` tooltip is not visible in the recording, but they're there._

![Skärmbild 2026-04-05 141509](/attachments/688338bd-538e-48b2-be6c-b0903386a7e7)

Reviewed-on: https://codeberg.org/vlw/vlw.se/pulls/73
2026-04-05 14:20:30 +02:00
fdce8405cd feat: add /ping public API endpoint (#71)
This PR adds a new public API endpoint `/ping` which is more of an endpoint for debugging. It returns all search parameters, JSON request body, and request headers.

Example response:
```json
{
  "ping": "pong",
  "GET": {
    "foo": "bar"
  },
  "POST": [],
  "HEADERS": {
    "Te": "trailers",
    "Priority": "u=0, i",
    "Sec-Fetch-User": "?1",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Dest": "document",
    "Upgrade-Insecure-Requests": "1",
    "Sec-Gpc": "1",
    "Accept-Encoding": "gzip, deflate, br, zstd",
    "Accept-Language": "en-US,en;q=0.5",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0",
    "Host": "local.vlw.se:44302",
    "Content-Length": "",
    "Content-Type": ""
  },
  "datetime": {
    "date": "2026-03-29 11:20:30.775397",
    "timezone_type": 3,
    "timezone": "UTC"
  }
}
```

Reviewed-on: https://codeberg.org/vlw/vlw.se/pulls/71
2026-04-05 12:18:37 +02:00
55a8234800 refactor: move CSS and JS assets to directory in project root (#72)
In this PR we move all of the CSS and JavaScript assets from the public assets directory to a new directory in the project root. The main reason for this is that I would like to believe this makes them easier to find. Since all CSS and JS is bundled with each page anyways there is no need to access these directly from a public directory. The main argument against this I think would be "transparency". We already run this site with `display_php_source=true` in Vegvisir (so page source code can be inspected by appending `.php` to the end of a url. But there is of course no reason to trust that it's the actual source code.

Reviewed-on: https://codeberg.org/vlw/vlw.se/pulls/72
2026-04-05 12:18:10 +02:00
eeaaeeecdd chore: bump Vegvisir to 3.7.0 (#69)
In this PR we add support for and bump Vegvisir to version 3.7.0

Reviewed-on: https://codeberg.org/vlw/vlw.se/pulls/69
2026-03-28 13:18:19 +01:00
38 changed files with 126 additions and 35 deletions

23
api/ping/GET.php Normal file
View file

@ -0,0 +1,23 @@
<?php
use Reflect\{Response, Path};
use VLW\API\API;
require_once Path::root("src/API/API.php");
final class GET_Ping extends API {
public function __construct() {
parent::__construct();
}
public function main(): Response {
return new Response([
"ping" => "pong",
"GET" => $_GET,
"POST" => $_POST,
"HEADERS" => getallheaders(),
"datetime" => new DateTimeImmutable(),
]);
}
}

View file

@ -140,6 +140,11 @@ section.featured featured-item .actions {
flex-direction: column;
padding-top: var(--padding);
margin-top: auto;
button svg {
height: 1.15em;
stroke: white;
}
}
/* # Size queries */

View file

@ -31,6 +31,7 @@ section.git {
section.git > svg {
fill: white;
width: 60px;
stroke: white;
}
section.git .buttons {
@ -125,6 +126,11 @@ section.timeline .items .item .actions {
align-items: baseline;
margin-top: 7px;
gap: var(--padding);
button svg {
height: 1.2em;
stroke: white;
}
}
/* # Size queries */

View file

@ -186,13 +186,14 @@ header nav {
display: flex;
align-items: center;
padding: var(--padding);
padding-right: unset;
}
header nav > p {
white-space: nowrap;
}
header .buttons {
header :is(.buttons, .links) {
display: none;
}
@ -471,6 +472,45 @@ search-results .info :is(svg, img) {
}
@media (min-width: 900px) {
header {
.links {
width: 100%;
display: flex;
justify-content: end;
button {
width: unset;
padding: 10px;
border-radius: 6px;
border-left: unset;
svg {
fill: white;
stroke: white;
height: 2em;
opacity: .4;
transition:
100ms fill,
100ms stroke,
100ms opacity
;
}
@media (hover: hover) {
&:hover {
svg {
fill: var(--color-accent);
stroke: var(--color-accent);
opacity: 1;
}
}
}
}
}
}
}
@media (min-width: 1070px) {
header {
.buttons {
display: flex;

View file

@ -55,7 +55,7 @@
};
?>
<style><?= VV::css("public/assets/css/pages/about") ?></style>
<?= VV::css("assets/css/pages/about") ?>
<section class="intro">
<h2 aria-hidden="true">Hi, I'm</h2>
<h1>Victor Westerlund</h1>
@ -143,4 +143,4 @@
<p>RFC&nbsp;3339</p>
<p>digital archiving</p>
</div>
<script><?= VV::js("public/assets/js/pages/about") ?></script>
<?= VV::js("assets/js/pages/about") ?>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" class="forgejo" xml:space="preserve" version="1.1" viewBox="0 0 35.66 55.86"><g id="layer1" style="display:inline" transform="translate(-38.62 -169.12)"><g id="g7"><g id="g8-5" style="display:block;overflow:hidden;fill-opacity:1;stroke:none" transform="matrix(.19 0 0 .19 -70.52 114.69)"><path id="path1-60-5" d="M718.55 402.67c-.94.27-4.72 2.59-6.65 5.5-1.86 2.8-2.44 6.58-1.76 10.45s2.36 6.27 5.38 8.74a19.7 19.7 0 0 0 9.96 4.44c3.62.49 6.61-.84 9.94-3.43 3.27-2.54 5.68-5.1 5.06-9.42-.61-4.32-.16-8.99-3.32-12.43s-7.42-5.74-10.77-5.42c-34.22 3.3-4.72-24.6 13-17.9 2.5.95 6.19 3.75 8.33 5.5 2.4 2 6.89 6.4 9.12 11.5 1.82 4.18 3.63 8.12 4.13 13.14.45 4.55.15 8.46-.34 11.8-.49 3.32-1.74 5.82-3.24 9.06s-3.53 5.4-6.27 7.86-5.4 4.43-8.35 5.58c-2.93 1.16-7.32 2.97-13.16 2.83q-8.76-.2-13.85-1.79a36 36 0 0 1-9.97-5.05q-4.88-3.45-8.25-8.21-3.36-4.77-5.24-10.88a31 31 0 0 1-1.48-11.2q.4-5.07 1.61-10.13c.82-3.37 2.63-6.83 4.7-9.85 3.32-4.87 8.9-7.95 12.39-9.44 6.46-2.77 17.1-3.9 20.74-3.26 29.03 5.07 6.7 16.72-11.7 22" style="fill-opacity:1;stroke:none;stroke-width:22.7556"/><path id="path1-60-5-8" d="M739.9 316.68c-.07-.98-1.55-5.18-4-7.7-2.37-2.41-5.96-3.77-9.9-3.92-3.96-.15-6.66 1-9.73 3.44a19.8 19.8 0 0 0-6.46 8.85c-1.25 3.46-.57 6.68 1.27 10.5 1.81 3.75 3.82 6.66 8.2 6.96s8.86 1.74 12.9-.64 7.22-6.09 7.61-9.45c3.96-34.33 25.17.54 14.85 16.55-1.45 2.25-4.98 5.3-7.17 7.02-2.45 1.95-7.72 5.43-13.2 6.55-4.5.91-8.76 1.85-13.8 1.28a48 48 0 0 1-11.52-2.8c-3.17-1.2-5.36-2.95-8.23-5.1-2.87-2.16-4.56-4.61-6.4-7.83-1.84-3.2-3.22-6.24-3.73-9.38-.51-3.13-1.37-7.82 0-13.53q2.04-8.56 4.67-13.23a36 36 0 0 1 7.07-8.74 35 35 0 0 1 9.81-6.38q5.39-2.3 11.8-2.86a31 31 0 0 1 11.31.9q4.92 1.47 9.63 3.73c3.14 1.52 6.15 4.02 8.69 6.68 4.08 4.3 5.93 10.44 6.66 14.18 1.36 6.94.23 17.63-1.16 21.07-11.11 27.47-17.85 3.06-19.17-16.15" style="display:block;overflow:hidden;fill-opacity:1;stroke:none;stroke-width:22.8745"/><path id="path3-8-2" d="M596.86 534.52c-6.43 7.02-2.89 19.38 2.84 21.97 4.08 1.85 5.73 2.83 10.16 2.6s9.43-2.13 11.52-6.24c2.1-4.1 4.3-10.91 1.71-15.08-1.66-2.7-5.42-7.55-8.38-8.31-1.3-.33-4.8-1.22-6.7-.96-17.28 2.45-2.79-12.97-7.42-19.98 5.36-2 12.2-3.67 22.8-.04a32 32 0 0 1 9.08 4.9 35 35 0 0 1 7.81 8.7 30 30 0 0 1 4.61 11.3c.7 3.91 1.15 7.6.36 12.8q-1.19 7.8-6.31 16.5-5.13 8.7-10.22 12.32c-3.38 2.42-4.44 2.91-8.58 3.7-4.14.78-9.01 1.48-13.63.86-4.62-.6-7.8-1.4-11.25-2.85a52 52 0 0 1-11.01-6.6c-3.9-2.95-7.35-8.03-8.5-11.54-1.14-3.44-1.57-6.65-1.97-14.12q-.6-11.19 4.36-19.34a31.3 31.3 0 0 1 13.25-12.17c5.52-2.68 8.64-5.11 13.29-6.55l1.05 22.27c-3.32.81-8.87 4.75-8.87 5.86" style="fill-opacity:1;stroke:none;stroke-width:25"/><path id="path2-2-7" d="M626.5 94.4c-1.81.16-40.94-.72-44.5-.8s-6.93-.15-10.08 0c-10.68.48-14.7 2.2-16.02 5.16-1.27 2.83-2.38 4.58-2.82 7.14a47 47 0 0 0-.69 7.47q-.03 3.63-.02 8.06t-.02 9.94a1507 1507 0 0 0 .08 19.42l.11 8.74.09 7.77c.02 2.38.11 5.06.14 7.8.23 21.2 2.64 7.1-11.39 7.4-11.07-.72-11.84 9.68-11.96-8.12q-.4-3.52-.38-7.08l.08-7.77.12-8.74a972 972 0 0 0 .08-19.42q-.02-5.5 0-10t.22-8.97a60 60 0 0 1 1.04-8.93q.87-4.45 3.37-10a35 35 0 0 1 5.42-8.86 54 54 0 0 1 6.4-6.13 21 21 0 0 1 8.29-4.02q4.82-1.2 11.72-1.51 6.9-.33 12.16-.1c3.5.16 44.8 1.13 48.06 1.13 6.04 0 9.55-.13 8.08 9.22-1.21 7.66-1.26 10.67-7.58 11.2" style="display:block;overflow:hidden;fill-opacity:1;stroke:none;stroke-width:19.9637" transform="translate(67.1 329.94)"/><path id="path2-2-7-2" d="M629.27.3c-3.03 0-45.46-.6-48.95-.69-3.48-.09-6.77-.16-9.84 0-10.44.51-14.36 2.35-15.65 5.5-1.24 3.02-2.33 4.88-2.75 7.61-.43 2.74-.33 27.77-.43 30.34-.27 6.5-.03 5.43-.03 8.59l-.01 10.59a1830 1830 0 0 0 .07 20.69l.11 9.3.09 8.3c.02 2.52.1 5.38.14 8.3.22 22.6 2.57 7.56-11.13 7.88-10.81-.76-11.57 10.32-11.68-8.65q-.4-3.75-.38-7.54l.08-8.28.12-9.31a1060 1060 0 0 0 .07-20.7V51.58q.03-4.8.22-9.56c.12-3.18.21-28.72.77-31.89a52 52 0 0 1 3.3-10.66q2.45-5.92 5.29-9.43a55 55 0 0 1 6.24-6.54 20 20 0 0 1 8.1-4.28 56 56 0 0 1 11.46-1.61q6.74-.34 11.88-.1c3.42.16 43.77 1.2 46.94 1.2 5.9 0 9.33-.14 7.9 9.82C629.9-2.53 622.4.32 629.26.3" style="display:block;overflow:hidden;fill-opacity:1;stroke:none;stroke-width:20.3681" transform="translate(67.1 329.94)"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -37,7 +37,7 @@
}
?>
<style><?= VV::css("public/assets/css/pages/contact") ?></style>
<?= VV::css("assets/css/pages/contact") ?>
<section>
<h1>Let's chat!</h1>
<p>The best way to get in touch is definitely by email, or through the form on this page. The time in Sweden is <i><?= $date->format("h:i a") ?></i> right now, I am currently <?= $date->is_available() ? "available" : "not available" ?> and will hopefully reply in about <?= $date->get_estimated_reply_hours() ?> hours.</p>
@ -112,4 +112,4 @@
</button>
</form>
</section>
<script ><?= VV::js("public/assets/js/pages/contact") ?></script>
<?= VV::js("assets/js/pages/contact") ?>

View file

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

View file

@ -7,7 +7,7 @@
}
?>
<style><?= VV::css("public/assets/css/pages/index") ?></style>
<?= VV::css("assets/css/pages/index") ?>
<div class="menu">
<?= VV::embed("public/assets/media/line.svg") ?>
<menu>
@ -23,4 +23,4 @@
</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"/>
<script type="module"><?= VV::js("public/assets/js/pages/index") ?></script>
<?= VV::js("assets/js/pages/index") ?>

View file

@ -22,7 +22,7 @@
}
?>
<style><?= VV::css("public/assets/css/pages/search") ?></style>
<?= VV::css("assets/css/pages/search") ?>
<section class="search">
<form>
<input name="<?= GET_KEY_QUERY ?>" type="search" placeholder="search vlw.se..." value="<?= $search->query ?>">
@ -105,4 +105,4 @@
<p>Start typing to search</p>
</section>
<?php endif; ?>
<script><?= VV::js("public/assets/js/pages/search") ?></script>
<?= VV::js("assets/js/pages/search") ?>

View file

@ -37,8 +37,8 @@
//--><!]]>
</script>
<style><?= VV::css("public/assets/css/fonts") ?></style>
<style><?= VV::css("public/assets/css/shell") ?></style>
<?= VV::css("assets/css/fonts") ?>
<?= VV::css("assets/css/shell") ?>
<title>Victor Westerlund</title>
<link rel="icon" href="/assets/media/vw.svg"/>
@ -61,6 +61,17 @@
<p>contact</p>
</button></a>
</div>
<div class="links">
<a href="/contact"><button title="Get in touch">
<?= VV::embed("public/assets/media/icons/email.svg") ?>
</button></a>
<a href="https://git.vlw.se/vlw"><button title="Forgejo">
<?= VV::embed("public/assets/media/icons/forgejo.svg") ?>
</button></a>
<a href="https://codeberg.org/vlw"><button title="Codeberg">
<?= VV::embed("public/assets/media/icons/codeberg.svg") ?>
</button></a>
</div>
</nav>
<button class="search searchbox-open">
<?= VV::embed("public/assets/media/icons/search.svg") ?>
@ -73,7 +84,7 @@
</searchbox>
</header>
<vv-shell></vv-shell>
<?= VV::shell() ?>
<search-results>
<div class="info empty">
@ -82,7 +93,6 @@
</div>
</search-results>
<?= VV::init() ?>
<script><?= VV::js("public/assets/js/shell") ?></script>
<?= VV::js("assets/js/shell") ?>
</body>
</html>

View file

@ -1,4 +1,4 @@
<style><?= VV::css("public/assets/css/pages/work/archive") ?></style>
<?= VV::css("assets/css/pages/work/archive") ?>
<section>
<h1>This is an archived website!</h1>
<p>You're about to view an archived version of this website on my domain. Everything you see, and all features that are available on the archived website have been recreated to simulate the real behavior as closely as possible. Some features can unfortunately not be simulated properly and have been disabled completely. No actions you take on this website have any real effects.</p>
@ -9,4 +9,4 @@
<?= VV::embed("public/assets/media/icons/chevron.svg") ?>
</button></a>
</section>
<script><?= VV::js("public/assets/js/pages/work/archive") ?></script>
<?= VV::js("assets/js/pages/work/archive") ?>

View file

@ -5,7 +5,7 @@
require_once VV::root("src/Database/Models/Work/Work.php");
?>
<style><?= VV::css("public/assets/css/pages/work/index") ?></style>
<?= VV::css("assets/css/pages/work/index") ?>
<section class="hero">
<div class="item vegvisir">
<div class="wrapper">

View file

@ -42,7 +42,7 @@
}
?>
<style><?= VV::css("public/assets/css/pages/work/timeline") ?></style>
<?= VV::css("assets/css/pages/work/timeline") ?>
<section class="git">
<?= VV::embed("public/assets/media/icons/codeberg.svg") ?>
<p>This timeline has most but not all of my FOSS software. If you want to see a list of all things I've created for the free software world, check out my repos on Codeberg or Forgejo.</p>

View file

@ -1,4 +1,4 @@
<style><?= VV::css("public/assets/css/pages/work/wip") ?></style>
<?= VV::css("assets/css/pages/work/wip") ?>
<section class="disclaimer">
<h1>Soon, very soon!</h1>
<p>Bear with me as I cook up some texts about this project! Hopefully with some pictures too.</p>

View file

@ -10,6 +10,11 @@ SET time_zone = "+00:00";
TRUNCATE TABLE `acl`;
INSERT INTO `acl` (`ref_group`, `ref_endpoint`, `method`) VALUES
(NULL, 'ping', 'DELETE'),
(NULL, 'ping', 'POST'),
(NULL, 'ping', 'PATCH'),
(NULL, 'ping', 'PUT'),
(NULL, 'ping', 'GET'),
(NULL, 'coffee', 'GET'),
(NULL, 'languages', 'GET'),
(NULL, 'update', 'GET'),
@ -19,6 +24,7 @@ INSERT INTO `acl` (`ref_group`, `ref_endpoint`, `method`) VALUES
TRUNCATE TABLE `endpoints`;
INSERT INTO `endpoints` (`id`, `active`) VALUES
('ping', 1),
('coffee', 1),
('languages', 1),
('update', 1),

@ -1 +1 @@
Subproject commit da0f56400903edaa53829c21e6a9142f5b22c23f
Subproject commit 35f1b77c565536433183c8289a59b81201fc077f