redo a lot of things

This commit is contained in:
notgne2 2023-08-15 09:33:36 -07:00
parent 7d6d0c79e1
commit a911ee93f8
Signed by: notgne2
SSH Key Fingerprint: SHA256:iSW0TFXEwbewdtLmWZs+/zMwxrpB5SMBjoCVDv9Rd6I
16 changed files with 5118 additions and 402 deletions

View File

@ -1 +1,40 @@
I promise I'm not as bad at writing code as I am at writing about pages
> Software engineer and SRE lead with 7+ years of experience designing, developing, and deploying high-quality, stable, and scalable software solutions.
### CV/Resume
If you're looking for my resume, you won't find it here. If you're interested in hiring me, then you can email me for a copy, or find it on my LinkedIn or Wellfound profile.
What you're going to find here, is going to be a lot more casual, incomprehensible, and just overall baffling. You can save yourself the confusion by closing the tab now. If this is your prefered means of consuming information, feel free to check out [skills](#skills) and [my work](/#my-work).
### _literally who_
I'm a person[[citation needed](https://en.wikipedia.org/wiki/Wikipedia:Citation_needed)] that does things on computers. Sometimes I fix them, break them, make them do things you want them to do, or make them do things I want them to do.
If you want to hire me, I am proud to speak the tongue of the demons we trapped inside the crushed up grains of sand you're using to browse this website.
#### Skills
My first skill would be always finding the time to interject that I don't really believe in tooling experience as a gauge of capability at all, but I've decided to list it separately. The fact that it's ambiguous what does and doesn't belong under "skills" is proof of this issue, but my personal criterea is "used a significant amount, in a significant way, had no issues understanding or using"
**Note: list is limited by what happened to come to mind at the time**
Personal favourites bolded, though I'm not much of an evangelist or ideologue, I just like to get things done.
- **Rust**, Python, **OCaml**, Nim, Go, C++, JavaScript/TypeScript, PureScript, **Elm**
- **NixOS**, CentOS, Debian, **deploy-rs**, NixOps, **Terraform**, Alpine, **Nomad**, Buildroot
- Docker, **Podman**, Firecracker, LXD, **Linux namespaces**, **cgroups**, QEMU, **KVM**, Hyper-V
- **Wireguard**, **Tailscale**, OpenVPN, **Prometheus**, AlertManager, **Grafana**, Kibana, **Vault**, **Consul**
- **PostgreSQL**, MySQL, **SQLite**, MongoDB, CouchDB, RethinkDB, **Redis**, Memcached, RabbitMQ, AWS SQS
- HTML, CSS, RiotJS, EdenJS, Svelte, Vue, React/Redux, **elm-ui**, **GraphQL**
### wait are you the guy wh-
You could be about to ask about 100s of things. I have no idea which one, but the answer is probably yes.
### do you really really get emails here?
Yes. I do. If you reached this page, and still don't know the answer to this, then chances are I have been arguing with you about it for a very long time now. You seen I have a website, you have no reason not to believe me at this point.
#### but your email starts wit-
I know. It works. Trust me.

View File

@ -1,17 +1,18 @@
# notgne2's pages
**_Yes, it's a real website, and no I didn't make up a fake email address_**
## 🍆 ...
## 🤔...
Passionate shitposter and software engineer.
I make things for computers to run, and make computers run things.
Sometimes I have posts [here](/posts/) (last updated: Mar 25 2022) but I don't make many.
Sometimes I write posts [here](/posts/) (last updated: Mar 25 2022) but I don't make many.
## My work
I have worked significant amounts on various projects, most recently and/or notably being:
- [deploy-rs](https://github.com/serokell/deploy-rs)
- [gm8.app](https://gm8.app) - Service for purchasing and managing servers over Discord
- [flox](https://github.com/flox/flox) - User friendly Nix-based environment manager
- [deploy-rs](https://github.com/serokell/deploy-rs) - A simple multi-profile Nix-flake deploy tool.
- [ffs](https://github.com/notgne2/ffs) - A tagging filesystem you can actually use
- [DiscordBots](https://discordbots.org/) - Popular index of Discord bots and servers (now [Top.gg](https://top.gg))
- [Tox](https://tox.chat) - FOSS Secure messaging application
- [Continix](https://github.com/notgne2/continix) - Tool to use NixOS configurations to generate Docker images
@ -20,6 +21,7 @@ I have worked significant amounts on various projects, most recently and/or nota
## Contact me
- Matrix: [@notgne2:wizbos.club](https://matrix.to/#/@notgne2:wizbos.club)
- Rizon/Freenode: [notgne2](#)
- Libera IRC: [notgne2](#)
- Email: [gen2@gen2.space](mailto:gen2@gen2.space)
- Discord: [notgne2#0240](#)
- Discord: [notgne2](#)

View File

@ -6,11 +6,12 @@
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"dillonkearns/elm-markdown": "1.1.3",
"dillonkearns/elm-markdown": "7.0.1",
"elm/browser": "1.0.2",
"elm/core": "1.0.2",
"elm/html": "1.0.0",
"elm/http": "2.0.0",
"elm/regex": "1.0.0",
"elm/url": "1.0.0",
"mdgriffith/elm-ui": "1.1.5"
},
@ -28,4 +29,4 @@
"direct": {},
"indirect": {}
}
}
}

101
flake.lock generated
View File

@ -16,19 +16,43 @@
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"gitignore": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1606424373,
"narHash": "sha256-oq8d4//CJOrVj+EcOaSXvMebvuTkmBJuT5tzlfewUnQ=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "99f1c2157fba4bfe6211a321fd0ee43199025dbf",
"lastModified": 1660459072,
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nix-npm-buildpackage": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1686315622,
"narHash": "sha256-ccqZqY6wUFot0ewyNKQUrMR6IEliGza+pjKoSVMXIeM=",
"owner": "serokell",
"repo": "nix-npm-buildpackage",
"rev": "991a792bccd611842f6bc1aa99fe80380ad68d44",
"type": "github"
},
"original": {
"owner": "serokell",
"repo": "nix-npm-buildpackage",
"type": "github"
}
},
@ -48,28 +72,13 @@
"type": "github"
}
},
"pnpm2nix": {
"flake": false,
"locked": {
"lastModified": 1613358015,
"narHash": "sha256-ZitojlLnQkP+1Gy4kcuW6jPV2B9a8ce43LAYUnSsCZo=",
"ref": "master",
"rev": "460e356b5ea58b4e9780b49afc9e77f98d8d202b",
"revCount": 91,
"type": "git",
"url": "ssh://git@github.com/notgne2/pnpm2nix.git"
},
"original": {
"type": "git",
"url": "ssh://git@github.com/notgne2/pnpm2nix.git"
}
},
"root": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nix-npm-buildpackage": "nix-npm-buildpackage",
"nixpkgs": "nixpkgs",
"utils": "utils",
"wand": "wand"
"utils": "utils"
}
},
"utils": {
@ -86,44 +95,6 @@
"repo": "flake-utils",
"type": "github"
}
},
"utils_2": {
"locked": {
"lastModified": 1610051610,
"narHash": "sha256-U9rPz/usA1/Aohhk7Cmc2gBrEEKRzcW4nwPWMPwja4Y=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "3982c9903e93927c2164caa727cd3f6a0e6d14cc",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"wand": {
"inputs": {
"flake-compat": "flake-compat_2",
"nixpkgs": [
"nixpkgs"
],
"pnpm2nix": "pnpm2nix",
"utils": "utils_2"
},
"locked": {
"lastModified": 1613358124,
"narHash": "sha256-LDiOa7rSG7MyUAdpry1LUgmSpnEzFPEuQncc9bIE/Us=",
"ref": "master",
"rev": "dfd6e75545e054e4ec5b2c9bf0e48bd2f3bb6949",
"revCount": 67,
"type": "git",
"url": "ssh://git@git.wizbos.club/wizbos-pub/wand-front-utils.git"
},
"original": {
"type": "git",
"url": "ssh://git@git.wizbos.club/wizbos-pub/wand-front-utils.git"
}
}
},
"root": "root",

107
flake.nix
View File

@ -2,33 +2,102 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
wand.url = "git+ssh://git@git.wizbos.club/wizbos-pub/wand-front-utils.git";
wand.inputs.nixpkgs.follows = "nixpkgs";
utils.url = "github:numtide/flake-utils";
nix-npm-buildpackage = {
url = "github:serokell/nix-npm-buildpackage";
inputs.nixpkgs.follows = "nixpkgs";
};
gitignore = {
url = "github:hercules-ci/gitignore.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
flake-compat = {
url = "github:edolstra/flake-compat";
flake = false;
};
};
outputs = {
self,
nixpkgs,
utils,
nix-npm-buildpackage,
gitignore,
...
}:
utils.lib.eachDefaultSystem (system: let
pkgs = import nixpkgs {
inherit system;
overlays = [nix-npm-buildpackage.overlays.default];
};
outputs = { self, nixpkgs, utils, wand, ... }:
utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in
{
packages.gen2space = wand.lib.${system}.mkWandardFront {
src = ./.;
name = "gen2.space";
description = "notgne2's shitty pages";
backgroundColor = "#220044";
themeColor = "#004400";
routes =
[ "/" "/about" "/404" "/posts" "/posts/viveahk" "/posts/librebasics" ];
src = gitignore.lib.gitignoreSource ./.;
nodeDependencies = with pkgs; [nasm zlib optipng libpng libtool automake autoconf vips pkgconfig python3 nodePackages.node-gyp];
buildVars = {
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "1";
PUPPETEER_EXECUTABLE_PATH = "${pkgs.chromium}/bin/chromium";
};
preBuild = ''
export LD=$CC
'';
in {
packages.gen2-space-front-raw = pkgs.buildNpmPackage {
inherit src;
dontNpmPrune = true;
extraNodeModulesArgs = {
buildInputs = nodeDependencies;
};
defaultPackage = self.packages.${system}.gen2space;
});
extraEnvVars = buildVars;
};
packages.gen2-space-front = pkgs.stdenv.mkDerivation ({
name = "gen2-space-front";
src = self.packages.${system}.gen2-space-front-raw;
buildInputs = with pkgs; [nodejs elmPackages.elm];
configurePhase = pkgs.elmPackages.fetchElmDeps {
elmVersion = "0.19.1";
registryDat = ./registry.dat;
elmPackages = import ./elm-srcs.nix;
};
buildPhase = ''
mkdir dist
node $src/node_modules/vite/bin/vite.js build
'';
installPhase = ''
mv dist $out
'';
}
// buildVars);
packages.default = self.packages.${system}.gen2-space-front;
devShells.default = pkgs.mkShell ({
inputsFrom = builtins.attrValues self.packages.${system};
shellHook = ''
export LD=$CC
'';
buildInputs =
nodeDependencies
++ (with pkgs; [
elmPackages.elm
elmPackages.elm-format
elmPackages.elm-review
elm2nix
nodejs
]);
}
// buildVars);
});
}

20
index.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<style>
</style>
<title></title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/index.js"></script>
</body>
</html>

66
index.js Normal file
View File

@ -0,0 +1,66 @@
import { Elm } from './src/Main.elm'
if (typeof window === 'undefined') {
global.window = {};
}
function _check_webp_feature(feature, callback) {
var kTestImages = {
lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA",
lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",
alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
};
var img = new Image();
img.onload = function () {
var result = (img.width > 0) && (img.height > 0);
callback(feature, result);
};
img.onerror = function () {
callback(feature, false);
};
img.src = "data:image/webp;base64," + kTestImages[feature];
}
async function getWebpFeatures() {
return (await Promise.all(["lossy", "lossless", "alpha"].map((f) => {
return new Promise((resolve, _) => {
_check_webp_feature(f, (_, s) => resolve(s ? f : null));
})
}))).filter(f => f !== null);
}
; (async () => {
const webpFeatures = await getWebpFeatures();
const images = {
// icon: (new URL("/data/images/icon.png", import.meta.url)).href,
// logo: (new URL("/data/images/logo.png", import.meta.url)).href,
// banners: {
// 1: (new URL("/data/images/banners/1.jpg", import.meta.url)).href,
// },
};
const lossyWebps = {
};
// use lossless=true
const losslessWebps = {
};
const allImages = {
...images,
...(webpFeatures.includes("lossless") ? losslessWebps : {}),
...(webpFeatures.includes("lossy") ? lossyWebps : {}),
};
const app = Elm.Main.init({
flags: {
images: allImages,
}
});
})()

4376
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,33 @@
{
"dependencies": {},
"name": "gen2-space",
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "notgne2",
"license": "AGPL-3.0",
"description": ""
}
"name": "gen2-space",
"description": "gen2.space",
"version": "1.2.0",
"color": "#004400",
"displayName": "gen2.space",
"author": "notgne2",
"license": "UNLICENSED",
"type": "module",
"scripts": {
"watch": "vite watch",
"build": "vite build"
},
"devDependencies": {
"sharp": "^0.32.1",
"svgo": "^3.0.2",
"vite": "^4.3.9",
"vite-imagetools": "^5.0.4",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-elm": "^2.9.0-beta.1",
"vite-plugin-html-config": "^1.0.11",
"vite-plugin-pwa": "^0.15.1",
"ws": "^8.13.0"
},
"routes": [
"/"
],
"alias": {
"process": {
"global": "process"
}
}
}

View File

@ -1,19 +1,23 @@
module MParser exposing (ezmd)
import Element exposing (..)
import Element.Background
import Element.Border
import Element.Background as Background
import Element.Border as Border
import Element.Font as Font
import Element.Input as Input
import Html
import Html.Attributes
import Markdown.Block as Block exposing (Block, Inline, ListItem(..), Task(..))
import Markdown.Html
import Markdown.Parser
import Markdown.Renderer
import SiteComponents exposing (..)
ezmd input =
case markdownView input of
Ok rendered ->
Element.column [ Element.spacing 30 ] rendered
Element.column [ Element.spacing 25 ] rendered
Err errors ->
Element.text errors
@ -24,104 +28,150 @@ markdownView markdown =
markdown
|> Markdown.Parser.parse
|> Result.mapError (\error -> error |> List.map Markdown.Parser.deadEndToString |> String.join "\n")
|> Result.andThen (Markdown.Parser.render renderer)
|> Result.andThen (Markdown.Renderer.render renderer)
renderer : Markdown.Parser.Renderer (Element msg)
renderer : Markdown.Renderer.Renderer (Element msg)
renderer =
{ heading = heading
, raw = Element.paragraph [ Element.spacing 15 ]
, thematicBreak = Element.none
, plain = Element.text
, bold = \content -> Element.row [ Font.bold ] [ Element.text content ]
, italic = \content -> Element.row [ Font.italic ] [ Element.text content ]
, code = code
, paragraph = paragraph [ spacing 10 ]
, thematicBreak = none
, text = text
, strong = \content -> row [ Font.bold, Font.color basedBrightText ] [ paragraph [] content ]
, emphasis = \content -> row [ Font.italic ] [ paragraph [] content ]
, strikethrough = \content -> row [ Font.strike ] [ paragraph [] content ]
, codeSpan = code
, link =
\{ destination } body ->
(if List.member (String.left 1 destination) [ "#", "/" ] then
Element.link
link
else
Element.newTabLink
newTabLink
)
[ Element.htmlAttribute (Html.Attributes.style "display" "inline-flex") ]
[ htmlAttribute (Html.Attributes.style "display" "inline-flex") ]
{ url = destination
, label = Element.paragraph [ Font.color basedGreen, Font.underline ] body
, label = paragraph [ Font.color basedGreen, Font.underline ] body
}
|> Ok
, hardLineBreak = Html.br [] [] |> Element.html
, image =
\image body ->
Element.image [ Element.width (Element.maximum 700 Element.fill) ] { src = image.src, description = body }
|> Ok
, list =
\i ->
image [ width (maximum 700 fill) ] { src = i.src, description = i.alt }
, blockQuote =
\children ->
row []
[ el [ width (px 10), height fill, patternBack basedPurple ] none
, paragraph
[ padding 10
, patternBack basedNavBack
]
children
]
, unorderedList =
\items ->
Element.column [ Element.spacing 15 ]
column [ paddingXY 10 0, spacing 5 ]
(items
|> List.map
(\itemBlocks ->
Element.row [ Element.spacing 5 ]
[ Element.el
[ Element.alignTop ]
(Element.text "")
, itemBlocks
(\(ListItem task children) ->
paragraph
[ alignTop ]
((case task of
IncompleteTask ->
Input.defaultCheckbox False
CompletedTask ->
Input.defaultCheckbox True
NoTask ->
text ""
)
:: text " "
:: children
)
)
)
, orderedList =
\startingIndex items ->
column [ spacing 5 ]
(items
|> List.indexedMap
(\index itemBlocks ->
paragraph [ spacing 5 ]
[ paragraph [ alignTop ]
(text (String.fromInt (index + startingIndex) ++ " ") :: itemBlocks)
]
)
)
, codeBlock = codeBlock
, table = column []
, tableHeader =
column
[ Font.bold
, width fill
, Font.center
]
, tableBody = column []
, tableRow = row [ height fill, width fill ]
, tableHeaderCell =
\maybeAlignment children ->
paragraph
tableBorder
children
, tableCell =
\maybeAlignment children ->
paragraph
tableBorder
children
, html = Markdown.Html.oneOf []
}
rawTextToId rawText =
rawText
|> String.toLower
|> String.replace " " "-"
tableBorder =
[ Border.color basedGreen
, Border.width 1
, Border.solid
, paddingXY 6 13
, height fill
]
heading : { level : Int, rawText : String, children : List (Element msg) } -> Element msg
heading { level, rawText, children } =
let
fontSize =
case level of
1 ->
36
niceHeader
(case level of
Block.H1 ->
1
2 ->
24
Block.H2 ->
2
_ ->
20
in
paragraph
[ Element.htmlAttribute (Html.Attributes.attribute "name" (rawTextToId rawText))
, Element.htmlAttribute (Html.Attributes.id (rawTextToId rawText))
, width fill
, height shrink
, padding 10
, greenLines
, Font.size fontSize
]
Block.H3 ->
3
_ ->
4
)
rawText
children
code : String -> Element msg
code snippet =
Element.el
[ Element.Background.color (Element.rgba 0 0 0 0.04)
el
[ Background.color (rgba 0 0 0 0.04)
, scrollbars
, Element.Border.rounded 2
, Element.paddingXY 5 3
, Border.rounded 2
, paddingXY 5 3
]
(Element.text snippet)
(text snippet)
codeBlock : { body : String, language : Maybe String } -> Element msg
codeBlock details =
Element.el
[ Element.Background.color (Element.rgba 0 0 0 0.03)
el
[ Background.color (rgba 0 0 0 0.03)
, scrollbars
, Element.htmlAttribute (Html.Attributes.style "white-space" "pre")
, Element.padding 20
, Element.width Element.fill
, htmlAttribute (Html.Attributes.style "white-space" "pre")
, padding 20
, width fill
]
(Element.text details.body)
(text details.body)

View File

@ -4,6 +4,7 @@ import Browser
import Browser.Navigation as Nav
import Dict exposing (Dict)
import Element exposing (..)
import Element.Background as Background
import Element.Font as Font
import Http
import List
@ -159,6 +160,7 @@ view model =
, Font.typeface "monospace"
, Font.monospace
]
, Font.size 16
]
(page model)
]
@ -169,7 +171,7 @@ navbarHeader =
link
[ centerX
, centerY
, Font.size 30
, Font.size 35
]
{ url = "/", label = image [ width fill ] { src = "/data/images/icon.png", description = "gen2 space" } }
@ -178,7 +180,7 @@ navbarLinks =
List.map
(\pageLink ->
link
[ Font.color basedPurple ]
[ Font.color basedGreen ]
{ url = "/" ++ pageLink
, label = text ("/" ++ pageLink)
}
@ -196,9 +198,9 @@ navbarLinks =
navbar =
column
[ width fill
, greenLines
, spacing 10
, padding 10
, patternBack basedNavBack
]
[ navbarHeader, navbarLinks ]
@ -207,11 +209,10 @@ page model =
column
[ width fill
, height fill
, linesBackground "#222222"
, spacing 20
, patternBack basedPurple
]
[ navbar
, body model
, el [ paddingXY 20 0, width fill, height fill ] (body model)
]
@ -224,7 +225,7 @@ posts =
postPreview ( name, data ) =
textColumn [ width fill, spacing 15 ] [ link [ Font.color basedPurple ] { url = "/posts/" ++ name, label = niceHeader ("Post: " ++ name) }, paragraph [] [ text data.subtext ] ]
textColumn [ width fill, spacing 15 ] [ link [] { url = "/posts/" ++ name, label = basicNiceHeader 2 ("Post: " ++ name) }, paragraph [] [ text data.subtext ] ]
postsText =
@ -243,15 +244,16 @@ postText model name =
notFoundLines
Just data ->
niceBigHeader name :: [ ezmd (Maybe.withDefault "" (Dict.get ("posts/" ++ name) model.sources)) ]
basicNiceHeader 1 name :: [ ezmd (Maybe.withDefault "" (Dict.get ("posts/" ++ name) model.sources)) ]
body model =
el
[ linesBackground "#220044"
, Font.color (rgb255 255 255 255)
, padding 30
, width (maximum 1300 fill)
[ Background.color basedBackground
, Font.color basedText
, paddingEach { top = 20, left = 20, bottom = 80, right = 20 }
, width (maximum 1200 fill)
, height fill
, centerX
]
(textColumn [ width fill, spacing 15 ]

View File

@ -1,8 +1,9 @@
module SiteComponents exposing (basedGreen, basedPurple, edges, greenLines, linesBackground, niceBigHeader, niceHeader)
module SiteComponents exposing (basedBackground, basedBrightText, basedGreen, basedNavBack, basedPurple, basedText, basicNiceHeader, edges, greenLines, niceHeader, patternBack, rawTextToId)
import Element exposing (..)
import Element.Font as Font
import Html.Attributes
import Regex
edges =
@ -14,38 +15,95 @@ edges =
basedPurple =
rgb255 40 0 90
rgb255 15 0 40
basedGreen =
rgb255 20 100 20
rgb255 5 150 5
linesBackground cssColor =
htmlAttribute (Html.Attributes.style "background" ("repeating-linear-gradient(" ++ cssColor ++ ", " ++ cssColor ++ " 1px, #2F2F2F 1px, #2F2F2F 2px)"))
basedBrightText =
rgb255 255 255 255
basedText =
rgb255 240 240 240
basedBackground =
rgb255 28 28 28
basedNavBack =
rgb255 20 20 20
patternBack color =
htmlAttribute <|
Html.Attributes.style "background"
(let
c =
toRgb color
cs cc =
String.fromInt (round (cc * 255))
fc =
"rgb(" ++ cs c.red ++ ", " ++ cs c.green ++ ", " ++ cs c.blue ++ ")"
in
"repeating-linear-gradient(90deg, " ++ fc ++ ", " ++ fc ++ "1px, #000 1px, #000 2px)"
)
greenLines =
linesBackground "#004400"
patternBack (rgb255 0 90 0)
niceHeader headerText =
el
[ width fill
rawTextToId rawText =
(case Regex.fromString "^[^a-zA-Z]|[^a-zA-Z0-9_:. -]" of
Nothing ->
rawText
Just regex ->
Regex.replace regex (\_ -> "") rawText
)
|> String.toLower
|> String.replace " " "-"
niceHeader level rawText children =
let
fontSize =
case level of
1 ->
36
2 ->
24
3 ->
20
_ ->
15
in
paragraph
[ width
(if level < 4 then
fill
else
shrink
)
, height shrink
, padding 10
, Font.size 24
, Font.size fontSize
, greenLines
, Element.htmlAttribute (Html.Attributes.attribute "name" (rawTextToId rawText))
, Element.htmlAttribute (Html.Attributes.id (rawTextToId rawText))
]
(text headerText)
children
niceBigHeader headerText =
el
[ width fill
, height shrink
, padding 10
, Font.size 30
, greenLines
]
(text headerText)
basicNiceHeader size t =
niceHeader size t [ text t ]

View File

@ -1,7 +0,0 @@
const { Elm } = require('./Main.elm');
Elm.Main.init({});
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => navigator.serviceWorker.register('/sw.js'));
}

243
vite.config.js Normal file
View File

@ -0,0 +1,243 @@
import { defineConfig } from "vite";
import elmPlugin from "vite-plugin-elm";
import { VitePWA } from "vite-plugin-pwa"
import viteCompression from "vite-plugin-compression"
// import vitePrerender from "vite-plugin-prerender"
// import prerender from '@prerenderer/rollup-plugin'
import { imagetools } from "vite-imagetools"
import htmlPlugin from "vite-plugin-html-config"
import { promises as fs } from "fs"
import path from "path"
// const Renderer = vitePrerender.PuppeteerRenderer;
/** @type {import("vite").UserConfig} */
export default defineConfig(async () => {
const pkg = JSON.parse(await fs.readFile(path.join(__dirname, "package.json")));
const lossyQuality = 85;
const prerenderOptions = {
staticDir: path.join(__dirname, "dist"),
routes: pkg.routes,
minify: {
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
collapseWhitespace: true,
decodeEntities: true,
sortAttributes: true,
sortClassName: true,
minifyCSS: true,
minifyJS: true,
removeAttributeQuotes: true,
removeComments: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
removeRedundantAttributes: true,
removeOptionalTags: true,
},
};
const host = process.env.URL || import.meta.url || "http://localhost";
const getURL = (path = "") => new URL(path, host).toString();
const getPath = (path = "") => {
const x = new URL(path, host);
return x.pathname + x.search;
// return await import("./" + path);
};
return {
plugins: [
htmlPlugin({
title: pkg.displayName,
// TODO .ico
favicon: getPath("/data/images/icon.png?h=16&w=16"),
links: [
{
rel: "apple-touch-icon",
href: getPath("/data/images/icon.png?h=180&w=180"),
sizes: "180x180",
},
{
rel: "icon",
href: getPath("/data/images/icon.png?h=32&w=32"),
sizes: "32x32",
type: "image/png",
},
{
rel: "icon",
href: getPath("/data/images/icon.png?h=16&w=16"),
sizes: "16x16",
type: "image/png",
},
{
rel: "mask-icon",
href: getPath("/data/images/icon.png?h=180&w=180"),
sizes: "180x180",
color: pkg.color,
},
{
rel: "icon",
href: getPath("/data/images/icon.png?h=16&w=16"),
sizes: "16x16",
},
// TODO .ico
{
rel: "icon",
href: getPath("/data/images/icon.png?h=16&w=16"),
sizes: "16x16",
},
],
metas: [
{
name: "description",
content: pkg.description,
},
{
name: "author",
content: pkg.author.name,
},
{
name: "theme-color",
content: pkg.color,
},
{
name: "og:url",
content: host,
},
{
name: "og:type",
content: "website",
},
{
name: "og:title",
content: pkg.displayName,
},
{
name: "og:description",
content: pkg.description,
},
{
name: "og:image",
content: getURL("/data/images/logo.png"),
},
{
name: "twitter:card",
content: "summary_large_image",
},
{
name: "twitter:domain",
content: "",
},
{
name: "twitter:url",
content: host,
},
{
name: "twitter:title",
content: pkg.displayName,
},
{
name: "twitter:description",
content: pkg.description,
},
{
name: "twitter:image",
content: getURL("/data/images/logo.png"),
},
],
}),
elmPlugin.plugin(),
VitePWA({
injectRegister: "auto",
registerType: "autoUpdate",
manifest: {
name: pkg.displayName,
// TODO better?
short_name: pkg.displayName,
description: pkg.description,
theme_color: pkg.color,
icons: [
{
src: getPath("/data/images/icon.png?h=192&w=192"),
sizes: "192x192",
type: "image/png",
},
{
src: getPath("/data/images/icon.png?h=512&w=512"),
sizes: "512x512",
type: "image/png",
},
{
src: getPath("/data/images/icon.png?h=512&w=512"),
sizes: "512x512",
type: "image/png",
purpose: "any maskable",
},
]
},
devOptions: {
enabled: true
}
}),
imagetools({
defaultDirectives: (url) => {
if (url.pathname.endsWith(".png")) {
return new URLSearchParams({
format: "webp",
lossless: true,
quality: 100,
});
}
if (url.pathname.endsWith(".l.png")) {
return new URLSearchParams({
format: "webp",
quality: lossyQuality,
});
}
if (url.pathname.endsWith(".jpg")) {
return new URLSearchParams({
format: "webp",
quality: lossyQuality,
});
}
return new URLSearchParams();
},
}),
viteCompression({
algorithm: "brotliCompress",
}),
// vitePrerender({
// ...prerenderOptions,
// outputDir: path.join(__dirname, "dist", "prerender", "mobile"),
// renderer: new Renderer({
// viewport: {
// isMobile: true,
// hasTouch: true,
// width: 450,
// height: 800,
// },
// }),
// }),
// vitePrerender({
// ...prerenderOptions,
// outputDir: path.join(__dirname, "dist", "prerender", "desktop"),
// renderer: new Renderer({
// viewport: {
// isMobile: false,
// hasTouch: false,
// width: 1500,
// height: 1000,
// },
// }),
// }),
],
}
});

View File

@ -1,4 +0,0 @@
document.body.innerHTML = ''
const sourceTarget = document.createElement('script')
sourceTarget.src = '/main.js'
document.body.appendChild(sourceTarget)

View File

@ -1,191 +0,0 @@
const ROUTES = ["/","/about","/404","/posts","/posts/viveahk","/posts/librebasics"]
const PRETTY_NAME = "gen2.space"
const DESCRIPTION = "notgne2's shitty pages"
const THEME_COLOR = "#004400"
const BACKGROUND_COLOR = "#220044"
const path = require('path')
const TerserPlugin = require('terser-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyPlugin = require('copy-webpack-plugin')
const { InjectManifest } = require('workbox-webpack-plugin')
const ImageminPlugin = require('imagemin-webpack-plugin').default
const ImageminWebpWebpackPlugin = require('imagemin-webp-webpack-plugin')
const FaviconsWebpackPlugin = require('favicons-webpack-plugin')
const { JSDOM } = require('jsdom')
const tempy = require('tempy')
const fs = require('fs')
const webpack = require('webpack')
const magicFile = (text) => {
const p = tempy.file()
fs.writeFileSync(p, text)
return p
}
const jsMin = {
mangle: true,
compress: {
keep_fargs: false,
pure_getters: true,
pure_funcs: ['F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9'],
unsafe: true,
ecma: 6,
unsafe_comps: true,
unsafe_arrows: true,
}
}
const htmlMin = {
minifyCSS: {
compatibility: 'ie9',
level: 2
},
minifyJS: jsMin,
collapseBooleanAttributes: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
removeComments: true,
sortAttributes: true,
sortClassName: true,
}
const productionPlugins = [
new InjectManifest({
importWorkboxFrom: 'local',
swSrc: magicFile(`
workbox.core.skipWaiting();
workbox.core.clientsClaim();
workbox.precaching.cleanupOutdatedCaches()
workbox.routing.registerNavigationRoute('/index.html');
workbox.precaching.precacheAndRoute(self.__precacheManifest);
`),
swDest: 'sw.js',
}),
new ImageminPlugin({
jpegtran: { progressive: true },
svgo: null,
}),
new ImageminWebpWebpackPlugin({
config: [{
test: /\.jpe?g/,
options: {
force: true,
quality: 75
}
}, {
test: /\.l\.png/,
options: {
force: true,
quality: 75
}
}, {
test: /\.png/,
options: {
force: true,
lossless: true
}
}],
overrideExtension: false,
detailedLogs: false,
silent: true,
strict: true,
}),
new FaviconsWebpackPlugin({
logo: path.join(__dirname, 'data', 'images', 'icon.png'),
inject: false, // manually in our template for now cos its fuckd
mode: 'webapp',
prefix: 'appdata',
favicons: {
appName: PRETTY_NAME,
appDescription: DESCRIPTION,
background: BACKGROUND_COLOR,
theme_color: THEME_COLOR,
}
}),
];
let plugins = (env, argv) => [
new webpack.HotModuleReplacementPlugin(),
new CopyPlugin([{ from: 'data', to: 'data' }, { from: 'wand.js', to: 'wand.js' }]),
new HtmlWebpackPlugin({
hash: true,
inject: true,
minify: argv.mode == 'production' ? htmlMin : false,
template: './template.html',
meta: {
viewport: 'width=320, initial-scale=1, maximum-scale=5',
description: DESCRIPTION,
}
}),
];
module.exports = (env, argv) => ({
target: 'web',
externals: {
svgo: 'svgo'
},
entry: magicFile(`
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => navigator.serviceWorker.register('/sw.js'));
}
require('${process.env.PWD}/src/index.js')
`),
output: {
filename: 'main.js',
path: path.join(__dirname, 'dist'),
publicPath: '/'
},
module: {
rules: [
{
test: /\.elm$/,
exclude: [/elm-stuff/, /node_modules/],
use: [
{
loader: 'elm-hot-webpack-loader',
options: {
debug: argv.mode != 'production',
}
},
{
loader: 'elm-webpack-loader',
options: {
optimize: argv.mode == 'production',
debug: argv.mode != 'production',
},
},
],
},
{
test: /\.html$/,
loader: 'html-loader',
options: {
minimize: true,
...htmlMin
},
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
devServer: {
inline: true,
hot: true,
stats: 'errors-only'
},
optimization: {
minimize: argv.mode == 'production',
minimizer: [
new TerserPlugin({ terserOptions: jsMin }),
new TerserPlugin({ terserOptions: jsMin }),
],
},
plugins: [...plugins(env, argv), ...(argv.mode == 'production' ? productionPlugins : [])],
})