feat: add tor service and style
feat: add tor service
This commit is contained in:
parent
f48390b15e
commit
7b842b3342
12 changed files with 203 additions and 24 deletions
84
flake.lock
generated
84
flake.lock
generated
|
|
@ -1,8 +1,53 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"agenix": {
|
||||||
|
"inputs": {
|
||||||
|
"darwin": "darwin",
|
||||||
|
"home-manager": "home-manager",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1770165109,
|
||||||
|
"narHash": "sha256-9VnK6Oqai65puVJ4WYtCTvlJeXxMzAp/69HhQuTdl/I=",
|
||||||
|
"owner": "ryantm",
|
||||||
|
"repo": "agenix",
|
||||||
|
"rev": "b027ee29d959fda4b60b57566d64c98a202e0feb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ryantm",
|
||||||
|
"repo": "agenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"darwin": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"agenix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1744478979,
|
||||||
|
"narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=",
|
||||||
|
"owner": "lnl7",
|
||||||
|
"repo": "nix-darwin",
|
||||||
|
"rev": "43975d782b418ebf4969e9ccba82466728c2851b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "lnl7",
|
||||||
|
"ref": "master",
|
||||||
|
"repo": "nix-darwin",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems"
|
"systems": "systems_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1731533236,
|
"lastModified": 1731533236,
|
||||||
|
|
@ -18,6 +63,27 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"home-manager": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"agenix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1745494811,
|
||||||
|
"narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "home-manager",
|
||||||
|
"rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "home-manager",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772542754,
|
"lastModified": 1772542754,
|
||||||
|
|
@ -52,6 +118,7 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"agenix": "agenix",
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
|
|
@ -89,6 +156,21 @@
|
||||||
"repo": "default",
|
"repo": "default",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
agenix.url = "github:ryantm/agenix";
|
||||||
|
agenix.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
outputs = { self, nixpkgs, rust-overlay, flake-utils }:
|
outputs = { self, nixpkgs, rust-overlay, flake-utils, agenix }:
|
||||||
(flake-utils.lib.eachDefaultSystem (system:
|
(flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
overlays = [ (import rust-overlay) ];
|
overlays = [ (import rust-overlay) ];
|
||||||
|
|
@ -81,7 +83,9 @@
|
||||||
pname = "jetpham-qa-api";
|
pname = "jetpham-qa-api";
|
||||||
version = "0.1.0";
|
version = "0.1.0";
|
||||||
src = ./api;
|
src = ./api;
|
||||||
cargoHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
cargoHash = "sha256-/EgiCn5N3E1tCcBWI3Sm3NGQt2h8l8yPwi/ZjporiVs=";
|
||||||
|
nativeBuildInputs = [ pkgs.pkg-config ];
|
||||||
|
buildInputs = [ pkgs.openssl ];
|
||||||
};
|
};
|
||||||
|
|
||||||
in {
|
in {
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body style="background:#000">
|
<body style="background:#000">
|
||||||
<canvas id="canvas" class="fixed top-0 left-0 -z-10 h-screen w-screen" aria-hidden="true"></canvas>
|
<canvas id="canvas" class="fixed top-0 left-0 -z-10 h-screen w-screen" aria-hidden="true"></canvas>
|
||||||
<nav class="flex justify-center px-4">
|
<nav aria-label="Main navigation" class="flex justify-center px-4">
|
||||||
<div class="relative px-[2ch] py-[1ch] mt-[2ch] w-full max-w-[66.666667%] min-w-fit">
|
<div class="relative px-[2ch] py-[1ch] mt-[2ch] w-full max-w-[66.666667%] min-w-fit">
|
||||||
<div
|
<div
|
||||||
class="pointer-events-none absolute inset-0"
|
class="pointer-events-none absolute inset-0"
|
||||||
|
|
@ -88,9 +88,8 @@
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></div>
|
></div>
|
||||||
<div class="absolute inset-0 border-2 border-white" aria-hidden="true"></div>
|
<div class="absolute inset-0 border-2 border-white" aria-hidden="true"></div>
|
||||||
<div class="relative z-10 flex gap-[2ch]">
|
<div class="relative z-10 flex justify-center gap-[2ch]">
|
||||||
<a href="/">[HOME]</a>
|
<a href="/">[HOME]</a>
|
||||||
<a href="/projects">[PROJECTS]</a>
|
|
||||||
<a href="/qa">[Q&A]</a>
|
<a href="/qa">[Q&A]</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
75
module.nix
75
module.nix
|
|
@ -16,6 +16,8 @@ in
|
||||||
description = "Domain to serve the website on.";
|
description = "Domain to serve the website on.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tor.enable = lib.mkEnableOption "Tor hidden service for the website";
|
||||||
|
|
||||||
envFile = lib.mkOption {
|
envFile = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.path;
|
type = lib.types.nullOr lib.types.path;
|
||||||
default = null;
|
default = null;
|
||||||
|
|
@ -36,6 +38,55 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
age.secrets.webhook-secret = {
|
||||||
|
file = "${self}/secrets/webhook-secret.age";
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
|
||||||
|
age.secrets.tor-onion-secret-key = lib.mkIf cfg.tor.enable {
|
||||||
|
file = "${self}/secrets/tor-onion-secret-key.age";
|
||||||
|
owner = "tor";
|
||||||
|
group = "tor";
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
age.secrets.tor-onion-public-key = lib.mkIf cfg.tor.enable {
|
||||||
|
file = "${self}/secrets/tor-onion-public-key.age";
|
||||||
|
owner = "tor";
|
||||||
|
group = "tor";
|
||||||
|
mode = "0444";
|
||||||
|
};
|
||||||
|
age.secrets.tor-onion-hostname = lib.mkIf cfg.tor.enable {
|
||||||
|
file = "${self}/secrets/tor-onion-hostname.age";
|
||||||
|
owner = "tor";
|
||||||
|
group = "tor";
|
||||||
|
mode = "0444";
|
||||||
|
};
|
||||||
|
|
||||||
|
services.tor = lib.mkIf cfg.tor.enable {
|
||||||
|
enable = true;
|
||||||
|
relay.onionServices.jetpham-website = {
|
||||||
|
map = [{ port = 80; target = { addr = "127.0.0.1"; port = 8888; }; }];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.tor-onion-keys = lib.mkIf cfg.tor.enable {
|
||||||
|
description = "Copy Tor onion keys into place";
|
||||||
|
after = [ "agenix.service" ];
|
||||||
|
before = [ "tor.service" ];
|
||||||
|
wantedBy = [ "tor.service" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
script = ''
|
||||||
|
dir="/var/lib/tor/onion/jetpham-website"
|
||||||
|
mkdir -p "$dir"
|
||||||
|
cp ${config.age.secrets.tor-onion-secret-key.path} "$dir/hs_ed25519_secret_key"
|
||||||
|
cp ${config.age.secrets.tor-onion-public-key.path} "$dir/hs_ed25519_public_key"
|
||||||
|
cp ${config.age.secrets.tor-onion-hostname.path} "$dir/hostname"
|
||||||
|
chown -R tor:tor "$dir"
|
||||||
|
chmod 700 "$dir"
|
||||||
|
chmod 400 "$dir/hs_ed25519_secret_key"
|
||||||
|
chmod 444 "$dir/hs_ed25519_public_key" "$dir/hostname"
|
||||||
|
'';
|
||||||
|
};
|
||||||
# Q&A API systemd service
|
# Q&A API systemd service
|
||||||
systemd.services.jetpham-qa-api = {
|
systemd.services.jetpham-qa-api = {
|
||||||
description = "Jet Pham Q&A API";
|
description = "Jet Pham Q&A API";
|
||||||
|
|
@ -47,14 +98,11 @@ in
|
||||||
Environment = [ "QA_DB_PATH=/var/lib/jetpham-qa/qa.db" ];
|
Environment = [ "QA_DB_PATH=/var/lib/jetpham-qa/qa.db" ];
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
RestartSec = 5;
|
RestartSec = 5;
|
||||||
} // lib.optionalAttrs (cfg.webhookSecretFile == null) {
|
LoadCredential = "webhook-secret:${config.age.secrets.webhook-secret.path}";
|
||||||
ExecStart = "${qaApi}/bin/jetpham-qa-api";
|
|
||||||
} // lib.optionalAttrs (cfg.envFile != null) {
|
} // lib.optionalAttrs (cfg.envFile != null) {
|
||||||
EnvironmentFile = cfg.envFile;
|
EnvironmentFile = cfg.envFile;
|
||||||
} // lib.optionalAttrs (cfg.webhookSecretFile != null) {
|
|
||||||
LoadCredential = "webhook-secret:${cfg.webhookSecretFile}";
|
|
||||||
};
|
};
|
||||||
script = lib.mkIf (cfg.webhookSecretFile != null) ''
|
script = ''
|
||||||
export WEBHOOK_SECRET="$(cat $CREDENTIALS_DIRECTORY/webhook-secret)"
|
export WEBHOOK_SECRET="$(cat $CREDENTIALS_DIRECTORY/webhook-secret)"
|
||||||
exec ${qaApi}/bin/jetpham-qa-api
|
exec ${qaApi}/bin/jetpham-qa-api
|
||||||
'';
|
'';
|
||||||
|
|
@ -76,5 +124,22 @@ in
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services.caddy.virtualHosts."http://127.0.0.1:8888" = lib.mkIf cfg.tor.enable {
|
||||||
|
extraConfig = ''
|
||||||
|
header Cross-Origin-Opener-Policy "same-origin"
|
||||||
|
header Cross-Origin-Embedder-Policy "require-corp"
|
||||||
|
|
||||||
|
handle /api/* {
|
||||||
|
reverse_proxy 127.0.0.1:3001
|
||||||
|
}
|
||||||
|
|
||||||
|
handle {
|
||||||
|
root * ${package}
|
||||||
|
try_files {path} /index.html
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
secrets.nix
Normal file
10
secrets.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
let
|
||||||
|
laptop = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE40ISu3ydCqfdpb26JYD5cIN0Fu0id/FDS+xjB5zpqu";
|
||||||
|
server = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAING219cDKTDLaZefmqvOHfXvYloA/ErsCGE0pM022vlB";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
"secrets/tor-onion-secret-key.age".publicKeys = [ laptop server ];
|
||||||
|
"secrets/tor-onion-public-key.age".publicKeys = [ laptop server ];
|
||||||
|
"secrets/tor-onion-hostname.age".publicKeys = [ laptop server ];
|
||||||
|
"secrets/webhook-secret.age".publicKeys = [ laptop server ];
|
||||||
|
}
|
||||||
BIN
secrets/tor-onion-hostname.age
Normal file
BIN
secrets/tor-onion-hostname.age
Normal file
Binary file not shown.
7
secrets/tor-onion-public-key.age
Normal file
7
secrets/tor-onion-public-key.age
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 Ziw7aw 2u0CVE/rQWpJNSRW/1xeWBKpShWT4ckke+Ih4j3WbRk
|
||||||
|
xeE2xTlSPEDPeC4BNkaSoOckwuOhyCQWqtXkwuhBiRo
|
||||||
|
-> ssh-ed25519 uKftJg Tt1mbTWHyXRDjvGWFBqmyrMl/PtUs45N1032luY88x8
|
||||||
|
A51wD3tiZ0lV1TSub+Pz7hZ+kndiEpnmBliP59qYzkY
|
||||||
|
--- 7X+mgLxb3uYfiYebJnAUwl/4jhGJJSweaolMttmoEIQ
|
||||||
|
3à‡’Í.Úu˼’ÞZ"Àä9Ö<39>'AëâªK~.@ŽÌ‰7¾lW„÷ pý{CôÁAŠ&@¸|ÕËšV%55˜Ot¼Šr9Çîºúñ<C3BA>h»L²‡@®G’õLä˜gû€
|
||||||
8
secrets/tor-onion-secret-key.age
Normal file
8
secrets/tor-onion-secret-key.age
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 Ziw7aw AZTbqWYmTaudHZ8PiTZlwpf7VzwaP921guVV1iQi8WM
|
||||||
|
7CGUyEjZoAPCBX2pqNHLd2P1KLnj/Y5nnBVToWYCjWg
|
||||||
|
-> ssh-ed25519 uKftJg RfAkte/jcx+/SvZiUNH07cnBJcvl0Sjt7zSxdSCsXE0
|
||||||
|
TdMsQ2u5WXw3KAi7Tk4JOdbiFStT8F88xjDlRN8LH2Q
|
||||||
|
--- pgQnGRRVjVK02tbMgDoh3SatJFxxFLazqy5ieHu96tk
|
||||||
|
žÞxó}ûë¥T¡½<11>SÒP^©kH
׋/æôœõþ›Ý³U~ókÅ;Ö•äÕ†=•ÁºNå«\PZï|óO¨/YíV6Ìx‹=„¹¹Â<gR+2á³C5n
|
||||||
|
©ãÃàÚ/J…‹k¤;5ÚNÈ—Ü#q’&Øxš‹|6²ƒŸ>H
|
||||||
7
secrets/webhook-secret.age
Normal file
7
secrets/webhook-secret.age
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 Ziw7aw zDI2q+wM70YbQFw7hEQbU58XrgkWnM8fura+ovG0bTw
|
||||||
|
0Aznts9gOT8ADxFY0SIv/VT0ExEEq9wZLTnlC0j15JU
|
||||||
|
-> ssh-ed25519 uKftJg dFQxnrMwQHJ2lVBMi6P4XugljREjAHPV9bEFmLhuiBc
|
||||||
|
VWKLaM3peiELoJigapdZv0N4Z4lRZZ8eGMEFy+WtdxA
|
||||||
|
--- zJghsuTUoVH8bKynqq29kzEuNDuEjvFf5v/s98jQdaA
|
||||||
|
𡂝濮+<2B>i籤V2*Hf苳儑齪<E58491><E9BDAA> c栛
|
||||||
|
|
@ -2,14 +2,10 @@ import "~/styles/globals.css";
|
||||||
import init, { start } from "cgol";
|
import init, { start } from "cgol";
|
||||||
import { route, initRouter } from "~/router";
|
import { route, initRouter } from "~/router";
|
||||||
import { homePage } from "~/pages/home";
|
import { homePage } from "~/pages/home";
|
||||||
import { projectsPage } from "~/pages/projects";
|
|
||||||
import { projectPage } from "~/pages/project";
|
|
||||||
import { qaPage } from "~/pages/qa";
|
import { qaPage } from "~/pages/qa";
|
||||||
import { notFoundPage } from "~/pages/not-found";
|
import { notFoundPage } from "~/pages/not-found";
|
||||||
|
|
||||||
route("/", homePage);
|
route("/", homePage);
|
||||||
route("/projects", projectsPage);
|
|
||||||
route("/projects/:slug", projectPage);
|
|
||||||
route("/qa", qaPage);
|
route("/qa", qaPage);
|
||||||
route("*", notFoundPage);
|
route("*", notFoundPage);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,13 @@ export async function qaPage(outlet: HTMLElement) {
|
||||||
outlet.innerHTML = `
|
outlet.innerHTML = `
|
||||||
<div class="flex flex-col items-center justify-start px-4">
|
<div class="flex flex-col items-center justify-start px-4">
|
||||||
${frostedBox(`
|
${frostedBox(`
|
||||||
<h1 style="color: var(--yellow);">Q&A</h1>
|
<form id="qa-form">
|
||||||
<form id="qa-form" class="mt-[2ch]">
|
<fieldset class="border-2 border-white px-[calc(1.5ch-0.5px)] pb-[1ch] pt-[1ch]">
|
||||||
<fieldset class="border-2 border-white px-[calc(1.5ch-0.5px)] pb-[1ch] pt-0">
|
|
||||||
<legend class="-mx-[0.5ch] px-[0.5ch] text-white">Ask a Question</legend>
|
<legend class="-mx-[0.5ch] px-[0.5ch] text-white">Ask a Question</legend>
|
||||||
<textarea id="qa-input" maxlength="200" rows="3"
|
<textarea id="qa-input" maxlength="200" rows="3"
|
||||||
class="qa-textarea"
|
class="qa-textarea"
|
||||||
placeholder="Type your question..."></textarea>
|
placeholder="Type your question..."></textarea>
|
||||||
<div class="flex justify-between mt-[0.5ch]">
|
<div class="flex justify-between mt-[1ch]">
|
||||||
<span id="char-count" style="color: var(--dark-gray);">0/200</span>
|
<span id="char-count" style="color: var(--dark-gray);">0/200</span>
|
||||||
<button type="submit" class="qa-button">[SUBMIT]</button>
|
<button type="submit" class="qa-button">[SUBMIT]</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -83,5 +82,6 @@ export async function qaPage(outlet: HTMLElement) {
|
||||||
} catch {
|
} catch {
|
||||||
list.textContent = "Failed to load questions.";
|
list.textContent = "Failed to load questions.";
|
||||||
list.style.color = "var(--light-red)";
|
list.style.color = "var(--light-red)";
|
||||||
|
list.style.textAlign = "center";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,14 +80,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: var(--blue);
|
background-color: var(--light-blue);
|
||||||
|
color: var(--black);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Form inputs */
|
/* Form inputs */
|
||||||
.qa-textarea {
|
.qa-textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: var(--black);
|
background-color: var(--black);
|
||||||
border: 1px solid var(--white);
|
border: 2px solid var(--white);
|
||||||
color: var(--light-gray);
|
color: var(--light-gray);
|
||||||
padding: 1ch;
|
padding: 1ch;
|
||||||
resize: none;
|
resize: none;
|
||||||
|
|
@ -97,8 +98,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.qa-button {
|
.qa-button {
|
||||||
border: 1px solid var(--white);
|
border: none;
|
||||||
padding: 0 1ch;
|
padding: 0.25ch 1ch;
|
||||||
color: var(--yellow);
|
color: var(--yellow);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
|
|
@ -139,7 +140,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-content pre {
|
.project-content pre {
|
||||||
border: 1px solid var(--dark-gray);
|
border: 2px solid var(--dark-gray);
|
||||||
padding: 1ch;
|
padding: 1ch;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
margin-bottom: 1ch;
|
margin-bottom: 1ch;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue