diff --git a/bert/caddy-natural-sort.patch b/bert/caddy-natural-sort.patch new file mode 100644 index 0000000..f86505a --- /dev/null +++ b/bert/caddy-natural-sort.patch @@ -0,0 +1,93 @@ +commit 668a0efbf6591ed7a90298bd8f6c63ad1a783587 +Author: Chandler Swift +Date: Sat Aug 30 22:52:35 2025 -0500 + + file_server: Implement natural sort for browse templates + +diff --git a/modules/caddyhttp/fileserver/browsetplcontext.go b/modules/caddyhttp/fileserver/browsetplcontext.go +index b9489c6a..339eea21 100644 +--- a/modules/caddyhttp/fileserver/browsetplcontext.go ++++ b/modules/caddyhttp/fileserver/browsetplcontext.go +@@ -325,11 +325,72 @@ type ( + byTime browseTemplateContext + ) + ++func isDigit(b byte) bool { return '0' <= b && b <= '9' } ++ ++// naturalLess compares two strings using natural ordering. This means that e.g. ++// "abc2" < "abc12". ++// ++// Non-digit sequences and numbers are compared separately. The former are ++// compared bytewise, while digits are compared numerically (except that ++// the number of leading zeros is used as a tie-breaker, so e.g. "2" < "02") ++// ++// Limitation: only ASCII digits (0-9) are considered. ++func naturalLess(str1, str2 string) bool { ++ idx1, idx2 := 0, 0 ++ for idx1 < len(str1) && idx2 < len(str2) { ++ c1, c2 := str1[idx1], str2[idx2] ++ dig1, dig2 := isDigit(c1), isDigit(c2) ++ switch { ++ case dig1 != dig2: // Digits before other characters. ++ return dig1 // True if LHS is a digit, false if the RHS is one. ++ case !dig1: // && !dig2, because dig1 == dig2 ++ // UTF-8 compares bytewise-lexicographically, no need to decode ++ // codepoints. ++ if c1 != c2 { ++ return c1 < c2 ++ } ++ idx1++ ++ idx2++ ++ default: // Digits ++ // Eat zeros. ++ for ; idx1 < len(str1) && str1[idx1] == '0'; idx1++ { ++ } ++ for ; idx2 < len(str2) && str2[idx2] == '0'; idx2++ { ++ } ++ // Eat all digits. ++ nonZero1, nonZero2 := idx1, idx2 ++ for ; idx1 < len(str1) && isDigit(str1[idx1]); idx1++ { ++ } ++ for ; idx2 < len(str2) && isDigit(str2[idx2]); idx2++ { ++ } ++ // If lengths of numbers with non-zero prefix differ, the shorter ++ // one is less. ++ if len1, len2 := idx1-nonZero1, idx2-nonZero2; len1 != len2 { ++ return len1 < len2 ++ } ++ // If they're equally long, string comparison is correct. ++ if nr1, nr2 := str1[nonZero1:idx1], str2[nonZero2:idx2]; nr1 != nr2 { ++ return nr1 < nr2 ++ } ++ // Otherwise, the one with less zeros is less. ++ // Because everything up to the number is equal, comparing the index ++ // after the zeros is sufficient. ++ if nonZero1 != nonZero2 { ++ return nonZero1 < nonZero2 ++ } ++ } ++ // They're identical so far, so continue comparing. ++ } ++ // So far they are identical. At least one is ended. If the other continues, ++ // it sorts last. ++ return len(str1) < len(str2) ++} ++ + func (l byName) Len() int { return len(l.Items) } + func (l byName) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] } + + func (l byName) Less(i, j int) bool { +- return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name) ++ return naturalLess(strings.ToLower(l.Items[i].Name), strings.ToLower(l.Items[j].Name)) + } + + func (l byNameDirFirst) Len() int { return len(l.Items) } +@@ -338,7 +399,7 @@ func (l byNameDirFirst) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l. + func (l byNameDirFirst) Less(i, j int) bool { + // sort by name if both are dir or file + if l.Items[i].IsDir == l.Items[j].IsDir { +- return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name) ++ return naturalLess(strings.ToLower(l.Items[i].Name), strings.ToLower(l.Items[j].Name)) + } + // sort dir ahead of file + return l.Items[i].IsDir diff --git a/bert/configuration.nix b/bert/configuration.nix index e8a81a0..2e30685 100644 --- a/bert/configuration.nix +++ b/bert/configuration.nix @@ -14,6 +14,14 @@ ./services/owntracks-recorder.nix ]; + nixpkgs.overlays = [ + (self: super: { + caddy = super.caddy.overrideAttrs (oldAttrs: { + patches = (oldAttrs.patches or []) ++ [ ./caddy-natural-sort.patch ]; + }); + }) + ]; + # Bootloader boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; diff --git a/bert/services/http/stjohnscccc.org.nix b/bert/services/http/stjohnscccc.org.nix index de32cea..cc96a3c 100644 --- a/bert/services/http/stjohnscccc.org.nix +++ b/bert/services/http/stjohnscccc.org.nix @@ -29,6 +29,8 @@ } } + redir /downloads/scholarship /downloads/2025%20Nemitz%20Scholarship%20Application.pdf + handle { encode zstd gzip php_fastcgi unix/${config.services.phpfpm.pools.stjohnscccc.socket} diff --git a/oscar/configuration.nix b/oscar/configuration.nix index 3caa3e9..f91ec8d 100644 --- a/oscar/configuration.nix +++ b/oscar/configuration.nix @@ -121,7 +121,7 @@ blender freecad frescobaldi - gimp + gimp3 gnome-sound-recorder inkscape josm @@ -178,9 +178,7 @@ # Unfree: discord - (import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/348ec8c846cce39c467cac90ddbbc9ce9bf61bd8.tar.gz") { - config.allowUnfree = true; - }).factorio-space-age + factorio-space-age ]; programs.thunderbird.enable = true;