Migrated project to use golang.
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
vendor/
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Run the application
|
||||||
|
run:
|
||||||
|
go run ./cmd
|
||||||
|
|
||||||
|
# Build the application binary into a bin/ folder
|
||||||
|
build:
|
||||||
|
go build -o bin/website ./cmd
|
||||||
|
@echo "Project built in bin directory"
|
||||||
|
|
||||||
|
# Clean up binaries and build artifacts
|
||||||
|
clean:
|
||||||
|
rm -rf bin/
|
||||||
|
@echo "Build artifacts removed."
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"jh/website/internal/handlers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
// Resolve static dir relative to the binary's location
|
||||||
|
mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
mux.HandleFunc("GET /", handlers.Home)
|
||||||
|
|
||||||
|
log.Println("Server starting on http://localhost:8080")
|
||||||
|
if err := http.ListenAndServe(":8080", mux); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "yourname/portfolio",
|
|
||||||
"description": "Personal developer portfolio",
|
|
||||||
"type": "project",
|
|
||||||
"require": {
|
|
||||||
"php": ">=8.1",
|
|
||||||
"league/route": "^5.1",
|
|
||||||
"nyholm/psr7": "^1.8",
|
|
||||||
"laminas/laminas-httphandlerrunner": "^2.9",
|
|
||||||
"nyholm/psr7-server": "^1.1"
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"App\\": "src/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Generated
-761
@@ -1,761 +0,0 @@
|
|||||||
{
|
|
||||||
"_readme": [
|
|
||||||
"This file locks the dependencies of your project to a known state",
|
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
|
||||||
"This file is @generated automatically"
|
|
||||||
],
|
|
||||||
"content-hash": "323c40f7ef7025d5fdc123b6e18ec5df",
|
|
||||||
"packages": [
|
|
||||||
{
|
|
||||||
"name": "laminas/laminas-httphandlerrunner",
|
|
||||||
"version": "2.13.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/laminas/laminas-httphandlerrunner.git",
|
|
||||||
"reference": "181eaeeb838ad3d80fbbcfb0657a46bc212bbd4e"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/181eaeeb838ad3d80fbbcfb0657a46bc212bbd4e",
|
|
||||||
"reference": "181eaeeb838ad3d80fbbcfb0657a46bc212bbd4e",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
|
|
||||||
"psr/http-message": "^1.0 || ^2.0",
|
|
||||||
"psr/http-message-implementation": "^1.0 || ^2.0",
|
|
||||||
"psr/http-server-handler": "^1.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"laminas/laminas-coding-standard": "~3.1.0",
|
|
||||||
"laminas/laminas-diactoros": "^3.6.0",
|
|
||||||
"phpunit/phpunit": "^10.5.46",
|
|
||||||
"psalm/plugin-phpunit": "^0.19.5",
|
|
||||||
"vimeo/psalm": "^6.10.3"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"laminas": {
|
|
||||||
"config-provider": "Laminas\\HttpHandlerRunner\\ConfigProvider"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Laminas\\HttpHandlerRunner\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"BSD-3-Clause"
|
|
||||||
],
|
|
||||||
"description": "Execute PSR-15 RequestHandlerInterface instances and emit responses they generate.",
|
|
||||||
"homepage": "https://laminas.dev",
|
|
||||||
"keywords": [
|
|
||||||
"components",
|
|
||||||
"laminas",
|
|
||||||
"mezzio",
|
|
||||||
"psr-15",
|
|
||||||
"psr-7"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"chat": "https://laminas.dev/chat",
|
|
||||||
"docs": "https://docs.laminas.dev/laminas-httphandlerrunner/",
|
|
||||||
"forum": "https://discourse.laminas.dev",
|
|
||||||
"issues": "https://github.com/laminas/laminas-httphandlerrunner/issues",
|
|
||||||
"rss": "https://github.com/laminas/laminas-httphandlerrunner/releases.atom",
|
|
||||||
"source": "https://github.com/laminas/laminas-httphandlerrunner"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://funding.communitybridge.org/projects/laminas-project",
|
|
||||||
"type": "community_bridge"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2025-10-12T20:58:29+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "league/route",
|
|
||||||
"version": "5.1.2",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/thephpleague/route.git",
|
|
||||||
"reference": "adf9b961dc5ffdbcffb2b8d7963c7978f2794c92"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/thephpleague/route/zipball/adf9b961dc5ffdbcffb2b8d7963c7978f2794c92",
|
|
||||||
"reference": "adf9b961dc5ffdbcffb2b8d7963c7978f2794c92",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"nikic/fast-route": "^1.3",
|
|
||||||
"opis/closure": "^3.5.5",
|
|
||||||
"php": "^7.2 || ^8.0",
|
|
||||||
"psr/container": "^1.0|^2.0",
|
|
||||||
"psr/http-factory": "^1.0",
|
|
||||||
"psr/http-message": "^1.0.1",
|
|
||||||
"psr/http-server-handler": "^1.0.1",
|
|
||||||
"psr/http-server-middleware": "^1.0.1",
|
|
||||||
"psr/simple-cache": "^1.0"
|
|
||||||
},
|
|
||||||
"replace": {
|
|
||||||
"orno/http": "~1.0",
|
|
||||||
"orno/route": "~1.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"laminas/laminas-diactoros": "^2.3",
|
|
||||||
"phpstan/phpstan": "^0.12",
|
|
||||||
"phpstan/phpstan-phpunit": "^0.12",
|
|
||||||
"phpunit/phpunit": "^8.5",
|
|
||||||
"roave/security-advisories": "dev-master",
|
|
||||||
"scrutinizer/ocular": "^1.8",
|
|
||||||
"squizlabs/php_codesniffer": "^3.5"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-1.x": "1.x-dev",
|
|
||||||
"dev-2.x": "2.x-dev",
|
|
||||||
"dev-3.x": "3.x-dev",
|
|
||||||
"dev-4.x": "4.x-dev",
|
|
||||||
"dev-5.x": "5.x-dev",
|
|
||||||
"dev-master": "5.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"League\\Route\\": "src"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Phil Bennett",
|
|
||||||
"email": "philipobenito@gmail.com",
|
|
||||||
"role": "Developer"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Fast routing and dispatch component including PSR-15 middleware, built on top of FastRoute.",
|
|
||||||
"homepage": "https://github.com/thephpleague/route",
|
|
||||||
"keywords": [
|
|
||||||
"dispatcher",
|
|
||||||
"league",
|
|
||||||
"psr-15",
|
|
||||||
"psr-7",
|
|
||||||
"psr15",
|
|
||||||
"psr7",
|
|
||||||
"route",
|
|
||||||
"router"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/thephpleague/route/issues",
|
|
||||||
"source": "https://github.com/thephpleague/route/tree/5.1.2"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/philipobenito",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2021-07-30T08:33:09+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "nikic/fast-route",
|
|
||||||
"version": "v1.3.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/nikic/FastRoute.git",
|
|
||||||
"reference": "181d480e08d9476e61381e04a71b34dc0432e812"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
|
|
||||||
"reference": "181d480e08d9476e61381e04a71b34dc0432e812",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=5.4.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"phpunit/phpunit": "^4.8.35|~5.7"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"files": [
|
|
||||||
"src/functions.php"
|
|
||||||
],
|
|
||||||
"psr-4": {
|
|
||||||
"FastRoute\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"BSD-3-Clause"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Nikita Popov",
|
|
||||||
"email": "nikic@php.net"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Fast request router for PHP",
|
|
||||||
"keywords": [
|
|
||||||
"router",
|
|
||||||
"routing"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/nikic/FastRoute/issues",
|
|
||||||
"source": "https://github.com/nikic/FastRoute/tree/master"
|
|
||||||
},
|
|
||||||
"time": "2018-02-13T20:26:39+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "nyholm/psr7",
|
|
||||||
"version": "1.8.2",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/Nyholm/psr7.git",
|
|
||||||
"reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3",
|
|
||||||
"reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=7.2",
|
|
||||||
"psr/http-factory": "^1.0",
|
|
||||||
"psr/http-message": "^1.1 || ^2.0"
|
|
||||||
},
|
|
||||||
"provide": {
|
|
||||||
"php-http/message-factory-implementation": "1.0",
|
|
||||||
"psr/http-factory-implementation": "1.0",
|
|
||||||
"psr/http-message-implementation": "1.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"http-interop/http-factory-tests": "^0.9",
|
|
||||||
"php-http/message-factory": "^1.0",
|
|
||||||
"php-http/psr7-integration-tests": "^1.0",
|
|
||||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
|
|
||||||
"symfony/error-handler": "^4.4"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.8-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Nyholm\\Psr7\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Tobias Nyholm",
|
|
||||||
"email": "tobias.nyholm@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Martijn van der Ven",
|
|
||||||
"email": "martijn@vanderven.se"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "A fast PHP7 implementation of PSR-7",
|
|
||||||
"homepage": "https://tnyholm.se",
|
|
||||||
"keywords": [
|
|
||||||
"psr-17",
|
|
||||||
"psr-7"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/Nyholm/psr7/issues",
|
|
||||||
"source": "https://github.com/Nyholm/psr7/tree/1.8.2"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/Zegnat",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/nyholm",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2024-09-09T07:06:30+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "nyholm/psr7-server",
|
|
||||||
"version": "1.1.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/Nyholm/psr7-server.git",
|
|
||||||
"reference": "4335801d851f554ca43fa6e7d2602141538854dc"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/Nyholm/psr7-server/zipball/4335801d851f554ca43fa6e7d2602141538854dc",
|
|
||||||
"reference": "4335801d851f554ca43fa6e7d2602141538854dc",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^7.1 || ^8.0",
|
|
||||||
"psr/http-factory": "^1.0",
|
|
||||||
"psr/http-message": "^1.0 || ^2.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"nyholm/nsa": "^1.1",
|
|
||||||
"nyholm/psr7": "^1.3",
|
|
||||||
"phpunit/phpunit": "^7.0 || ^8.5 || ^9.3"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Nyholm\\Psr7Server\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Tobias Nyholm",
|
|
||||||
"email": "tobias.nyholm@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Martijn van der Ven",
|
|
||||||
"email": "martijn@vanderven.se"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Helper classes to handle PSR-7 server requests",
|
|
||||||
"homepage": "http://tnyholm.se",
|
|
||||||
"keywords": [
|
|
||||||
"psr-17",
|
|
||||||
"psr-7"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/Nyholm/psr7-server/issues",
|
|
||||||
"source": "https://github.com/Nyholm/psr7-server/tree/1.1.0"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/Zegnat",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/nyholm",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2023-11-08T09:30:43+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "opis/closure",
|
|
||||||
"version": "3.7.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/opis/closure.git",
|
|
||||||
"reference": "b1a22a6be71c1263f3ca6e68f00b3fd4d394abc4"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/opis/closure/zipball/b1a22a6be71c1263f3ca6e68f00b3fd4d394abc4",
|
|
||||||
"reference": "b1a22a6be71c1263f3ca6e68f00b3fd4d394abc4",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^5.4 || ^7.0 || ^8.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"jeremeamia/superclosure": "^2.0",
|
|
||||||
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "3.6.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"files": [
|
|
||||||
"functions.php"
|
|
||||||
],
|
|
||||||
"psr-4": {
|
|
||||||
"Opis\\Closure\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Marius Sarca",
|
|
||||||
"email": "marius.sarca@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Sorin Sarca",
|
|
||||||
"email": "sarca_sorin@hotmail.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.",
|
|
||||||
"homepage": "https://opis.io/closure",
|
|
||||||
"keywords": [
|
|
||||||
"anonymous functions",
|
|
||||||
"closure",
|
|
||||||
"function",
|
|
||||||
"serializable",
|
|
||||||
"serialization",
|
|
||||||
"serialize"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/opis/closure/issues",
|
|
||||||
"source": "https://github.com/opis/closure/tree/3.7.0"
|
|
||||||
},
|
|
||||||
"time": "2025-07-08T20:30:08+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "psr/container",
|
|
||||||
"version": "2.0.2",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/php-fig/container.git",
|
|
||||||
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
|
|
||||||
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=7.4.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "2.0.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Psr\\Container\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "PHP-FIG",
|
|
||||||
"homepage": "https://www.php-fig.org/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Common Container Interface (PHP FIG PSR-11)",
|
|
||||||
"homepage": "https://github.com/php-fig/container",
|
|
||||||
"keywords": [
|
|
||||||
"PSR-11",
|
|
||||||
"container",
|
|
||||||
"container-interface",
|
|
||||||
"container-interop",
|
|
||||||
"psr"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/php-fig/container/issues",
|
|
||||||
"source": "https://github.com/php-fig/container/tree/2.0.2"
|
|
||||||
},
|
|
||||||
"time": "2021-11-05T16:47:00+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "psr/http-factory",
|
|
||||||
"version": "1.1.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/php-fig/http-factory.git",
|
|
||||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
|
||||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=7.1",
|
|
||||||
"psr/http-message": "^1.0 || ^2.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.0.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Psr\\Http\\Message\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "PHP-FIG",
|
|
||||||
"homepage": "https://www.php-fig.org/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
|
||||||
"keywords": [
|
|
||||||
"factory",
|
|
||||||
"http",
|
|
||||||
"message",
|
|
||||||
"psr",
|
|
||||||
"psr-17",
|
|
||||||
"psr-7",
|
|
||||||
"request",
|
|
||||||
"response"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"source": "https://github.com/php-fig/http-factory"
|
|
||||||
},
|
|
||||||
"time": "2024-04-15T12:06:14+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "psr/http-message",
|
|
||||||
"version": "1.1",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/php-fig/http-message.git",
|
|
||||||
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
|
||||||
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^7.2 || ^8.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.1.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Psr\\Http\\Message\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "PHP-FIG",
|
|
||||||
"homepage": "http://www.php-fig.org/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Common interface for HTTP messages",
|
|
||||||
"homepage": "https://github.com/php-fig/http-message",
|
|
||||||
"keywords": [
|
|
||||||
"http",
|
|
||||||
"http-message",
|
|
||||||
"psr",
|
|
||||||
"psr-7",
|
|
||||||
"request",
|
|
||||||
"response"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"source": "https://github.com/php-fig/http-message/tree/1.1"
|
|
||||||
},
|
|
||||||
"time": "2023-04-04T09:50:52+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "psr/http-server-handler",
|
|
||||||
"version": "1.0.2",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/php-fig/http-server-handler.git",
|
|
||||||
"reference": "84c4fb66179be4caaf8e97bd239203245302e7d4"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4",
|
|
||||||
"reference": "84c4fb66179be4caaf8e97bd239203245302e7d4",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=7.0",
|
|
||||||
"psr/http-message": "^1.0 || ^2.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.0.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Psr\\Http\\Server\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "PHP-FIG",
|
|
||||||
"homepage": "https://www.php-fig.org/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Common interface for HTTP server-side request handler",
|
|
||||||
"keywords": [
|
|
||||||
"handler",
|
|
||||||
"http",
|
|
||||||
"http-interop",
|
|
||||||
"psr",
|
|
||||||
"psr-15",
|
|
||||||
"psr-7",
|
|
||||||
"request",
|
|
||||||
"response",
|
|
||||||
"server"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"source": "https://github.com/php-fig/http-server-handler/tree/1.0.2"
|
|
||||||
},
|
|
||||||
"time": "2023-04-10T20:06:20+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "psr/http-server-middleware",
|
|
||||||
"version": "1.0.2",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/php-fig/http-server-middleware.git",
|
|
||||||
"reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829",
|
|
||||||
"reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=7.0",
|
|
||||||
"psr/http-message": "^1.0 || ^2.0",
|
|
||||||
"psr/http-server-handler": "^1.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.0.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Psr\\Http\\Server\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "PHP-FIG",
|
|
||||||
"homepage": "https://www.php-fig.org/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Common interface for HTTP server-side middleware",
|
|
||||||
"keywords": [
|
|
||||||
"http",
|
|
||||||
"http-interop",
|
|
||||||
"middleware",
|
|
||||||
"psr",
|
|
||||||
"psr-15",
|
|
||||||
"psr-7",
|
|
||||||
"request",
|
|
||||||
"response"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/php-fig/http-server-middleware/issues",
|
|
||||||
"source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2"
|
|
||||||
},
|
|
||||||
"time": "2023-04-11T06:14:47+00:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "psr/simple-cache",
|
|
||||||
"version": "1.0.1",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/php-fig/simple-cache.git",
|
|
||||||
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
|
|
||||||
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=5.3.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.0.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Psr\\SimpleCache\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "PHP-FIG",
|
|
||||||
"homepage": "http://www.php-fig.org/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Common interfaces for simple caching",
|
|
||||||
"keywords": [
|
|
||||||
"cache",
|
|
||||||
"caching",
|
|
||||||
"psr",
|
|
||||||
"psr-16",
|
|
||||||
"simple-cache"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"source": "https://github.com/php-fig/simple-cache/tree/master"
|
|
||||||
},
|
|
||||||
"time": "2017-10-23T01:57:42+00:00"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"packages-dev": [],
|
|
||||||
"aliases": [],
|
|
||||||
"minimum-stability": "stable",
|
|
||||||
"stability-flags": {},
|
|
||||||
"prefer-stable": false,
|
|
||||||
"prefer-lowest": false,
|
|
||||||
"platform": {
|
|
||||||
"php": ">=8.1"
|
|
||||||
},
|
|
||||||
"platform-dev": {},
|
|
||||||
"plugin-api-version": "2.6.0"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
type HomeData struct {
|
||||||
|
Title string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Home(w http.ResponseWriter, r *http.Request) {
|
||||||
|
data := HomeData{Title: "Home"}
|
||||||
|
view(w, r, "home", data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Renders a full page by combining the base template with a page template
|
||||||
|
// Parsed together so the page can define blocks needed for base template
|
||||||
|
func render(w http.ResponseWriter, _ *http.Request, page string, data any) {
|
||||||
|
files := []string{
|
||||||
|
filepath.Join("internal", "templates", "layouts", "base.html"),
|
||||||
|
filepath.Join("internal", "templates", "pages", page+".html"),
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.ParseFiles(files...)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "template error: " + err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "text/html")
|
||||||
|
|
||||||
|
if err := tmpl.ExecuteTemplate(w, "base", data); err != nil {
|
||||||
|
http.Error(w, "render error: " + err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func view(w http.ResponseWriter, r *http.Request, page string, data any) {
|
||||||
|
render(w, r, page, data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{{define "base"}}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{.Title}}</title>
|
||||||
|
<link rel="stylesheet" href="/static/css/reset.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/index.css">
|
||||||
|
<link rel="stylesheet" href="/static/js/index.js" defer>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a href="/">Home</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{{template "content" .}}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© <span id="year"></span></p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
document.querySelector('#year').innerHTML = new Date().getFullYear();
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{{define "content"}}
|
||||||
|
<section class="hero">
|
||||||
|
<h1>Hi, I'm Jason</h1>
|
||||||
|
<p>PHP & Go developer. I build web applications and tools.</p>
|
||||||
|
<a href="https://codeberg.org/yourname" target="_blank" rel="noopener">Codeberg</a>
|
||||||
|
</section>
|
||||||
|
{{end}}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../vendor/autoload.php';
|
|
||||||
|
|
||||||
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
|
|
||||||
use League\Route\Router;
|
|
||||||
use League\Route\Strategy\ApplicationStrategy;
|
|
||||||
use Nyholm\Psr7\Factory\Psr17Factory;
|
|
||||||
use Nyholm\Psr7Server\ServerRequestCreator;
|
|
||||||
|
|
||||||
use App\Controllers\HomeController;
|
|
||||||
|
|
||||||
$psr17Factory = new Psr17Factory();
|
|
||||||
$creator = new ServerRequestCreator($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
|
|
||||||
$request = $creator->fromGlobals();
|
|
||||||
|
|
||||||
// --- Router setup ---
|
|
||||||
$strategy = new ApplicationStrategy();
|
|
||||||
$router = new Router();
|
|
||||||
|
|
||||||
// --- Routes ---
|
|
||||||
$router->get('/', [HomeController::class, 'index']);
|
|
||||||
|
|
||||||
// --- Dispatch and emit ---
|
|
||||||
try {
|
|
||||||
$response = $router->dispatch($request);
|
|
||||||
} catch (\League\Route\Http\Exception\NotFoundException $e) {
|
|
||||||
$response = (new Psr17Factory())->createResponse(404);
|
|
||||||
$response->getBody()->write('<h1>404 — Page not found</h1>');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$response = (new Psr17Factory())->createResponse(500);
|
|
||||||
$response->getBody()->write('<h1>500 — Server error</h1>');
|
|
||||||
}
|
|
||||||
|
|
||||||
(new SapiEmitter())->emit($response);
|
|
||||||
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Controllers;
|
|
||||||
|
|
||||||
use Nyholm\Psr7\Factory\Psr17Factory;
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
|
||||||
|
|
||||||
abstract class BaseController
|
|
||||||
{
|
|
||||||
private Psr17Factory $factory;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->factory = new Psr17Factory();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render a template — the template itself is responsible for including any layout.
|
|
||||||
*/
|
|
||||||
protected function render(string $template, array $data = [], int $status = 200): ResponseInterface
|
|
||||||
{
|
|
||||||
ob_start();
|
|
||||||
extract($data);
|
|
||||||
require __DIR__ . '/../views/' . $template . '.php';
|
|
||||||
$html = ob_get_clean();
|
|
||||||
|
|
||||||
return $this->response($html, $status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically return a partial for HTMX requests, full page otherwise.
|
|
||||||
* Each template is responsible for including its own layout if needed.
|
|
||||||
*/
|
|
||||||
protected function view(
|
|
||||||
ServerRequestInterface $request,
|
|
||||||
string $fullTemplate,
|
|
||||||
string $partialTemplate,
|
|
||||||
array $data = []
|
|
||||||
): ResponseInterface {
|
|
||||||
$template = $this->isHtmx($request) ? $partialTemplate : $fullTemplate;
|
|
||||||
|
|
||||||
return $this->render($template, $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a JSON response.
|
|
||||||
*/
|
|
||||||
protected function json(array $data, int $status = 200): ResponseInterface
|
|
||||||
{
|
|
||||||
return $this->response(json_encode($data), $status, 'application/json');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the request came from HTMX.
|
|
||||||
*/
|
|
||||||
protected function isHtmx(ServerRequestInterface $request): bool
|
|
||||||
{
|
|
||||||
return $request->hasHeader('HX-Request');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a basic HTML response.
|
|
||||||
*/
|
|
||||||
private function response(string $body, int $status = 200, string $contentType = 'text/html'): ResponseInterface
|
|
||||||
{
|
|
||||||
$response = $this->factory->createResponse($status);
|
|
||||||
$response->getBody()->write($body);
|
|
||||||
return $response->withHeader('Content-Type', $contentType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Controllers;
|
|
||||||
|
|
||||||
use Psr\Http\Message\ResponseInterface;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
|
||||||
|
|
||||||
class HomeController extends BaseController
|
|
||||||
{
|
|
||||||
public function index(ServerRequestInterface $request): ResponseInterface
|
|
||||||
{
|
|
||||||
return $this->render('pages/home');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<p> Jason testing, Here! </p>
|
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
body {
|
||||||
|
background-color: #191919;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
@@ -0,0 +1,467 @@
|
|||||||
|
/**
|
||||||
|
* By Oskar Wickström
|
||||||
|
* Licensed under the MIT License (https://github.com/owickstrom/the-monospace-web/blob/main/LICENSE.md)
|
||||||
|
**/
|
||||||
|
@import url('https://fonts.cdnfonts.com/css/jetbrains-mono-2');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--font-family: "JetBrains Mono", monospace;
|
||||||
|
--line-height: 1.20rem;
|
||||||
|
--border-thickness: 2px;
|
||||||
|
--text-color: #000;
|
||||||
|
--text-color-alt: #666;
|
||||||
|
--background-color: #fff;
|
||||||
|
--background-color-alt: #eee;
|
||||||
|
|
||||||
|
--font-weight-normal: 500;
|
||||||
|
--font-weight-medium: 600;
|
||||||
|
--font-weight-bold: 800;
|
||||||
|
|
||||||
|
font-family: var(--font-family);
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
font-weight: var(--font-weight-normal);
|
||||||
|
font-style: normal;
|
||||||
|
font-variant-numeric: tabular-nums lining-nums;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--text-color: #fff;
|
||||||
|
--text-color-alt: #aaa;
|
||||||
|
--background-color: #000;
|
||||||
|
--background-color-alt: #111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
* + * {
|
||||||
|
margin-top: var(--line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--line-height) 2ch;
|
||||||
|
max-width: calc(min(80ch, round(down, 100%, 1ch)));
|
||||||
|
line-height: var(--line-height);
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 480px) {
|
||||||
|
:root {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
padding: var(--line-height) 1ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
margin: calc(var(--line-height) * 2) 0 var(--line-height);
|
||||||
|
line-height: var(--line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: calc(2 * var(--line-height));
|
||||||
|
margin-bottom: calc(var(--line-height) * 2);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
height: var(--line-height);
|
||||||
|
margin: calc(var(--line-height) * 1.5) 0;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
hr:after {
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--line-height) / 2 - var(--border-thickness));
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
border-top: calc(var(--border-thickness) * 3) double var(--text-color);
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration-thickness: var(--border-thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link, a:visited {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: var(--line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
}
|
||||||
|
em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
vertical-align: sub;
|
||||||
|
line-height: 0;
|
||||||
|
width: calc(1ch / 0.75);
|
||||||
|
font-size: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
position: relative;
|
||||||
|
top: calc(var(--line-height) / 2);
|
||||||
|
width: calc(round(down, 100%, 1ch));
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 0 0 calc(var(--line-height) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: var(--border-thickness) solid var(--text-color);
|
||||||
|
padding:
|
||||||
|
calc((var(--line-height) / 2))
|
||||||
|
calc(1ch - var(--border-thickness) / 2)
|
||||||
|
calc((var(--line-height) / 2) - (var(--border-thickness)))
|
||||||
|
;
|
||||||
|
line-height: var(--line-height);
|
||||||
|
vertical-align: top;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
table tbody tr:first-child > * {
|
||||||
|
padding-top: calc((var(--line-height) / 2) - var(--border-thickness));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
th {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.width-min {
|
||||||
|
width: 0%;
|
||||||
|
}
|
||||||
|
.width-auto {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
margin-bottom: calc(var(--line-height) * 2);
|
||||||
|
}
|
||||||
|
.header h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.header tr td:last-child {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
word-break: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
img, video {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--text-color-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
border: var(--border-thickness) solid var(--text-color);
|
||||||
|
padding: calc(var(--line-height) - var(--border-thickness)) 1ch;
|
||||||
|
margin-bottom: var(--line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
details[open] summary {
|
||||||
|
margin-bottom: var(--line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
details ::marker {
|
||||||
|
display: inline-block;
|
||||||
|
content: '▶';
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
details[open] ::marker {
|
||||||
|
content: '▼';
|
||||||
|
}
|
||||||
|
|
||||||
|
details :last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
white-space: pre;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: var(--line-height) 0;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
figure pre {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre, code {
|
||||||
|
font-family: var(--font-family);
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: calc(var(--line-height) * 2) 3ch;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
figcaption {
|
||||||
|
display: block;
|
||||||
|
font-style: italic;
|
||||||
|
margin-top: var(--line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0 var(--line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: square;
|
||||||
|
padding: 0 0 0 2ch;
|
||||||
|
}
|
||||||
|
ol {
|
||||||
|
list-style-type: none;
|
||||||
|
counter-reset: item;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
ol ul,
|
||||||
|
ol ol,
|
||||||
|
ul ol,
|
||||||
|
ul ul {
|
||||||
|
padding: 0 0 0 3ch;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
ol li:before {
|
||||||
|
content: counters(item, ".") ". ";
|
||||||
|
counter-increment: item;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li::marker {
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
height: var(--line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
input, button, textarea {
|
||||||
|
border: var(--border-thickness) solid var(--text-color);
|
||||||
|
padding:
|
||||||
|
calc(var(--line-height) / 2 - var(--border-thickness))
|
||||||
|
calc(1ch - var(--border-thickness));
|
||||||
|
margin: 0;
|
||||||
|
font: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
height: calc(var(--line-height) * 2);
|
||||||
|
width: auto;
|
||||||
|
overflow: visible;
|
||||||
|
background: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
line-height: normal;
|
||||||
|
-webkit-font-smoothing: inherit;
|
||||||
|
-moz-osx-font-smoothing: inherit;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox],
|
||||||
|
input[type=radio] {
|
||||||
|
display: inline-grid;
|
||||||
|
place-content: center;
|
||||||
|
vertical-align: top;
|
||||||
|
width: 2ch;
|
||||||
|
height: var(--line-height);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
input[type=checkbox]:checked:before,
|
||||||
|
input[type=radio]:checked:before {
|
||||||
|
content: "";
|
||||||
|
width: 1ch;
|
||||||
|
height: calc(var(--line-height) / 2);
|
||||||
|
background: var(--text-color);
|
||||||
|
}
|
||||||
|
input[type=radio],
|
||||||
|
input[type=radio]:before {
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus, input:focus {
|
||||||
|
--border-thickness: 3px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: calc(round(down, 100%, 1ch));
|
||||||
|
}
|
||||||
|
::placeholder {
|
||||||
|
color: var(--text-color-alt);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
::-ms-input-placeholder {
|
||||||
|
color: var(--text-color-alt);
|
||||||
|
}
|
||||||
|
button::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: var(--background-color-alt);
|
||||||
|
}
|
||||||
|
button:active {
|
||||||
|
transform: translate(2px, 2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
width: calc(round(down, 100%, 1ch));
|
||||||
|
height: auto;
|
||||||
|
line-height: var(--line-height);
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree, .tree ul {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
line-height: var(--line-height);
|
||||||
|
}
|
||||||
|
.tree ul {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.tree ul li {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 1.5ch;
|
||||||
|
margin-left: 1.5ch;
|
||||||
|
border-left: var(--border-thickness) solid var(--text-color);
|
||||||
|
}
|
||||||
|
.tree ul li:before {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: calc(var(--line-height) / 2);
|
||||||
|
left: 0;
|
||||||
|
content: "";
|
||||||
|
width: 1ch;
|
||||||
|
border-bottom: var(--border-thickness) solid var(--text-color);
|
||||||
|
}
|
||||||
|
.tree ul li:last-child {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
.tree ul li:last-child:after {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
content: "";
|
||||||
|
height: calc(var(--line-height) / 2);
|
||||||
|
border-left: var(--border-thickness) solid var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
--grid-cells: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 1ch;
|
||||||
|
width: calc(round(down, 100%, (1ch * var(--grid-cells)) - (1ch * var(--grid-cells) - 1)));
|
||||||
|
margin-bottom: var(--line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid > *,
|
||||||
|
.grid > input {
|
||||||
|
flex: 0 0 calc(round(down, (100% - (1ch * (var(--grid-cells) - 1))) / var(--grid-cells), 1ch));
|
||||||
|
}
|
||||||
|
.grid:has(> :last-child:nth-child(1)) { --grid-cells: 1; }
|
||||||
|
.grid:has(> :last-child:nth-child(2)) { --grid-cells: 2; }
|
||||||
|
.grid:has(> :last-child:nth-child(3)) { --grid-cells: 3; }
|
||||||
|
.grid:has(> :last-child:nth-child(4)) { --grid-cells: 4; }
|
||||||
|
.grid:has(> :last-child:nth-child(5)) { --grid-cells: 5; }
|
||||||
|
.grid:has(> :last-child:nth-child(6)) { --grid-cells: 6; }
|
||||||
|
.grid:has(> :last-child:nth-child(7)) { --grid-cells: 7; }
|
||||||
|
.grid:has(> :last-child:nth-child(8)) { --grid-cells: 8; }
|
||||||
|
.grid:has(> :last-child:nth-child(9)) { --grid-cells: 9; }
|
||||||
|
|
||||||
|
/* DEBUG UTILITIES */
|
||||||
|
|
||||||
|
.debug .debug-grid {
|
||||||
|
--color: color-mix(in srgb, var(--text-color) 10%, var(--background-color) 90%);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: -1;
|
||||||
|
background-image:
|
||||||
|
repeating-linear-gradient(var(--color) 0 1px, transparent 1px 100%),
|
||||||
|
repeating-linear-gradient(90deg, var(--color) 0 1px, transparent 1px 100%);
|
||||||
|
background-size: 1ch var(--line-height);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug .off-grid {
|
||||||
|
background: rgba(255, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-toggle-label {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/* http://meyerweb.com/eric/tools/css/reset/
|
||||||
|
v2.0 | 20110126
|
||||||
|
License: none (public domain)
|
||||||
|
*/
|
||||||
|
|
||||||
|
html, body, div, span, applet, object, iframe,
|
||||||
|
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||||
|
a, abbr, acronym, address, big, cite, code,
|
||||||
|
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||||
|
small, strike, strong, sub, sup, tt, var,
|
||||||
|
b, u, i, center,
|
||||||
|
dl, dt, dd, ol, ul, li,
|
||||||
|
fieldset, form, label, legend,
|
||||||
|
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||||
|
article, aside, canvas, details, embed,
|
||||||
|
figure, figcaption, footer, header, hgroup,
|
||||||
|
menu, nav, output, ruby, section, summary,
|
||||||
|
time, mark, audio, video {
|
||||||
|
/*margin: 0;*/
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article, aside, details, figcaption, figure,
|
||||||
|
footer, header, hgroup, menu, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
ol, ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
blockquote, q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
blockquote:before, blockquote:after,
|
||||||
|
q:before, q:after {
|
||||||
|
content: '';
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
function gridCellDimensions() {
|
||||||
|
const element = document.createElement("div");
|
||||||
|
element.style.position = "fixed";
|
||||||
|
element.style.height = "var(--line-height)";
|
||||||
|
element.style.width = "1ch";
|
||||||
|
document.body.appendChild(element);
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
document.body.removeChild(element);
|
||||||
|
return { width: rect.width, height: rect.height };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add padding to each media to maintain grid.
|
||||||
|
function adjustMediaPadding() {
|
||||||
|
const cell = gridCellDimensions();
|
||||||
|
|
||||||
|
function setHeightFromRatio(media, ratio) {
|
||||||
|
const rect = media.getBoundingClientRect();
|
||||||
|
const realHeight = rect.width / ratio;
|
||||||
|
const diff = cell.height - (realHeight % cell.height);
|
||||||
|
media.style.setProperty("padding-bottom", `${diff}px`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFallbackHeight(media) {
|
||||||
|
const rect = media.getBoundingClientRect();
|
||||||
|
const height = Math.round((rect.width / 2) / cell.height) * cell.height;
|
||||||
|
media.style.setProperty("height", `${height}px`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMediaLoaded(media) {
|
||||||
|
var width, height;
|
||||||
|
switch (media.tagName) {
|
||||||
|
case "IMG":
|
||||||
|
width = media.naturalWidth;
|
||||||
|
height = media.naturalHeight;
|
||||||
|
break;
|
||||||
|
case "VIDEO":
|
||||||
|
width = media.videoWidth;
|
||||||
|
height = media.videoHeight;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (width > 0 && height > 0) {
|
||||||
|
setHeightFromRatio(media, width / height);
|
||||||
|
} else {
|
||||||
|
setFallbackHeight(media);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const medias = document.querySelectorAll("img, video");
|
||||||
|
for (media of medias) {
|
||||||
|
switch (media.tagName) {
|
||||||
|
case "IMG":
|
||||||
|
if (media.complete) {
|
||||||
|
onMediaLoaded(media);
|
||||||
|
} else {
|
||||||
|
media.addEventListener("load", () => onMediaLoaded(media));
|
||||||
|
media.addEventListener("error", function() {
|
||||||
|
setFallbackHeight(media);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "VIDEO":
|
||||||
|
switch (media.readyState) {
|
||||||
|
case HTMLMediaElement.HAVE_CURRENT_DATA:
|
||||||
|
case HTMLMediaElement.HAVE_FUTURE_DATA:
|
||||||
|
case HTMLMediaElement.HAVE_ENOUGH_DATA:
|
||||||
|
onMediaLoaded(media);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
media.addEventListener("loadeddata", () => onMediaLoaded(media));
|
||||||
|
media.addEventListener("error", function() {
|
||||||
|
setFallbackHeight(media);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustMediaPadding();
|
||||||
|
window.addEventListener("load", adjustMediaPadding);
|
||||||
|
window.addEventListener("resize", adjustMediaPadding);
|
||||||
|
|
||||||
|
function checkOffsets() {
|
||||||
|
const ignoredTagNames = new Set([
|
||||||
|
"THEAD",
|
||||||
|
"TBODY",
|
||||||
|
"TFOOT",
|
||||||
|
"TR",
|
||||||
|
"TD",
|
||||||
|
"TH",
|
||||||
|
]);
|
||||||
|
const cell = gridCellDimensions();
|
||||||
|
const elements = document.querySelectorAll("body :not(.debug-grid, .debug-toggle)");
|
||||||
|
for (const element of elements) {
|
||||||
|
if (ignoredTagNames.has(element.tagName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
if (rect.width === 0 && rect.height === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const top = rect.top + window.scrollY;
|
||||||
|
const left = rect.left + window.scrollX;
|
||||||
|
const offset = top % (cell.height / 2);
|
||||||
|
if(offset > 0) {
|
||||||
|
element.classList.add("off-grid");
|
||||||
|
console.error("Incorrect vertical offset for", element, "with remainder", top % cell.height, "when expecting divisible by", cell.height / 2);
|
||||||
|
} else {
|
||||||
|
element.classList.remove("off-grid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const debugToggle = document.querySelector(".debug-toggle");
|
||||||
|
function onDebugToggle() {
|
||||||
|
document.body.classList.toggle("debug", debugToggle.checked);
|
||||||
|
}
|
||||||
|
debugToggle.addEventListener("change", onDebugToggle);
|
||||||
|
onDebugToggle();
|
||||||
Reference in New Issue
Block a user