too lazy to think of a message, so enjoy this pointless text. Good luck future me...
All checks were successful
Test action / Print hello world (push) Successful in 8m32s

This commit is contained in:
Chris Kruining 2025-10-08 10:49:59 +00:00
parent e7b0307690
commit 28fdba8b00
15 changed files with 3868 additions and 2785 deletions

3322
bun.lock

File diff suppressed because it is too large Load diff

1466
bun.nix

File diff suppressed because it is too large Load diff

View file

@ -1,39 +1,39 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
flake-parts = {
url = "github:hercules-ci/flake-parts";
inputs.nixpkgs-lib.follows = "nixpkgs";
};
bun2nix = {
url = "github:baileyluTCD/bun2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = inputs@{ flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
"x86_64-darwin"
];
imports = [
./nix/devShells/flake-module.nix
./nix/packages/flake-module.nix
./nix/modules/flake-module.nix
];
perSystem = { lib, self', ... }: {
checks =
let
packages = lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages;
devShells = lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells;
in
packages // devShells;
};
};
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
flake-parts = {
url = "github:hercules-ci/flake-parts";
inputs.nixpkgs-lib.follows = "nixpkgs";
};
bun2nix = {
url = "github:baileyluTCD/bun2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = inputs@{ flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
"x86_64-darwin"
];
imports = [
./nix/devShells/flake-module.nix
./nix/packages/flake-module.nix
./nix/modules/flake-module.nix
];
perSystem = { lib, self', ... }: {
checks =
let
packages = lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages;
devShells = lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells;
in
packages // devShells;
};
};
}

View file

@ -1,6 +1,6 @@
push:
git add .
git commit -m 'too lazy to think of a message, so enjoy this pointless text. Good luck future me...'
push:
git add .
git commit -m 'too lazy to think of a message, so enjoy this pointless text. Good luck future me...'
git push

View file

@ -1,11 +1,11 @@
{ inputs, ... }:
{
perSystem = { pkgs, system, ... }: {
devShells.default = pkgs.mkShellNoCC {
nativeBuildInputs = with pkgs; [
bun
inputs.bun2nix.packages.${system}.default
];
};
};
{ inputs, ... }:
{
perSystem = { pkgs, system, ... }: {
devShells.default = pkgs.mkShellNoCC {
nativeBuildInputs = with pkgs; [
bun
inputs.bun2nix.packages.${system}.default
];
};
};
}

View file

@ -1,142 +1,144 @@
{ self, ... }:
{
flake.nixosModules.default =
nixos@{ config, pkgs, lib, utils, ... }:
let
inherit (lib) mkEnableOption mkPackageOption mkOption mkIf types;
format = pkgs.formats.json {};
cfg = config.services.amarth-customer-portal;
in
{
options.services.amarth-customer-portal = {
enable = mkEnableOption "Enable Amarth cloud's customer portal.";
package = mkPackageOption self.packages.${pkgs.hostPlatform.system} "amarth-customer-portal" {};
openFirewall = mkOption {
type = types.bool;
default = false;
example = "true";
description = ''
Open the configured port in the firewall.
'';
};
user = lib.mkOption {
type = types.str;
default = "amarth";
description = ''
User account under which FileBrowser runs.
'';
};
group = lib.mkOption {
type = types.str;
default = "amarth";
description = ''
Group under which FileBrowser runs.
'';
};
settings = mkOption {
default = {};
description = ''
'';
type = types.submodule {
freeformType = format.type;
options = {
address = mkOption {
default = "localhost";
description = ''
The address to listen on.
'';
type = types.str;
};
port = mkOption {
type = types.port;
default = 8080;
description = ''
Which port to run the portal on.
'';
};
dataDir = lib.mkOption {
default = "/var/lib/amarth/customer-portal";
description = ''
Directory where the portal persists files.
'';
type = types.path;
};
};
};
};
};
config = mkIf cfg.enable {
systemd = {
services.amarthCustomerPortal = {
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
description = "Amarth cloud's customer portal";
serviceConfig = {
ExecStart = utils.escapeSystemdExecArgs [
(lib.getExe cfg.package)
"--config"
(format.generate "config.json" cfg.settings)
];
StateDirectory = "amarth-customer-portal";
CacheDirectory = "amarth-customer-portal";
WorkingDirectory = cfg.settings.dataDir;
User = cfg.user;
Group = cfg.group;
UMask = "0077";
NoNewPrivileges = true;
PrivateDevices = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
MemoryDenyWriteExecute = true;
LockPersonality = true;
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
DevicePolicy = "closed";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
};
};
tmpfiles.settings.amarth-customer-portal = {
"${cfg.settings.dataDir}".d = {
inherit (cfg) user group;
mode = "0700";
};
};
};
users = {
users = mkIf (cfg.user == "amarth") {
amarth = { inherit (cfg) group; isSystemUser = true; };
};
groups = mkIf (cfg.group == "amarth") {
amarth = {};
};
};
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.port ];
};
};
{ self, ... }:
{
flake.nixosModules.default =
nixos@{ config, pkgs, lib, utils, ... }:
let
inherit (lib) mkEnableOption mkPackageOption mkOption mkIf types;
format = pkgs.formats.json {};
cfg = config.services.amarth-customer-portal;
in
{
options.services.amarth-customer-portal = {
enable = mkEnableOption "Enable Amarth cloud's customer portal.";
package = mkPackageOption self.packages.${pkgs.hostPlatform.system} "amarth-customer-portal" {};
openFirewall = mkOption {
type = types.bool;
default = false;
example = "true";
description = ''
Open the configured port in the firewall.
'';
};
user = lib.mkOption {
type = types.str;
default = "amarth";
description = ''
User account under which FileBrowser runs.
'';
};
group = lib.mkOption {
type = types.str;
default = "amarth";
description = ''
Group under which FileBrowser runs.
'';
};
settings = mkOption {
default = {};
description = ''
'';
type = types.submodule {
freeformType = format.type;
options = {
address = mkOption {
default = "localhost";
description = ''
The address to listen on.
'';
type = types.str;
};
port = mkOption {
type = types.port;
default = 8080;
description = ''
Which port to run the portal on.
'';
};
dataDir = lib.mkOption {
default = "/var/lib/amarth/customer-portal";
description = ''
Directory where the portal persists files.
'';
type = types.path;
};
};
};
};
};
config = mkIf cfg.enable {
systemd = {
services.amarthCustomerPortal = {
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
description = "Amarth cloud's customer portal";
serviceConfig = {
ExecStart = utils.escapeSystemdExecArgs [
(lib.getExe cfg.bun)
"run"
"start"
"--config"
(format.generate "config.json" cfg.settings)
];
StateDirectory = "amarth-customer-portal";
CacheDirectory = "amarth-customer-portal";
WorkingDirectory = cfg.settings.dataDir;
User = cfg.user;
Group = cfg.group;
UMask = "0077";
NoNewPrivileges = true;
PrivateDevices = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
MemoryDenyWriteExecute = true;
LockPersonality = true;
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
DevicePolicy = "closed";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
};
};
tmpfiles.settings.amarth-customer-portal = {
"${cfg.settings.dataDir}".d = {
inherit (cfg) user group;
mode = "0700";
};
};
};
users = {
users = mkIf (cfg.user == "amarth") {
amarth = { inherit (cfg) group; isSystemUser = true; };
};
groups = mkIf (cfg.group == "amarth") {
amarth = {};
};
};
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.port ];
};
};
}

View file

@ -1,21 +1,21 @@
{ ... }:
{
imports =
let
# Get all subdirectories in the current directory
dirContents = builtins.readDir ./.;
# Filter to include only directories that have a flake-module.nix file
# and exclude special directories like 'result'
validModuleDirs = builtins.filter (
name:
name != "result"
&& dirContents.${name} == "directory"
&& builtins.pathExists (./. + "/${name}/flake-module.nix")
) (builtins.attrNames dirContents);
# Create import paths for each valid directory
imports = map (name: ./. + "/${name}/flake-module.nix") validModuleDirs;
in
imports;
}
{ ... }:
{
imports =
let
# Get all subdirectories in the current directory
dirContents = builtins.readDir ./.;
# Filter to include only directories that have a flake-module.nix file
# and exclude special directories like 'result'
validModuleDirs = builtins.filter (
name:
name != "result"
&& dirContents.${name} == "directory"
&& builtins.pathExists (./. + "/${name}/flake-module.nix")
) (builtins.attrNames dirContents);
# Create import paths for each valid directory
imports = map (name: ./. + "/${name}/flake-module.nix") validModuleDirs;
in
imports;
}

View file

@ -16,6 +16,9 @@
buildPhase = ''
runHook preBuild
export BETTER_AUTH_SECRET='8&!3$!^U!&56qvSydEJ^E$cr^GSBWWFmbHJCLJ@w7vRWm7!R5b$DSoCmY$GW7HEF'
export SESSION_SECRET='jJBqeVMvQe52HqLYWDunLEKbkkC9JqCrgP92nV5j2dC99eZWCtK9H2NrASH8AbxF'
bun run build --bun

View file

@ -1,471 +1,471 @@
@layer reset, base, tokens, recipes, utilities;
@import "open-props/style" layer(tokens);
@import "open-props/normalize" layer(reset);
@import "open-props/durations" layer(base);
@import "open-props/theme.light.switch.min.css" layer(tokens);
@import "open-props/theme.dark.switch.min.css" layer(tokens);
@layer base {
html {
display: grid;
grid: 100% / 100%;
inline-size: 100%;
block-size: 100%;
overflow: clip;
/* font-size: clamp(1rem, -0.875rem + 8.333vw, 3.5rem); */
& > body {
display: grid;
grid: 100% / 100%;
inline-size: 100%;
block-size: 100%;
contain: layout style paint;
margin: 0;
font-family: sans-serif;
overflow: clip;
background-color: var(--surface-3);
color: var(--text-2);
accent-color: var(--primary-500);
* {
box-sizing: border-box;
&:focus-visible {
outline: 1px solid var(--info);
}
}
}
}
}
@layer reset {
@property --sibling-index {
syntax: "<integer>";
inherits: false;
initial-value: 1;
}
@property --sibling-count {
syntax: "<integer>";
inherits: false;
initial-value: 0;
}
:nth-child(1) {
--sibling-index: 1;
}
:nth-child(2) {
--sibling-index: 2;
}
:nth-child(3) {
--sibling-index: 3;
}
:nth-child(4) {
--sibling-index: 4;
}
:nth-child(5) {
--sibling-index: 5;
}
:nth-child(6) {
--sibling-index: 6;
}
:nth-child(7) {
--sibling-index: 7;
}
:nth-child(8) {
--sibling-index: 8;
}
:nth-child(9) {
--sibling-index: 9;
}
:nth-child(10) {
--sibling-index: 10;
}
:nth-child(11) {
--sibling-index: 11;
}
:nth-child(12) {
--sibling-index: 12;
}
:nth-child(13) {
--sibling-index: 13;
}
:nth-child(14) {
--sibling-index: 14;
}
:nth-child(15) {
--sibling-index: 15;
}
:nth-child(16) {
--sibling-index: 16;
}
:nth-child(17) {
--sibling-index: 17;
}
:nth-child(18) {
--sibling-index: 18;
}
:nth-child(19) {
--sibling-index: 19;
}
:nth-child(20) {
--sibling-index: 20;
}
:nth-child(21) {
--sibling-index: 21;
}
:nth-child(22) {
--sibling-index: 22;
}
:nth-child(23) {
--sibling-index: 23;
}
:nth-child(24) {
--sibling-index: 24;
}
:nth-child(25) {
--sibling-index: 25;
}
:nth-child(26) {
--sibling-index: 26;
}
:nth-child(27) {
--sibling-index: 27;
}
:nth-child(28) {
--sibling-index: 28;
}
:nth-child(29) {
--sibling-index: 29;
}
:nth-child(30) {
--sibling-index: 30;
}
:nth-child(31) {
--sibling-index: 31;
}
:nth-child(32) {
--sibling-index: 32;
}
:nth-child(33) {
--sibling-index: 33;
}
:nth-child(34) {
--sibling-index: 34;
}
:nth-child(35) {
--sibling-index: 35;
}
:nth-child(36) {
--sibling-index: 36;
}
:nth-child(37) {
--sibling-index: 37;
}
:nth-child(38) {
--sibling-index: 38;
}
:nth-child(39) {
--sibling-index: 39;
}
:nth-child(40) {
--sibling-index: 40;
}
:nth-child(41) {
--sibling-index: 41;
}
:nth-child(42) {
--sibling-index: 42;
}
:nth-child(43) {
--sibling-index: 43;
}
:nth-child(44) {
--sibling-index: 44;
}
:nth-child(45) {
--sibling-index: 45;
}
:nth-child(46) {
--sibling-index: 46;
}
:nth-child(47) {
--sibling-index: 47;
}
:nth-child(48) {
--sibling-index: 48;
}
:nth-child(49) {
--sibling-index: 49;
}
:nth-child(50) {
--sibling-index: 50;
}
:nth-child(51) {
--sibling-index: 51;
}
:nth-child(52) {
--sibling-index: 52;
}
:nth-child(53) {
--sibling-index: 53;
}
:nth-child(54) {
--sibling-index: 54;
}
:nth-child(55) {
--sibling-index: 55;
}
:nth-child(56) {
--sibling-index: 56;
}
:nth-child(57) {
--sibling-index: 57;
}
:nth-child(58) {
--sibling-index: 58;
}
:nth-child(59) {
--sibling-index: 59;
}
:has(> :last-child:nth-child(1)) > * {
--sibling-count: 1;
}
:has(> :last-child:nth-child(2)) > * {
--sibling-count: 2;
}
:has(> :last-child:nth-child(3)) > * {
--sibling-count: 3;
}
:has(> :last-child:nth-child(4)) > * {
--sibling-count: 4;
}
:has(> :last-child:nth-child(5)) > * {
--sibling-count: 5;
}
:has(> :last-child:nth-child(6)) > * {
--sibling-count: 6;
}
:has(> :last-child:nth-child(7)) > * {
--sibling-count: 7;
}
:has(> :last-child:nth-child(8)) > * {
--sibling-count: 8;
}
:has(> :last-child:nth-child(9)) > * {
--sibling-count: 9;
}
:has(> :last-child:nth-child(10)) > * {
--sibling-count: 10;
}
:has(> :last-child:nth-child(11)) > * {
--sibling-count: 11;
}
:has(> :last-child:nth-child(12)) > * {
--sibling-count: 12;
}
:has(> :last-child:nth-child(13)) > * {
--sibling-count: 13;
}
:has(> :last-child:nth-child(14)) > * {
--sibling-count: 14;
}
:has(> :last-child:nth-child(15)) > * {
--sibling-count: 15;
}
:has(> :last-child:nth-child(16)) > * {
--sibling-count: 16;
}
:has(> :last-child:nth-child(17)) > * {
--sibling-count: 17;
}
:has(> :last-child:nth-child(18)) > * {
--sibling-count: 18;
}
:has(> :last-child:nth-child(19)) > * {
--sibling-count: 19;
}
:has(> :last-child:nth-child(20)) > * {
--sibling-count: 20;
}
:has(> :last-child:nth-child(21)) > * {
--sibling-count: 21;
}
:has(> :last-child:nth-child(22)) > * {
--sibling-count: 22;
}
:has(> :last-child:nth-child(23)) > * {
--sibling-count: 23;
}
:has(> :last-child:nth-child(24)) > * {
--sibling-count: 24;
}
:has(> :last-child:nth-child(25)) > * {
--sibling-count: 25;
}
:has(> :last-child:nth-child(26)) > * {
--sibling-count: 26;
}
:has(> :last-child:nth-child(27)) > * {
--sibling-count: 27;
}
:has(> :last-child:nth-child(28)) > * {
--sibling-count: 28;
}
:has(> :last-child:nth-child(29)) > * {
--sibling-count: 29;
}
:has(> :last-child:nth-child(30)) > * {
--sibling-count: 30;
}
:has(> :last-child:nth-child(31)) > * {
--sibling-count: 31;
}
:has(> :last-child:nth-child(32)) > * {
--sibling-count: 32;
}
:has(> :last-child:nth-child(33)) > * {
--sibling-count: 33;
}
:has(> :last-child:nth-child(34)) > * {
--sibling-count: 34;
}
:has(> :last-child:nth-child(35)) > * {
--sibling-count: 35;
}
:has(> :last-child:nth-child(36)) > * {
--sibling-count: 36;
}
:has(> :last-child:nth-child(37)) > * {
--sibling-count: 37;
}
:has(> :last-child:nth-child(38)) > * {
--sibling-count: 38;
}
:has(> :last-child:nth-child(39)) > * {
--sibling-count: 39;
}
:has(> :last-child:nth-child(40)) > * {
--sibling-count: 40;
}
:has(> :last-child:nth-child(41)) > * {
--sibling-count: 41;
}
:has(> :last-child:nth-child(42)) > * {
--sibling-count: 42;
}
:has(> :last-child:nth-child(43)) > * {
--sibling-count: 43;
}
:has(> :last-child:nth-child(44)) > * {
--sibling-count: 44;
}
:has(> :last-child:nth-child(45)) > * {
--sibling-count: 45;
}
:has(> :last-child:nth-child(46)) > * {
--sibling-count: 46;
}
:has(> :last-child:nth-child(47)) > * {
--sibling-count: 47;
}
:has(> :last-child:nth-child(48)) > * {
--sibling-count: 48;
}
:has(> :last-child:nth-child(49)) > * {
--sibling-count: 49;
}
:has(> :last-child:nth-child(50)) > * {
--sibling-count: 50;
}
:has(> :last-child:nth-child(51)) > * {
--sibling-count: 51;
}
:has(> :last-child:nth-child(52)) > * {
--sibling-count: 52;
}
:has(> :last-child:nth-child(53)) > * {
--sibling-count: 53;
}
:has(> :last-child:nth-child(54)) > * {
--sibling-count: 54;
}
:has(> :last-child:nth-child(55)) > * {
--sibling-count: 55;
}
:has(> :last-child:nth-child(56)) > * {
--sibling-count: 56;
}
:has(> :last-child:nth-child(57)) > * {
--sibling-count: 57;
}
:has(> :last-child:nth-child(58)) > * {
--sibling-count: 58;
}
:has(> :last-child:nth-child(59)) > * {
--sibling-count: 59;
}
:has(> :last-child:nth-child(60)) > * {
--sibling-count: 60;
}
:has(> :last-child:nth-child(61)) > * {
--sibling-count: 61;
}
:has(> :last-child:nth-child(62)) > * {
--sibling-count: 62;
}
:has(> :last-child:nth-child(63)) > * {
--sibling-count: 63;
}
:has(> :last-child:nth-child(64)) > * {
--sibling-count: 64;
}
:has(> :last-child:nth-child(65)) > * {
--sibling-count: 65;
}
:has(> :last-child:nth-child(66)) > * {
--sibling-count: 66;
}
:has(> :last-child:nth-child(67)) > * {
--sibling-count: 67;
}
:has(> :last-child:nth-child(68)) > * {
--sibling-count: 68;
}
:has(> :last-child:nth-child(69)) > * {
--sibling-count: 69;
}
}
@layer reset, base, tokens, recipes, utilities;
@import "open-props/style" layer(tokens);
@import "open-props/normalize" layer(reset);
@import "open-props/durations" layer(base);
@import "open-props/theme.light.switch.min.css" layer(tokens);
@import "open-props/theme.dark.switch.min.css" layer(tokens);
@layer base {
html {
display: grid;
grid: 100% / 100%;
inline-size: 100%;
block-size: 100%;
overflow: clip;
/* font-size: clamp(1rem, -0.875rem + 8.333vw, 3.5rem); */
& > body {
display: grid;
grid: 100% / 100%;
inline-size: 100%;
block-size: 100%;
contain: layout style paint;
margin: 0;
font-family: sans-serif;
overflow: clip;
background-color: var(--surface-3);
color: var(--text-2);
accent-color: var(--primary-500);
* {
box-sizing: border-box;
&:focus-visible {
outline: 1px solid var(--info);
}
}
}
}
}
@layer reset {
@property --sibling-index {
syntax: "<integer>";
inherits: false;
initial-value: 1;
}
@property --sibling-count {
syntax: "<integer>";
inherits: false;
initial-value: 0;
}
:nth-child(1) {
--sibling-index: 1;
}
:nth-child(2) {
--sibling-index: 2;
}
:nth-child(3) {
--sibling-index: 3;
}
:nth-child(4) {
--sibling-index: 4;
}
:nth-child(5) {
--sibling-index: 5;
}
:nth-child(6) {
--sibling-index: 6;
}
:nth-child(7) {
--sibling-index: 7;
}
:nth-child(8) {
--sibling-index: 8;
}
:nth-child(9) {
--sibling-index: 9;
}
:nth-child(10) {
--sibling-index: 10;
}
:nth-child(11) {
--sibling-index: 11;
}
:nth-child(12) {
--sibling-index: 12;
}
:nth-child(13) {
--sibling-index: 13;
}
:nth-child(14) {
--sibling-index: 14;
}
:nth-child(15) {
--sibling-index: 15;
}
:nth-child(16) {
--sibling-index: 16;
}
:nth-child(17) {
--sibling-index: 17;
}
:nth-child(18) {
--sibling-index: 18;
}
:nth-child(19) {
--sibling-index: 19;
}
:nth-child(20) {
--sibling-index: 20;
}
:nth-child(21) {
--sibling-index: 21;
}
:nth-child(22) {
--sibling-index: 22;
}
:nth-child(23) {
--sibling-index: 23;
}
:nth-child(24) {
--sibling-index: 24;
}
:nth-child(25) {
--sibling-index: 25;
}
:nth-child(26) {
--sibling-index: 26;
}
:nth-child(27) {
--sibling-index: 27;
}
:nth-child(28) {
--sibling-index: 28;
}
:nth-child(29) {
--sibling-index: 29;
}
:nth-child(30) {
--sibling-index: 30;
}
:nth-child(31) {
--sibling-index: 31;
}
:nth-child(32) {
--sibling-index: 32;
}
:nth-child(33) {
--sibling-index: 33;
}
:nth-child(34) {
--sibling-index: 34;
}
:nth-child(35) {
--sibling-index: 35;
}
:nth-child(36) {
--sibling-index: 36;
}
:nth-child(37) {
--sibling-index: 37;
}
:nth-child(38) {
--sibling-index: 38;
}
:nth-child(39) {
--sibling-index: 39;
}
:nth-child(40) {
--sibling-index: 40;
}
:nth-child(41) {
--sibling-index: 41;
}
:nth-child(42) {
--sibling-index: 42;
}
:nth-child(43) {
--sibling-index: 43;
}
:nth-child(44) {
--sibling-index: 44;
}
:nth-child(45) {
--sibling-index: 45;
}
:nth-child(46) {
--sibling-index: 46;
}
:nth-child(47) {
--sibling-index: 47;
}
:nth-child(48) {
--sibling-index: 48;
}
:nth-child(49) {
--sibling-index: 49;
}
:nth-child(50) {
--sibling-index: 50;
}
:nth-child(51) {
--sibling-index: 51;
}
:nth-child(52) {
--sibling-index: 52;
}
:nth-child(53) {
--sibling-index: 53;
}
:nth-child(54) {
--sibling-index: 54;
}
:nth-child(55) {
--sibling-index: 55;
}
:nth-child(56) {
--sibling-index: 56;
}
:nth-child(57) {
--sibling-index: 57;
}
:nth-child(58) {
--sibling-index: 58;
}
:nth-child(59) {
--sibling-index: 59;
}
:has(> :last-child:nth-child(1)) > * {
--sibling-count: 1;
}
:has(> :last-child:nth-child(2)) > * {
--sibling-count: 2;
}
:has(> :last-child:nth-child(3)) > * {
--sibling-count: 3;
}
:has(> :last-child:nth-child(4)) > * {
--sibling-count: 4;
}
:has(> :last-child:nth-child(5)) > * {
--sibling-count: 5;
}
:has(> :last-child:nth-child(6)) > * {
--sibling-count: 6;
}
:has(> :last-child:nth-child(7)) > * {
--sibling-count: 7;
}
:has(> :last-child:nth-child(8)) > * {
--sibling-count: 8;
}
:has(> :last-child:nth-child(9)) > * {
--sibling-count: 9;
}
:has(> :last-child:nth-child(10)) > * {
--sibling-count: 10;
}
:has(> :last-child:nth-child(11)) > * {
--sibling-count: 11;
}
:has(> :last-child:nth-child(12)) > * {
--sibling-count: 12;
}
:has(> :last-child:nth-child(13)) > * {
--sibling-count: 13;
}
:has(> :last-child:nth-child(14)) > * {
--sibling-count: 14;
}
:has(> :last-child:nth-child(15)) > * {
--sibling-count: 15;
}
:has(> :last-child:nth-child(16)) > * {
--sibling-count: 16;
}
:has(> :last-child:nth-child(17)) > * {
--sibling-count: 17;
}
:has(> :last-child:nth-child(18)) > * {
--sibling-count: 18;
}
:has(> :last-child:nth-child(19)) > * {
--sibling-count: 19;
}
:has(> :last-child:nth-child(20)) > * {
--sibling-count: 20;
}
:has(> :last-child:nth-child(21)) > * {
--sibling-count: 21;
}
:has(> :last-child:nth-child(22)) > * {
--sibling-count: 22;
}
:has(> :last-child:nth-child(23)) > * {
--sibling-count: 23;
}
:has(> :last-child:nth-child(24)) > * {
--sibling-count: 24;
}
:has(> :last-child:nth-child(25)) > * {
--sibling-count: 25;
}
:has(> :last-child:nth-child(26)) > * {
--sibling-count: 26;
}
:has(> :last-child:nth-child(27)) > * {
--sibling-count: 27;
}
:has(> :last-child:nth-child(28)) > * {
--sibling-count: 28;
}
:has(> :last-child:nth-child(29)) > * {
--sibling-count: 29;
}
:has(> :last-child:nth-child(30)) > * {
--sibling-count: 30;
}
:has(> :last-child:nth-child(31)) > * {
--sibling-count: 31;
}
:has(> :last-child:nth-child(32)) > * {
--sibling-count: 32;
}
:has(> :last-child:nth-child(33)) > * {
--sibling-count: 33;
}
:has(> :last-child:nth-child(34)) > * {
--sibling-count: 34;
}
:has(> :last-child:nth-child(35)) > * {
--sibling-count: 35;
}
:has(> :last-child:nth-child(36)) > * {
--sibling-count: 36;
}
:has(> :last-child:nth-child(37)) > * {
--sibling-count: 37;
}
:has(> :last-child:nth-child(38)) > * {
--sibling-count: 38;
}
:has(> :last-child:nth-child(39)) > * {
--sibling-count: 39;
}
:has(> :last-child:nth-child(40)) > * {
--sibling-count: 40;
}
:has(> :last-child:nth-child(41)) > * {
--sibling-count: 41;
}
:has(> :last-child:nth-child(42)) > * {
--sibling-count: 42;
}
:has(> :last-child:nth-child(43)) > * {
--sibling-count: 43;
}
:has(> :last-child:nth-child(44)) > * {
--sibling-count: 44;
}
:has(> :last-child:nth-child(45)) > * {
--sibling-count: 45;
}
:has(> :last-child:nth-child(46)) > * {
--sibling-count: 46;
}
:has(> :last-child:nth-child(47)) > * {
--sibling-count: 47;
}
:has(> :last-child:nth-child(48)) > * {
--sibling-count: 48;
}
:has(> :last-child:nth-child(49)) > * {
--sibling-count: 49;
}
:has(> :last-child:nth-child(50)) > * {
--sibling-count: 50;
}
:has(> :last-child:nth-child(51)) > * {
--sibling-count: 51;
}
:has(> :last-child:nth-child(52)) > * {
--sibling-count: 52;
}
:has(> :last-child:nth-child(53)) > * {
--sibling-count: 53;
}
:has(> :last-child:nth-child(54)) > * {
--sibling-count: 54;
}
:has(> :last-child:nth-child(55)) > * {
--sibling-count: 55;
}
:has(> :last-child:nth-child(56)) > * {
--sibling-count: 56;
}
:has(> :last-child:nth-child(57)) > * {
--sibling-count: 57;
}
:has(> :last-child:nth-child(58)) > * {
--sibling-count: 58;
}
:has(> :last-child:nth-child(59)) > * {
--sibling-count: 59;
}
:has(> :last-child:nth-child(60)) > * {
--sibling-count: 60;
}
:has(> :last-child:nth-child(61)) > * {
--sibling-count: 61;
}
:has(> :last-child:nth-child(62)) > * {
--sibling-count: 62;
}
:has(> :last-child:nth-child(63)) > * {
--sibling-count: 63;
}
:has(> :last-child:nth-child(64)) > * {
--sibling-count: 64;
}
:has(> :last-child:nth-child(65)) > * {
--sibling-count: 65;
}
:has(> :last-child:nth-child(66)) > * {
--sibling-count: 66;
}
:has(> :last-child:nth-child(67)) > * {
--sibling-count: 67;
}
:has(> :last-child:nth-child(68)) > * {
--sibling-count: 68;
}
:has(> :last-child:nth-child(69)) > * {
--sibling-count: 69;
}
}

View file

@ -1,35 +1,35 @@
import { Component, createSignal, onCleanup, onMount } from "solid-js";
import { Entry } from "~/features/content";
import css from "./details.module.css";
interface DetailsProps {
entry: Entry;
}
export const Details: Component<DetailsProps> = (props) => {
const [header, setHeader] = createSignal<HTMLElement>();
onMount(() => {
const observer = new ResizeObserver(([entry]) => {
const { inlineSize, blockSize } = entry.contentBoxSize[0];
(entry.target as HTMLElement).style.setProperty(
"--ratio",
String((blockSize * 0.2) / inlineSize)
);
});
observer.observe(header()!);
onCleanup(() => observer.disconnect());
});
return (
<div class={css.container}>
<header ref={setHeader} class={css.header}>
<img class={css.background} src={props.entry.image} />
<h1>{props.entry.title}</h1>
</header>
</div>
);
};
import { Component, createSignal, onCleanup, onMount } from "solid-js";
import { Entry } from "~/features/content";
import css from "./details.module.css";
interface DetailsProps {
entry: Entry;
}
export const Details: Component<DetailsProps> = (props) => {
const [header, setHeader] = createSignal<HTMLElement>();
onMount(() => {
const observer = new ResizeObserver(([entry]) => {
const { inlineSize, blockSize } = entry.contentBoxSize[0];
(entry.target as HTMLElement).style.setProperty(
"--ratio",
String((blockSize * 0.2) / inlineSize)
);
});
observer.observe(header()!);
onCleanup(() => observer.disconnect());
});
return (
<div class={css.container}>
<header ref={setHeader} class={css.header}>
<img class={css.background} src={props.entry.image} />
<h1>{props.entry.title}</h1>
</header>
</div>
);
};

View file

@ -1,88 +1,88 @@
import {
ContextProviderProps,
createContextProvider,
} from "@solid-primitives/context";
import { action, createAsyncStore, query, useAction } from "@solidjs/router";
import { createStore } from "solid-js/store";
import { useSession } from "vinxi/http";
export enum ColorScheme {
Auto = "light dark",
Light = "light",
Dark = "dark",
}
export interface State {
colorScheme: ColorScheme;
hue: number;
}
const getSession = async () => {
"use server";
return useSession<State>({
password: process.env.SESSION_SECRET!,
});
};
export const getState = query(async () => {
"use server";
const session = await getSession();
if (Object.getOwnPropertyNames(session.data).length === 0) {
await session.update({
colorScheme: ColorScheme.Auto,
hue: 0,
});
}
return session.data;
}, "color-scheme");
const setState = action(async (state: State) => {
"use server";
const session = await getSession();
await session.update((prev) => ({ ...prev, ...state }));
}, "color-scheme");
interface ThemeContextType {
readonly theme: State;
setColorScheme(colorScheme: ColorScheme): void;
setHue(colorScheme: number): void;
}
const [ThemeContextProvider, useTheme] = createContextProvider<
ThemeContextType,
ContextProviderProps
>(
(props) => {
const updateState = useAction(setState);
const state = createAsyncStore(() => getState());
return {
get theme() {
return state.latest ?? { colorScheme: null };
},
setColorScheme(colorScheme) {
// updateState({ colorScheme, hue: state.latest!.hue });
},
setHue(hue) {
// updateState({ hue, colorScheme: state.latest!.colorScheme });
},
};
},
{
theme: {
colorScheme: ColorScheme.Auto,
hue: 180,
},
setColorScheme(colorScheme) {},
setHue(hue) {},
},
);
export { ThemeContextProvider, useTheme };
import {
ContextProviderProps,
createContextProvider,
} from "@solid-primitives/context";
import { action, createAsyncStore, query, useAction } from "@solidjs/router";
import { createStore } from "solid-js/store";
import { useSession } from "vinxi/http";
export enum ColorScheme {
Auto = "light dark",
Light = "light",
Dark = "dark",
}
export interface State {
colorScheme: ColorScheme;
hue: number;
}
const getSession = async () => {
"use server";
return useSession<State>({
password: process.env.SESSION_SECRET!,
});
};
export const getState = query(async () => {
"use server";
const session = await getSession();
if (Object.getOwnPropertyNames(session.data).length === 0) {
await session.update({
colorScheme: ColorScheme.Auto,
hue: 0,
});
}
return session.data;
}, "color-scheme");
const setState = action(async (state: State) => {
"use server";
const session = await getSession();
await session.update((prev) => ({ ...prev, ...state }));
}, "color-scheme");
interface ThemeContextType {
readonly theme: State;
setColorScheme(colorScheme: ColorScheme): void;
setHue(colorScheme: number): void;
}
const [ThemeContextProvider, useTheme] = createContextProvider<
ThemeContextType,
ContextProviderProps
>(
(props) => {
const updateState = useAction(setState);
const state = createAsyncStore(() => getState());
return {
get theme() {
return state.latest ?? { colorScheme: null };
},
setColorScheme(colorScheme) {
// updateState({ colorScheme, hue: state.latest!.hue });
},
setHue(hue) {
// updateState({ hue, colorScheme: state.latest!.colorScheme });
},
};
},
{
theme: {
colorScheme: ColorScheme.Auto,
hue: 180,
},
setColorScheme(colorScheme) {},
setHue(hue) {},
},
);
export { ThemeContextProvider, useTheme };

View file

@ -1,4 +1,4 @@
export { ThemeContextProvider, useTheme } from './context';
export { ThemeContextProvider, useTheme } from './context';
export { ColorSchemePicker } from './picker';

View file

@ -1,9 +1,9 @@
.picker {
grid-template-columns: auto 1fr;
}
.hue {
display: flex;
flex-flow: row;
align-items: center;
.picker {
grid-template-columns: auto 1fr;
}
.hue {
display: flex;
flex-flow: row;
align-items: center;
}

View file

@ -1,69 +1,69 @@
import {
WiMoonAltFirstQuarter,
WiMoonAltFull,
WiMoonAltNew,
} from "solid-icons/wi";
import {
Component,
createEffect,
For,
Match,
on,
Setter,
Switch,
} from "solid-js";
import { ColorScheme, useTheme } from "./context";
import css from "./picker.module.css";
import { Select } from "~/components/select";
const colorSchemes: Record<ColorScheme, keyof typeof ColorScheme> =
Object.fromEntries(
Object.entries(ColorScheme).map(([k, v]) => [v, k])
) as any;
export const ColorSchemePicker: Component = (props) => {
const themeContext = useTheme();
const setScheme: Setter<ColorScheme> = (next) => {
if (typeof next === "function") {
next = next();
}
themeContext.setColorScheme(next);
};
return (
<>
<label aria-label="Color scheme picker">
<Select
id="color-scheme-picker"
class={css.picker}
value={themeContext.theme.colorScheme}
setValue={setScheme}
values={colorSchemes}
>
{(k, v) => (
<>
<Switch>
<Match when={k === ColorScheme.Auto}>
<WiMoonAltFirstQuarter />
</Match>
<Match when={k === ColorScheme.Light}>
<WiMoonAltNew />
</Match>
<Match when={k === ColorScheme.Dark}>
<WiMoonAltFull />
</Match>
</Switch>
{v}
</>
)}
</Select>
</label>
{/* <label class={css.hue} aria-label="Hue slider">
<input type="range" min="0" max="360" value={theme.hue} onInput={e => setHue(e.target.valueAsNumber)} />
</label> */}
</>
);
};
import {
WiMoonAltFirstQuarter,
WiMoonAltFull,
WiMoonAltNew,
} from "solid-icons/wi";
import {
Component,
createEffect,
For,
Match,
on,
Setter,
Switch,
} from "solid-js";
import { ColorScheme, useTheme } from "./context";
import css from "./picker.module.css";
import { Select } from "~/components/select";
const colorSchemes: Record<ColorScheme, keyof typeof ColorScheme> =
Object.fromEntries(
Object.entries(ColorScheme).map(([k, v]) => [v, k])
) as any;
export const ColorSchemePicker: Component = (props) => {
const themeContext = useTheme();
const setScheme: Setter<ColorScheme> = (next) => {
if (typeof next === "function") {
next = next();
}
themeContext.setColorScheme(next);
};
return (
<>
<label aria-label="Color scheme picker">
<Select
id="color-scheme-picker"
class={css.picker}
value={themeContext.theme.colorScheme}
setValue={setScheme}
values={colorSchemes}
>
{(k, v) => (
<>
<Switch>
<Match when={k === ColorScheme.Auto}>
<WiMoonAltFirstQuarter />
</Match>
<Match when={k === ColorScheme.Light}>
<WiMoonAltNew />
</Match>
<Match when={k === ColorScheme.Dark}>
<WiMoonAltFull />
</Match>
</Switch>
{v}
</>
)}
</Select>
</label>
{/* <label class={css.hue} aria-label="Hue slider">
<input type="range" min="0" max="360" value={theme.hue} onInput={e => setHue(e.target.valueAsNumber)} />
</label> */}
</>
);
};

View file

@ -1,41 +1,41 @@
import { Meta } from "@solidjs/meta";
import { query, createAsync } from "@solidjs/router";
import { ParentProps } from "solid-js";
import { getRequestEvent } from "solid-js/web";
import { auth } from "~/auth.server";
import { Shell } from "~/features/shell";
import { useTheme } from "~/features/theme";
import { User } from "~/features/user";
const load = query(async (): Promise<User | undefined> => {
"use server";
const session = await auth.api.getSession(getRequestEvent()!.request);
if (session === null) {
return undefined;
}
const { username, name, email, image = null } = session.user;
return { username, name, email, image };
}, "session");
export const route = {
async preload() {
return load();
},
};
export default function ShellPage(props: ParentProps) {
const user = createAsync(() => load());
const themeContext = useTheme();
return (
<Shell user={user()}>
<Meta name="color-scheme" content={themeContext.theme.colorScheme} />
{props.children}
</Shell>
);
}
import { Meta } from "@solidjs/meta";
import { query, createAsync } from "@solidjs/router";
import { ParentProps } from "solid-js";
import { getRequestEvent } from "solid-js/web";
import { auth } from "~/auth.server";
import { Shell } from "~/features/shell";
import { useTheme } from "~/features/theme";
import { User } from "~/features/user";
const load = query(async (): Promise<User | undefined> => {
"use server";
const session = await auth.api.getSession(getRequestEvent()!.request);
if (session === null) {
return undefined;
}
const { username, name, email, image = null } = session.user;
return { username, name, email, image };
}, "session");
export const route = {
async preload() {
return load();
},
};
export default function ShellPage(props: ParentProps) {
const user = createAsync(() => load());
const themeContext = useTheme();
return (
<Shell user={user()}>
<Meta name="color-scheme" content={themeContext.theme.colorScheme} />
{props.children}
</Shell>
);
}