diff --git a/flake.lock b/flake.lock index 66cb95e..dfe9f89 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,25 @@ { "nodes": { + "alex": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs", + "nixpkgs-python": "nixpkgs-python" + }, + "locked": { + "lastModified": 1727894075, + "narHash": "sha256-2yGczQTBlMkImM95Q7fmrUX16CJ5y1OTe/KlyXwIo8g=", + "owner": "AlexanderGrooff", + "repo": "nix-flakes", + "rev": "248ec1e226de7e3cd86092312910158c1fea3402", + "type": "github" + }, + "original": { + "owner": "AlexanderGrooff", + "repo": "nix-flakes", + "type": "github" + } + }, "crane": { "locked": { "lastModified": 1744386647, @@ -15,21 +35,19 @@ "type": "github" } }, - "flake-parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, + "flake-compat": { + "flake": false, "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { - "owner": "hercules-ci", - "repo": "flake-parts", + "owner": "edolstra", + "repo": "flake-compat", "type": "github" } }, @@ -55,6 +73,59 @@ "inputs": { "systems": "systems_2" }, + "locked": { + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_3": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, + "flake-utils_4": { + "inputs": { + "systems": "systems_4" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_5": { + "inputs": { + "systems": "systems_5" + }, "locked": { "lastModified": 1731533236, "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", @@ -72,8 +143,8 @@ "markdoll": { "inputs": { "crane": "crane", - "flake-utils": "flake-utils_2", - "nixpkgs": "nixpkgs_2", + "flake-utils": "flake-utils_5", + "nixpkgs": "nixpkgs_5", "rust-overlay": "rust-overlay" }, "locked": { @@ -90,13 +161,97 @@ "url": "https://codeberg.org/0x57e11a/markdoll.git" } }, + "mermaid-ascii": { + "inputs": { + "alex": "alex", + "flake-utils": "flake-utils_3", + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1729281716, + "narHash": "sha256-CkfYSDm5Mg+2EN42IiAh5F070Hi8octtB9FAoiHorLo=", + "owner": "AlexanderGrooff", + "repo": "mermaid-ascii", + "rev": "0a4a78057fbd9aff8db8746c54bb87a424bc4b53", + "type": "github" + }, + "original": { + "owner": "AlexanderGrooff", + "repo": "mermaid-ascii", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1745234285, - "narHash": "sha256-GfpyMzxwkfgRVN0cTGQSkTC0OHhEkv3Jf6Tcjm//qZ0=", + "lastModified": 1727893769, + "narHash": "sha256-RQIsGOy+7VzPJ4eHS2UENKGgGnW6WXQJqm+8d7BsRKc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c11863f1e964833214b767f4a369c6e6a7aba141", + "rev": "bc63f402fdf4ad861b18746174d4e1fd37fcc4b0", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-python": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1722978926, + "narHash": "sha256-sqOOEaKJJSUFBzag/cGeeXV491TrrVFY3DFBs1w20V8=", + "owner": "cachix", + "repo": "nixpkgs-python", + "rev": "7c550bca7e6cf95898e32eb2173efe7ebb447460", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "nixpkgs-python", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1719253556, + "narHash": "sha256-A/76RFUVxZ/7Y8+OMVL1Lc8LRhBxZ8ZE2bpMnvZ1VpY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "fc07dc3bdf2956ddd64f24612ea7fc894933eb2e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1727811607, + "narHash": "sha256-2ByOBflaIUJKeF9q6efVcYHljZXGZ7MnCWtseRvmpm8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1839883cd0068572aed75fb9442b508bbd9ef09c", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1747179050, + "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e", "type": "github" }, "original": { @@ -106,22 +261,7 @@ "type": "github" } }, - "nixpkgs-lib": { - "locked": { - "lastModified": 1743296961, - "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "nixpkgs_2": { + "nixpkgs_5": { "locked": { "lastModified": 1744536153, "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", @@ -137,7 +277,7 @@ "type": "github" } }, - "nixpkgs_3": { + "nixpkgs_6": { "locked": { "lastModified": 1744868846, "narHash": "sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs=", @@ -155,8 +295,9 @@ }, "root": { "inputs": { - "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs", + "flake-utils": "flake-utils", + "mermaid-ascii": "mermaid-ascii", + "nixpkgs": "nixpkgs_4", "teapot": "teapot" } }, @@ -212,18 +353,63 @@ "type": "github" } }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_4": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_5": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "teapot": { "inputs": { - "flake-utils": "flake-utils", + "flake-utils": "flake-utils_4", "markdoll": "markdoll", - "nixpkgs": "nixpkgs_3" + "nixpkgs": "nixpkgs_6" }, "locked": { - "lastModified": 1745381508, - "narHash": "sha256-mQYFQ8e0AnfsYcHO8O8oAbTrou6iOc3HR23hVguQVYs=", + "lastModified": 1747275871, + "narHash": "sha256-1GvVW/B27P3rWpck5Pu973HXSbH3zZbSwA2Jzb35eHA=", "ref": "refs/heads/main", - "rev": "a4faf2f38c06d238874fb88e2fca1bcad9a05c72", - "revCount": 8, + "rev": "f9dd88697493518bacae5c9fb5ff985b11946ee3", + "revCount": 15, "type": "git", "url": "https://git.sapphic.engineer/noe/teapot" }, diff --git a/flake.nix b/flake.nix index 770effd..92bac7b 100644 --- a/flake.nix +++ b/flake.nix @@ -1,36 +1,43 @@ { - description = "https://noe.sh"; + description = "https://blood.pet"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - flake-parts.url = "github:hercules-ci/flake-parts"; + flake-utils.url = "github:numtide/flake-utils"; teapot.url = "git+https://git.sapphic.engineer/noe/teapot"; + mermaid-ascii.url = "github:AlexanderGrooff/mermaid-ascii"; }; - outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { - systems = [ "x86_64-linux" "aarch64-linux" ]; - perSystem = { config, self', pkgs, lib, system, ... }: let - in rec { + outputs = { self, nixpkgs, flake-utils, ... }@inputs: flake-utils.lib.eachDefaultSystem ( + system: let + pkgs = import nixpkgs { + inherit system; + overlays = [ + inputs.teapot.overlays.default + (final: prev: { + mermaid-ascii = inputs.mermaid-ascii.packages.${system}.default; + }) + ]; + }; + in { devShells.default = import ./shell.nix { inherit pkgs; }; - apps.default = apps.dev; - apps.dev = pkgs.writeShellScript "noe.sh" '' - ${pkgs.python3} -m http.server -d ${packages.default} - ''; - - packages.default = inputs.teapot.packages.${system}.brewTea { - name = "noe.sh"; + packages.default = pkgs.brewTea { + name = "blood.pet"; version = "0.0.0"; src = ./.; layoutSrc = ./layouts; + staticAssets = [ "./static" ]; - markdollPlugins = let - mkMarkdollPlugin = inputs.teapot.packages.${system}.mkMarkdollPlugin; - in [ - (mkMarkdollPlugin "utils" ./plugins/utils.sh) + markdollPlugins = [ + (pkgs.mkMarkdollPlugin "utils" ./plugins/utils.sh) + (pkgs.mkMarkdollPlugin "logroll" ./plugins/logroll.sh) + + (pkgs.mkMarkdollPlugin "mermaid" ./plugins/mermaid.sh) + pkgs.mermaid-ascii ]; }; - }; - }; + } + ); } \ No newline at end of file diff --git a/layouts/default.html b/layouts/default.html index f0c698e..42007a1 100644 --- a/layouts/default.html +++ b/layouts/default.html @@ -2,29 +2,7 @@ %%TITLE%%:41666 - + %%HEAD%% diff --git a/log/001-static-sites-rework.doll b/log/001-static-sites-rework.doll new file mode 100644 index 0000000..6582644 --- /dev/null +++ b/log/001-static-sites-rework.doll @@ -0,0 +1,283 @@ +--- +title: static_sites_rework log[001] +description: getting automated deployments back in the dollhive +--- + +&static sites rework (14MAY2025) ^a+n + + so like this site,, its a bunch of .doll files, compiled down to .html. + + it has other sites too, like noe.sh, which are a small blob of .html, .js, and .css. + + and also 3d.noe.sh, which is a nightmare transpiled app, but its still outputting .html, .js, and .css. + + it used to be pretty into cloudflare pages and vercel, both automated deployment platforms + that solve the same problems we're looking to solve. + + but, obviously these are corpo trash. what are they doing that we can't do? + + that's right. [em:nothing] + + &the original architecture + + so we've had a bunch of static sites already being hosted. we'll focus on blood.pet specifically. + + as a user, the static-sites VM is what serves blood.pet + + [invoke(mermaid)(-x 2 -y 2 -p 0):: + flowchart LR + user --> ingress-proxy --> static-sites --> nix store + ] + + as a doll, deployment was a 3 step process + + [invoke(mermaid)(-x 2 -y 2 -p 0):: + flowchart LR + commit --> flake update --> deploy static-sites + ] + + this also meant if its wife wanted to update its own site, wife would need to bother + this doll, and hope the adhd doesn't kick in. + + the node itself looks like this at this point + + [invoke(mermaid)(-x 2 -y 2 -p 0):: + flowchart TD + https://blood.pet -->|static-sites| nginx + nginx --> nix-store + deploy -->|nixos-rebuild| nix-store + ] + + + &the corporate carcinization of sin + + ok so if it was at work, this is all unacceptable. + + we'd be using a CI/CD system and upload assets to S3 and the + S3 would maybe have CloudFront or S3 Website mode in front... + + cloudflare pages is all of this; they have a CI-like tool and + that uploads to their S3 store and their CDN handles the fun. + [em:it's literally the same_] + + ok so fuck AWS parts of that, lets replicate each bit. + + - for the bucket, we have [link(https://min.io):minio]. it acts like S3. perfect. + - for CI/CD, we have a lot of options. we boiled it down to these two: + - [link(https://forgejo.org/docs/next/user/actions/):forgejo actions] + - the "actions" ecosystem is okay but we wanted to cut down on runtime dependencies. + - [link(https://woodpecker-ci.org/):woodpecker CI] + - we chose woodpecker. it is similar to "actions" but offers more direct control, we like this. + - we already have nginx to be our "front of house", so.. yay + + &the platform-y parts + + we deployed minio with a shockingly simple + + [codeblock:: + services.minio = { + enable = true; + rootCredentialsFile = /secrets/yay; # it uses sops-nix here + }; + ] + + this deploys minio to S3 port :9000, and web UI to port :9001. cake. + + we set up our blood.pet bucket (remember to allow anonymous reads to *) + + so nginx and minio need to talk to each other. we decided to keep these on the same machine, as + minio will not be serving public traffic at all directly. (and we don't want it to.) + + [em:minio is just really loud on its own] + + like check this; this is what a curl to blood.pet/index.html looks like if minio serves it. + + [//:this would be a codeblock; but it wanted to have the bold for effect] + [invoke(cat):: +
+				HTTP/1.1 200 OK
+				Accept-Ranges: bytes
+				Content-Length: 25956
+				Content-Type: text/html
+				ETag: "e00f624fab315d9e804999ab2994f16c"
+				Last-Modified: Wed, 14 May 2025 07:20:42 GMT
+				Server: MinIO
+				Strict-Transport-Security: max-age=31536000; includeSubDomains
+				Vary: Origin
+				Vary: Accept-Encoding
+				X-Amz-Bucket-Region: us-east-1
+				X-Amz-Id-2: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8
+				X-Amz-Request-Id: 183F92F40CF892BF
+				X-Content-Type-Options: nosniff
+				X-Ratelimit-Limit: 583
+				X-Ratelimit-Remaining: 583
+				X-Xss-Protection: 1; mode=block
+				x-amz-meta-s3cmd-attrs: atime:1747207238/ctime:1747207238/gid:0/gname:root/md5:e00f624fab315d9e804999ab2994f16c/mode:33060/mtime:1/uid:0/uname:root
+				Date: Thu, 15 May 2025 02:39:17 GMT
+				
+ ] + + the headers include everything from the s3cmd user (root because docker) to random AWS IDs. + + this is where our bestie nginx comes in. + + [em:(and yo just so its clear, we use nix a lot in the dollhive.)] + [codeblock:: + services.nginx.virtualHosts."blood.pet" = { + locations."/" = { + recommendedProxySettings = true; + proxyPass = "http://127.0.0.1:9000/blood.pet/"; # trailing slash Required. + extraConfig = '' + # try to catch errors + proxy_intercept_errors on; + + # minio why so fingerprintable.... + proxy_hide_header x-amz-request-id; + proxy_hide_header x-amz-bucket-region; + proxy_hide_header x-amz-id-2; + proxy_hide_header x-amz-meta-s3cmd-attrs; + proxy_hide_header x-ratelimit-limit; + proxy_hide_header x-ratelimit-remaining; + proxy_hide_header x-minio-deployment-id; + proxy_hide_header strict-transport-security; + proxy_hide_header x-firefox-spdy; + proxy_hide_header x-xss-protection; + proxy_hide_header x-content-type-options; + proxy_hide_header vary; + + # prevent minio fingerprinting back... + proxy_set_header user-agent ""; + + # and especially dont POST its server omg + proxy_method GET; + + # fix example: https://blood.pet/ to request /blood.pet/index.html + # fix example: https://blood.pet/pronouns/ to request /blood.pet/pronouns/index.html + rewrite (.*)/$ $1/index.html; + ''; + }; + }; + ] + + so we can just kinda yeet all those out. + + nginx now lives as our "CDN" layer. nice. its job is to do all the internal-to-external bridging to minio. + + &a bird in the hand... + + woodpecker is a different story; we want this in its own machine, separate and safe away from + the common dangers of the Proxmox cluster or even just the internet. + + we use forgejo so we'll also cover configuration for that. + + [codeblock:: + # just so its there when we SSH + environment.systemPackages = [ + pkgs.woodpecker-cli + ]; + + services.woodpecker-server = { + enable = true; + environment = { + WOODPECKER_HOST = "https://"; + WOODPECKER_SERVER_ADDR = ":9001"; + WOODPECKER_GRPC_PORT = ":9000"; + WOODPECKER_OPEN = "true"; + WOODPECKER_FORGEJO = "true"; + WOODPECKER_FORGEJO_URL = "https://"; + WOODPECKER_ADMIN = "noe"; + }; + environmentFile = /secrets/yay; + # get these from /user/settings/applications + # WOODPECKER_FORGEJO_CLIENT=awawawawa + # WOODPECKER_FORGEJO_SECRET=dolldolldoll + }; + + services.woodpecker-agents.agents."podman" = { + enable = true; + environment = { + WOODPECKER_SERVER = "localhost:9000"; + WOODPECKER_BACKEND = "docker"; + WOODPECKER_MAX_WORKFLOWS = "4"; + DOCKER_HOST = "unix:///run/podman/podman.sock"; + WOODPECKER_HEALTHCHECK = "false"; + WOODPECKER_GRPC_SECURE = "false"; + }; + extraGroups = [ "podman" ]; + environmentFile = [ /secrets/yay ]; + # get this from /admin/agents + # WOODPECKER_AGENT_SECRET=dolldolldollawawawawawa + }; + + virtualisation.podman = { + enable = true; + defaultNetwork.settings = { + dns_enabled = true; + }; + autoPrune = { + enable = true; + dates = "daily"; + }; + }; + ] + + and just like that, we have our woodpecker thing going. awawa!!! + + &the final piece, automation + + we made it!!! + + go make an access key in minio, and save those as secrets in woodpecker! + we named it like [code:static_sites_client] but anything's cool. + + so our last step is to make a step file, [code:.woodpecker/build-deploy.yaml] + + [codeblock:: + when: + - event: push + branch: main + + steps: + - name: build & deploy + image: nixos/nix + commands: + - echo 'experimental-features = flakes nix-command' >> /etc/nix/nix.conf + - nix build -L . + - | + nix-shell -p s3cmd --command 's3cmd \ + --host=static-sites:9000 \ + --host-bucket=static-sites:9000 \ + --no-ssl \ + sync result/* s3://blood.pet' + environment: + AWS_ACCESS_KEY_ID: + from_secret: static_sites_client + AWS_SECRET_ACCESS_KEY: + from_secret: static_sites_secret + ] + + what's going on here? + = we tell nix it should know what a "flake" is + = we build it (with -L for live logs) + = we use s3cmd to upload it + - we set `--host` and `--host-bucket` to the same thing, this isn't S3 + - disable SSL because haha doll + + &ad extremum + + so our server architecture now looks like + + [invoke(mermaid)(-x 2 -y 2 -p 0):: + flowchart TD + https://blood.pet -->|static-sites| nginx + nginx -->|http| minio + git commit --> woodpecker + woodpecker -->|s3cmd| minio + ] + + also you're looking at this site, all deployed with the above :> + + &links + - blood.pet on git: [link(https://git.sapphic.engineer/noe/blood.pet/):noe/blood.pet] + - static-sites config: [link(https://git.sapphic.engineer/noe/nixos/src/branch/main/nixos/hosts/static-sites):noe/nixos:/nixos/hosts/static-sites] + - woodpecker config: [link(https://git.sapphic.engineer/noe/nixos/src/branch/main/nixos/hosts/woodpecker/default.nix):noe/nixos:/nixos/hosts/woodpecker/default.nix] \ No newline at end of file diff --git a/log/index.doll b/log/index.doll new file mode 100644 index 0000000..3cb1890 --- /dev/null +++ b/log/index.doll @@ -0,0 +1,6 @@ +--- +title: ls log +description: 41666 logs +--- + +[invoke(logroll)] \ No newline at end of file diff --git a/plugins/logroll.sh b/plugins/logroll.sh new file mode 100755 index 0000000..eff63e2 --- /dev/null +++ b/plugins/logroll.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +cd log + +echo "
    " + +for file in "$(ls -1 ???-*.doll)"; do + echo "
  • " + + echo "${file//.doll/}" + + echo "
  • " +done + +echo "
" \ No newline at end of file diff --git a/plugins/mermaid.sh b/plugins/mermaid.sh new file mode 100755 index 0000000..5914f76 --- /dev/null +++ b/plugins/mermaid.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "
"
+mermaid-ascii --file /dev/stdin $@
+echo "
" \ No newline at end of file diff --git a/plugins/plural.sh b/plugins/plural.sh new file mode 100755 index 0000000..cc1f786 --- /dev/null +++ b/plugins/plural.sh @@ -0,0 +1 @@ +#!/bin/bash \ No newline at end of file diff --git a/static/main.css b/static/main.css new file mode 100644 index 0000000..f8cef2e --- /dev/null +++ b/static/main.css @@ -0,0 +1,55 @@ +:root { + --red1: #a20000; + --red2: #c20000; + --dark1: #071213; + --dark2: #000000; + --bright1: #aed3d4; + --middle1: #3f7c59; +} + +body { + background-color: var(--dark2); + color: var(--red2); + font-family: monospace; + font-size: 1.012rem; + margin: 0; + padding: 0; +} + +main { + margin: 2rem; + width: 780px; + max-width: calc(100vw - 4rem); +} + +h1, +h2, +h3, +h4, +h5, +h6 { + color: var(--bright1); + margin-top: 2em; +} + +code { + background-color: var(--dark1); + padding: 0.1rem; + border-radius: 2px; + color: var(--middle1); +} + +.doll-inline { + margin-bottom: 1rem; +} + +.doll-code-block { + background-color: var(--dark1); + padding: 0.4rem 1rem; + color: var(--middle1); + border-left: 1px solid var(--red1); + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; + overflow-x: scroll; + tab-size: 2; +}