Template with echo, htmx, tailwindcss, templ

This commit is contained in:
ekzyis 2025-11-07 16:58:39 +01:00
parent d68cf1cc8d
commit 486a445c85
33 changed files with 11662 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.env
template
*_templ.go
public/css/tailwind.css

6
Makefile Normal file
View File

@ -0,0 +1,6 @@
.PHONY: build
build:
templ generate
tailwindcss -i ./tailwind.css -o ./public/css/tailwind.css
go build -o template main.go

44
db/db.go Normal file
View File

@ -0,0 +1,44 @@
package db
import (
"context"
"database/sql"
"log"
_ "github.com/lib/pq"
)
type Db struct {
*sql.DB
}
type Tx struct {
*sql.Tx
}
func New(url string) (*Db, error) {
db, err := sql.Open("postgres", url)
if err != nil {
return nil, err
}
// test connection
_, err = db.Exec("SELECT 1")
if err != nil {
log.Printf("failed to connect to database: %v", err)
}
return &Db{db}, nil
}
func (d *Db) Migrate() error {
return nil
}
func (d *Db) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
tx, err := d.DB.BeginTx(ctx, opts)
if err != nil {
return nil, err
}
return &Tx{tx}, nil
}

55
env/env.go vendored Normal file
View File

@ -0,0 +1,55 @@
package env
import (
"fmt"
"log"
"net/url"
"os/exec"
"strings"
"github.com/joho/godotenv"
"github.com/namsral/flag"
)
var (
Port int
Env string
PublicUrl string
CommitShortSha string
CommitLongSha string
PostgresUrl string
PostgresUrlWithoutPassword string
)
func Load(filenames ...string) error {
if err := godotenv.Load(); err != nil {
return err
}
flag.IntVar(&Port, "PORT", 4444, "Server port")
flag.StringVar(&PublicUrl, "PUBLIC_URL", "", "Base URL")
flag.StringVar(&PostgresUrl, "POSTGRES_DB", "", "PostgreSQL connection URL")
flag.StringVar(&Env, "ENV", "development", "Build environment")
return nil
}
func Parse() {
flag.Parse()
CommitLongSha = execCmd("git", "rev-parse", "HEAD")
CommitShortSha = execCmd("git", "rev-parse", "--short", "HEAD")
if u, err := url.Parse(PostgresUrl); err == nil {
if pw, ok := u.User.Password(); ok {
PostgresUrlWithoutPassword = strings.Replace(PostgresUrl, fmt.Sprintf(":%s@", pw), ":*****@", 1)
} else {
PostgresUrlWithoutPassword = PostgresUrl
}
}
}
func execCmd(name string, args ...string) string {
cmd := exec.Command(name, args...)
stdout, err := cmd.Output()
if err != nil {
log.Fatal(err)
}
return strings.TrimSpace(string(stdout))
}

61
flake.lock generated Normal file
View File

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"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"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1764316264,
"narHash": "sha256-82L+EJU+40+FIdeG4gmUlOF1jeSwlf2AwMarrpdHF6o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9a7b80b6f82a71ea04270d7ba11b48855681c4b0",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"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",
"version": 7
}

32
flake.nix Normal file
View File

@ -0,0 +1,32 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
go
tailwindcss_4
gnumake
postgresql_17
];
};
apps.default = {
type = "app";
program = toString (pkgs.writeShellScript "serve" ''
# install templ if not already installed
command -v templ > /dev/null || go install github.com/a-h/templ/cmd/templ@latest
${pkgs.make}/bin/make build
'');
};
}
);
}

512
fonts/chunky.flf Normal file
View File

@ -0,0 +1,512 @@
flf2a$ 5 4 20 15 1
Square by Chris Gill, 30-JUN-94 -- based on .sig of Jeb Hagan.
$@
$@
$@
$@
$@@
__ @
| |@
|__|@
|__|@
@@
____ @
| | |@
|_|_|@
@
@@
_____ @
_| | |_ @
|_ _|@
|_ _|@
|__|__| @@
__,-,__ @
| ' '__|@
|__ |@
|_______|@
|_| @@
__ ___ @
|__| |@
| __|@
|___|__|@
@@
__,-,__ @
| ' '__|@
| __|@
|_______|@
|_| @@
__ @
| |@
|_|@
@
@@
___ @
,' _|@
| | @
| |_ @
`.___|@@
___ @
|_ `.@
| |@
_| |@
|___,'@@
__ _ __ @
| | | |@
> < @
|__|_|__|@
@@
__ @
_| |_ @
|_ _|@
|__| @
@@
@
@
__ @
| |@
|_|@@
@
______ @
|______|@
@
@@
@
@
__ @
|__|@
@@
___@
/ /@
,' ,' @
/__/ @
@@
______ @
| |@
| -- |@
|______|@
@@
____ @
|_ | @
_| |_ @
|______|@
@@
______ @
|__ |@
| __|@
|______|@
@@
______ @
|__ |@
|__ |@
|______|@
@@
_____ @
| | | @
|__ |@
|__| @
@@
______ @
| __|@
|__ |@
|______|@
@@
______ @
| __|@
| __ |@
|______|@
@@
______ @
| |@
|_ |@
|____|@
@@
______ @
| __ |@
| __ |@
|______|@
@@
______ @
| __ |@
|__ |@
|______|@
@@
__ @
|__|@
__ @
|__|@
@@
__ @
|__|@
__ @
| |@
|_|@@
__ @
,' _|@
/ / @
\ \_ @
`.__|@@
@
______ @
|______|@
|______|@
@@
__ @
|_ `. @
\ \@
_/ /@
|__,' @@
_____ @
|__ |@
', ,-'@
|--| @
'--' @@
_________ @
| ___ |@
| | _ |@
| |______|@
|_________|@@
_______ @
| _ |@
| |@
|___|___|@
@@
______ @
| __ \@
| __ <@
|______/@
@@
______ @
| |@
| ---|@
|______|@
@@
_____ @
| \ @
| -- |@
|_____/ @
@@
_______ @
| ___|@
| ___|@
|_______|@
@@
_______ @
| ___|@
| ___|@
|___| @
@@
_______ @
| __|@
| | |@
|_______|@
@@
_______ @
| | |@
| |@
|___|___|@
@@
_______ @
|_ _|@
_| |_ @
|_______|@
@@
_____ @
_| |@
| |@
|_______|@
@@
__ __ @
| |/ |@
| < @
|__|\__|@
@@
_____ @
| |_ @
| |@
|_______|@
@@
_______ @
| | |@
| |@
|__|_|__|@
@@
_______ @
| | |@
| |@
|__|____|@
@@
_______ @
| |@
| - |@
|_______|@
@@
______ @
| __ \@
| __/@
|___| @
@@
_______ @
| |@
| - _|@
|_______|@
@@
______ @
| __ \@
| <@
|___|__|@
@@
_______ @
| __|@
|__ |@
|_______|@
@@
_______ @
|_ _|@
| | @
|___| @
@@
_______ @
| | |@
| | |@
|_______|@
@@
___ ___ @
| | |@
| | |@
\_____/ @
@@
________ @
| | | |@
| | | |@
|________|@
@@
___ ___ @
| | |@
|- -|@
|___|___|@
@@
___ ___ @
| | |@
\ / @
|___| @
@@
_______ @
|__ |@
| __|@
|_______|@
@@
____ @
| _|@
| | @
| |_ @
|____|@@
___ @
\ \ @
`. `. @
\__\@
@@
____ @
|_ |@
| |@
_| |@
|____|@@
____ @
| |@
|_/\_|@
@
@@
@
@
@
______ @
|______|@@
__ @
| |@
|_| @
@
@@
@
.---.-.@
| _ |@
|___._|@
@@
__ @
| |--.@
| _ |@
|_____|@
@@
@
.----.@
| __|@
|____|@
@@
__ @
.--| |@
| _ |@
|_____|@
@@
@
.-----.@
| -__|@
|_____|@
@@
___ @
.' _|@
| _|@
|__| @
@@
@
.-----.@
| _ |@
|___ |@
|_____|@@
__ @
| |--.@
| |@
|__|__|@
@@
__ @
|__|@
| |@
|__|@
@@
__ @
|__|@
| |@
| |@
|___|@@
__ @
| |--.@
| < @
|__|__|@
@@
__ @
| |@
| |@
|__|@
@@
@
.--------.@
| |@
|__|__|__|@
@@
@
.-----.@
| |@
|__|__|@
@@
@
.-----.@
| _ |@
|_____|@
@@
@
.-----.@
| _ |@
| __|@
|__| @@
@
.-----.@
| _ |@
|__ |@
|__|@@
@
.----.@
| _|@
|__| @
@@
@
.-----.@
|__ --|@
|_____|@
@@
__ @
| |_ @
| _|@
|____|@
@@
@
.--.--.@
| | |@
|_____|@
@@
@
.--.--.@
| | |@
\___/ @
@@
@
.--.--.--.@
| | | |@
|________|@
@@
@
.--.--.@
|_ _|@
|__.__|@
@@
@
.--.--.@
| | |@
|___ |@
|_____|@@
@
.-----.@
|-- __|@
|_____|@
@@
___ @
| _|@
/ / @
\ \_ @
|___|@@
__ @
| |@
| |@
| |@
|__|@@
___ @
|_ | @
\ \@
_/ /@
|___| @@
___ @
| ' |@
|_,_| @
@
@@
.--.--.@
|-----|@
| - |@
|__|__|@
@@
.--.--.@
|-----|@
| _ |@
|_____|@
@@
.--.--.@
|--|--|@
| | |@
|_____|@
@@
.--.--.@
|---.-|@
| _ |@
|___._|@
@@
.--.--.@
|-----|@
| _ |@
|_____|@
@@
.--.--.@
|--|--|@
| | |@
|_____|@
@@
_______ @
| __ \@
| __ <@
| |____/@
|__| @@

569
fonts/drpepper.flf Normal file
View File

@ -0,0 +1,569 @@
flf2a$ 5 4 20 0 16
Font : Dr. Pepper (after a name in one sig done in this style).
Author: Eero Tamminen, t150315@cc.tut.fi.
Characters '#' and '&' are lousy and I'm not very satisfied
with the '$' or 't'... Suggestions?
Explanation of first line:
flf2 - "magic number" for file identifiction
a - should always be `a', for now
$ - the "hardblank" -- prints s a blank, but can't be smushed
5 - height of a character
4 - height of a character, not including descenders
20 - max line length (excluding comment lines) + fudge factor
0 - default smushmode for this font
16 - number of comment lines
$@
$@
$@
$@
$@@
_ @
| |@
|_/@
<_>@
@@
_ _@
|/|/@
@
@
@@
@
$_|_|_$@
$_|_|_$@
| | @
@@
@
||_@
<_-<@
/__/@
|| @@
__@
<>/ /@
/ / @
/_/<>@
@@
_ @
< > @
/.\/$@
\_/\$@
@@
_@
|/@
@
@
@@
__@
/ /@
| | @
| | @
\_\@@
__ @
\ \ @
| |@
| |@
/_/ @@
@
_/\_@
> <@
\/ @
@@
_ @
_| |_ @
|_ _|@
|_| @
@@
@
@
_@
|/@
@@
@
___ @
|___|@
@
@@
@
@
_ @
<_>@
@@
__@
/ /@
/ / @
/_/ @
@@
___ @
| |@
| / |@
`___'@
@@
_ @
/ |@
| |@
|_|@
@@
___ @
<_ >@
/ / @
<___>@
@@
____@
<__ /@
<_ \@
<___/@
@@
__ @
/. | @
/_ .|@
|_| @
@@
___ @
| __|@
`__ \@
|___/@
@@
___ @
| __>@
| . \@
`___/@
@@
___ @
|_ |@
/ / @
/_/ @
@@
___ @
< . >@
/ . \@
\___/@
@@
___ @
| . |@
`_ /@
/_/ @
@@
_ @
<_>@
_ @
<_>@
@@
_ @
<_>@
_ @
|/ @
@@
__@
/ /@
< < @
\_\@
@@
___ @
|___|@
___ @
|___|@
@@
__ @
\ \ @
> >@
/_/ @
@@
___ @
<_. >@
/_/ @
<_> @
@@
___ @
| "|@
| \_|@
`___/@
@@
___ @
| . |@
| |@
|_|_|@
@@
___ @
| . >@
| . \@
|___/@
@@
___ @
| _>@
| <__@
`___/@
@@
___ @
| . \@
| | |@
|___/@
@@
___ @
| __>@
| _> @
|___>@
@@
___ @
| __>@
| _> @
|_| @
@@
___ @
/ _> @
| <_/\@
`____/@
@@
_ _ @
| | |@
| |@
|_|_|@
@@
_ @
| |@
| |@
|_|@
@@
_ @
| |@
_| |@
\__/@
@@
_ __@
| / /@
| \ @
|_\_\@
@@
_ @
| | @
| |_ @
|___|@
@@
__ __ @
| \ \@
| |@
|_|_|_|@
@@
_ _ @
| \ |@
| |@
|_\_|@
@@
___ @
| . |@
| | |@
`___'@
@@
___ @
| . \@
| _/@
|_| @
@@
___ @
| . |@
| | |@
`___\@
@@
___ @
| . \@
| /@
|_\_\@
@@
___ @
/ __>@
\__ \@
<___/@
@@
___ @
|_ _|@
| | @
|_| @
@@
_ _ @
| | |@
| ' |@
`___'@
@@
_ _ @
| | |@
| ' |@
|__/ @
@@
_ _ _ @
| | | |@
| | | |@
|__/_/ @
@@
__ _$@
\ \/ @
\ \ @
_/\_\ @
@@
_ _ @
| | |@
\ /@
|_| @
@@
____@
|_ /@
/ / @
/___|@
@@
___ @
| _|@
| | @
| |_ @
|___|@@
__ @
\ \ @
\ \ @
\_\@
@@
___ @
|_ |@
| |@
_| |@
|___|@@
/\ @
</\>@
@
@
@@
@
@
___ @
|___|@
@@
_ @
\|@
@
@
@@
@
___ @
<_> |@
<___|@
@@
_ @
| |_ @
| . \@
|___/@
@@
@
___ @
/ | '@
\_|_.@
@@
_ @
_| |@
/ . |@
\___|@
@@
@
___ @
/ ._>@
\___.@
@@
___ @
| | '@
| |- @
|_| @
@@
@
___ @
/ . |@
\_. |@
<___'@@
_ @
| |_ @
| . |@
|_|_|@
@@
_ @
<_>@
| |@
|_|@
@@
_ @
<_>@
| |@
| |@
<__'@@
_ @
| |__@
| / /@
|_\_\@
@@
_ @
| |@
| |@
|_|@
@@
@
._ _ _ @
| ' ' |@
|_|_|_|@
@@
@
._ _ @
| ' |@
|_|_|@
@@
@
___ @
/ . \@
\___/@
@@
@
___ @
| . \@
| _/@
|_| @@
@
___ @
/ . |@
\_ |@
|_|@@
@
_ _ @
| '_>@
|_| @
@@
@
___@
<_-<@
/__/@
@@
_ @
$_| |_$@
| | @
|_| @
@@
@
_ _ @
| | |@
`___|@
@@
@
_ _ @
| | |@
|__/ @
@@
@
_ _ _ @
| | | |@
|__/_/ @
@@
@
__ @
\ \/@
/\_\@
@@
@
_ _ @
| | |@
`_. |@
<___'@@
@
.___@
/ /@
/___@
@@
__@
/ /@
/ | @
\ | @
\_\@@
||@
||@
||@
||@
@@
__ @
\ \ @
| \@
| /@
/_/ @@
@
/\/|@
|/\/ @
@
@@
<>_<>@
| . |@
| |@
|_|_|@
@@
<>_<>@
| . |@
| | |@
`___'@
@@
<>_<>@
| | |@
| ' |@
`___'@
@@
@
<>_<>@
`_> |@
<___|@
@@
@
<>_<>@
/ . \@
\___/@
@@
@
<>_<>@
| | |@
`___|@
@@
___ @
| . >@
| . \@
| ._/@
|_| @@
196
<>_<>@
| . |@
| |@
|_|_|@
@@
214
<>_<>@
| . |@
| | |@
`___'@
@@
220
<>_<>@
| | |@
| ' |@
`___'@
@@
223
___ @
| . >@
| . \@
| ._/@
|_| @@
228
@
<>_<>@
`_> |@
<___|@
@@
246
@
<>_<>@
/ . \@
\___/@
@@
252
@
<>_<>@
| | |@
`___|@
@@

617
fonts/graffiti.flf Normal file
View File

@ -0,0 +1,617 @@
flf2a$ 6 5 32 15 4
Font name is graffiti.flf
This figlet font designed by Leigh Purdie (purdie@zeus.usq.edu.au)
'fig-fonted' by Leigh Purdie and Tim Maggio (tim@claremont.com)
Date: 5 Mar 1994
$@
$@
$@
$@
$@
$@@
._.@
| |@
| |@
\|@
__@
\/@@
/\/\@
)/)/@
@
@
@
@@
_ _ @
__| || |__@
\ __ /@
| || | @
/_ ~~ _\@
|_||_| @@
____/\__@
/ / /_/@
\__/ / \ @
/ / / \@
/_/ /__ /@
\/ \/ @@
_ /\ @
/ \ / / @
\_// /_ @
/ // \@
/ / \_/@
\/ @@
____ @
/ _ \ @
> _ </\@
/ <_\ \/@
\_____\ \@
\/@@
/\@
)/@
@
@
@
@@
$ ___$@
$ / /$@
$ / / $@
$( ( $@
$ \ \ $@
$ \__\$@@
$___ $@
$\ \ $@
$ \ \ $@
$ ) )$@
$ / / $@
$/__/ $@@
$ $@
$ /\|\/\ $@
$_) (__$@
$\_ _/$@
$ ) \ $@
$ \/\|\/ $@@
$ $@
$ .__ $@
$ __| |___$@
$/__ __/$@
$ |__| $@
$ $@@
$ @
$ @
$ @
$ @
$/\@
$)/@@
$ $@
$ $@
$ ______$@
$/_____/$@
$ $@
$ $@@
$ @
$ @
$ @
$ @
$/\@
$\/@@
$ /\$@
$ / /$@
$ / / $@
$ / / $@
$/ / $@
$\/ $@@
_______ @
\ _ \ @
/ /_\ \ @
\ \_/ \@
\_____ /@
\/ @@
____ @
/_ |@
| |@
| |@
|___|@
@@
________ @
\_____ \ @
/ ____/ @
/ \ @
\_______ \@
\/@@
________ @
\_____ \ @
_(__ < @
/ \@
/______ /@
\/ @@
_____ @
/ | | @
/ | |_@
/ ^ /@
\____ | @
|__| @@
.________@
| ____/@
|____ \ @
/ \@
/______ /@
\/ @@
________@
/ _____/@
/ __ \ @
\ |__\ \@
\_____ /@
\/ @@
_________ @
\______ \@
/ /@
/ / @
/____/ @
@@
______ @
/ __ \ @
> < @
/ -- \@
\______ /@
\/ @@
________ @
/ __ \@
\____ /@
/ / @
/____/ @
@@
$ $@
$/\$@
$\/$@
$/\$@
$\/$@
$ $@@
$ $@
$/\$@
$\/$@
$/\$@
$)/$@
$ $@@
$ __$@
$ / /$@
$/ / $@
$\ \ $@
$ \_\$@
$ $@@
$ $@
$ ______$@
$/_____/$@
$/_____/$@
$ $@
$ $@@
$__ $@
$\ \ $@
$ \ \$@
$ / /$@
$/_/ $@
$ $@@
_________ @
\_____ \@
/ __/@
| | @
|___| @
<___> @@
_____ @
/ ___ \ @
/ / ._\ \@
< \_____/@
\_____\ @
@@
_____ @
/ _ \ @
/ /_\ \ @
/ | \@
\____|__ /@
\/ @@
__________ @
\______ \@
| | _/@
| | \@
|______ /@
\/ @@
_________ @
\_ ___ \ @
/ \ \/ @
\ \____@
\______ /@
\/ @@
________ @
\______ \ @
| | \ @
| ` \@
/_______ /@
\/ @@
___________@
\_ _____/@
| __)_ @
| \@
/_______ /@
\/ @@
___________@
\_ _____/@
| __) @
| \ @
\___ / @
\/ @@
________ @
/ _____/ @
/ \ ___ @
\ \_\ \@
\______ /@
\/ @@
___ ___ @
/ | \ @
/ ~ \@
\ Y /@
\___|_ / @
\/ @@
.___ @
| |@
| |@
| |@
|___|@
@@
____.@
| |@
| |@
/\__| |@
\________|@
@@
____ __.@
| |/ _|@
| < @
| | \ @
|____|__ \@
\/@@
.____ @
| | @
| | @
| |___ @
|_______ \@
\/@@
_____ @
/ \ @
/ \ / \ @
/ Y \@
\____|__ /@
\/ @@
_______ @
\ \ @
/ | \ @
/ | \@
\____|__ /@
\/ @@
________ @
\_____ \ @
/ | \ @
/ | \@
\_______ /@
\/ @@
__________ @
\______ \@
| ___/@
| | @
|____| @
@@
________ @
\_____ \ @
/ / \ \ @
/ \_/. \@
\_____\ \_/@
\__>@@
__________ @
\______ \@
| _/@
| | \@
|____|_ /@
\/ @@
_________@
/ _____/@
\_____ \ @
/ \@
/_______ /@
\/ @@
___________@
\__ ___/@
| | @
| | @
|____| @
@@
____ ___ @
| | \@
| | /@
| | / @
|______/ @
@@
____ ____@
\ \ / /@
\ Y / @
\ / @
\___/ @
@@
__ __ @
/ \ / \@
\ \/\/ /@
\ / @
\__/\ / @
\/ @@
____ ___@
\ \/ /@
\ / @
/ \ @
/___/\ \@
\_/@@
_____.___.@
\__ | |@
/ | |@
\____ |@
/ ______|@
\/ @@
__________@
\____ /@
/ / @
/ /_ @
/_______ \@
\/@@
$.____ $@
$| _|$@
$| | $@
$| | $@
$| |_ $@
$|____|$@@
/\ @
\ \ @
\ \ @
\ \ @
\ \@
\/@@
$ ____.$@
$|_ |$@
$ | |$@
$ | |$@
$ _| |$@
$|____|$@@
$ /\ $@
$/ \$@
$\/\/$@
$ $@
$ $@
$ $@@
@
@
@
@
______@
/_____/@@
/\@
\(@
@
@
@
@@
@
_____ @
\__ \ @
/ __ \_@
(____ /@
\/ @@
___. @
\_ |__ @
| __ \ @
| \_\ \@
|___ /@
\/ @@
@
____ @
_/ ___\ @
\ \___ @
\___ >@
\/ @@
.___@
__| _/@
/ __ | @
/ /_/ | @
\____ | @
\/ @@
@
____ @
_/ __ \ @
\ ___/ @
\___ >@
\/ @@
_____ @
_/ ____\@
\ __\ @
| | @
|__| @
@@
@
____ @
/ ___\ @
/ /_/ >@
\___ / @
/_____/ @@
.__ @
| |__ @
| | \ @
| Y \@
|___| /@
\/ @@
.__ @
|__|@
| |@
| |@
|__|@
@@
__ @
|__|@
| |@
| |@
/\__| |@
\______|@@
__ @
| | __@
| |/ /@
| < @
|__|_ \@
\/@@
.__ @
| | @
| | @
| |__@
|____/@
@@
@
_____ @
/ \ @
| Y Y \@
|__|_| /@
\/ @@
@
____ @
/ \ @
| | \@
|___| /@
\/ @@
@
____ @
/ _ \ @
( <_> )@
\____/ @
@@
@
______ @
\____ \ @
| |_> >@
| __/ @
|__| @@
@
______@
/ ____/@
< <_| |@
\__ |@
|__|@@
@
_______ @
\_ __ \@
| | \/@
|__| @
@@
@
______@
/ ___/@
\___ \ @
/____ >@
\/ @@
__ @
_/ |_ @
\ __\@
| | @
|__| @
@@
@
__ __ @
| | \@
| | /@
|____/ @
@@
@
___ __@
\ \/ /@
\ / @
\_/ @
@@
@
__ _ __@
\ \/ \/ /@
\ / @
\/\_/ @
@@
@
___ ___@
\ \/ /@
> < @
/__/\_ \@
\/@@
@
___.__.@
< | |@
\___ |@
/ ____|@
\/ @@
@
________@
\___ /@
/ / @
/_____ \@
\/@@
$ ___$@
$/ / $@
$\ \ $@
$< < $@
$/ / $@
$\_\_$@@
$._.$@
$| |$@
$|_|$@
$|-|$@
$| |$@
$|_|$@@
$___ $@
$ \ \$@
$ / /$@
$ > >$@
$ \ \$@
$_/_/$@@
$ ___ $@
$/ _ \_/\$@
$\/ \___/$@
$ $@
$ $@
$ $@@
@
@
@
@
@
@@
@
@
@
@
@
@@
@
@
@
@
@
@@
@
@
@
@
@
@@
@
@
@
@
@
@@
@
@
@
@
@
@@
@
@
@
@
@
@@

614
fonts/rectangles.flf Normal file
View File

@ -0,0 +1,614 @@
flf2a$ 6 5 15 1 1
rectangles.flf by David Villegas <mnementh@netcom.com> 12/94
$$@
$$@
$$@
$$@
$$@
$$@@
__ @
| |@
| |@
|__|@
|__|@
@@
_ _ @
| | |@
|_|_|@
$$$ @
$$$ @
$$$ @@
_ _ @
_| | |_ @
|_ _|@
|_ _|@
|_|_| @
@@
_ @
_| |_ @
| __|@
|__ |@
|_ _|@
|_| @@
@
__ __ @
|__| |@
| __|@
|__|__|@
@@
_ @
_| |_ @
| __|@
| __|@
|_ _|@
|_| @@
_ @
| |@
|_|@
$ @
$ @
$ @@
_ @
_|_|@
| | @
| | @
|_|_ @
|_|@@
_ @
|_|_ @
| |@
| |@
_|_|@
|_| @@
@
_____ @
| | | |@
|- -|@
|_|_|_|@
@@
@
_ @
_| |_ @
|_ _|@
|_| @
@@
$ @
$ @
$ @
_ @
| |@
|_|@@
$$$ @
$$$ @
___ @
|___|@
$$$ @
$$$ @@
$ @
$ @
$ @
_ @
|_|@
$ @@
@
_ @
/ |@
/ / @
|_/ @
@@
@
___ @
| |@
| | |@
|___|@
@@
@
___ @
|_ | @
_| |_ @
|_____|@
@@
@
___ @
|_ |@
| _|@
|___|@
@@
@
___ @
|_ |@
|_ |@
|___|@
@@
@
___ @
| | |@
|_ |@
|_|@
@@
@
___ @
| _|@
|_ |@
|___|@
@@
@
___ @
| _|@
| . |@
|___|@
@@
@
___ @
|_ |@
| |@
|_|@
@@
@
___ @
| . |@
| . |@
|___|@
@@
@
___ @
| . |@
|_ |@
|___|@
@@
@
_ @
|_|@
_ @
|_|@
@@
@
_ @
|_|@
_ @
| |@
|_|@@
__@
/ /@
/ / @
< < @
\ \ @
\_\@@
$$$$$ @
$$$$$ @
_____ @
|_____|@
|_____|@
$$$$$ @@
__ @
\ \ @
\ \ @
> >@
/ / @
/_/ @@
_____ @
|___ |@
| _|@
|_| @
|_| @
@@
@
_____ @
| __ |@
| |___|@
|_____|@
@@
@
_____ @
| _ |@
| |@
|__|__|@
@@
@
_____ @
| __ |@
| __ -|@
|_____|@
@@
@
_____ @
| |@
| --|@
|_____|@
@@
@
____ @
| \ @
| | |@
|____/ @
@@
@
_____ @
| __|@
| __|@
|_____|@
@@
@
_____ @
| __|@
| __|@
|__| @
@@
@
_____ @
| __|@
| | |@
|_____|@
@@
@
_____ @
| | |@
| |@
|__|__|@
@@
@
_____ @
| |@
|- -|@
|_____|@
@@
@
__ @
__| |@
| | |@
|_____|@
@@
@
_____ @
| | |@
| -|@
|__|__|@
@@
@
__ @
| | @
| |__ @
|_____|@
@@
@
_____ @
| |@
| | | |@
|_|_|_|@
@@
@
_____ @
| | |@
| | | |@
|_|___|@
@@
@
_____ @
| |@
| | |@
|_____|@
@@
@
_____ @
| _ |@
| __|@
|__| @
@@
@
_____ @
| |@
| | |@
|__ _|@
|__|@@
@
_____ @
| __ |@
| -|@
|__|__|@
@@
@
_____ @
| __|@
|__ |@
|_____|@
@@
@
_____ @
|_ _|@
| | @
|_| @
@@
@
_____ @
| | |@
| | |@
|_____|@
@@
@
_____ @
| | |@
| | |@
\___/ @
@@
@
_ _ _ @
| | | |@
| | | |@
|_____|@
@@
@
__ __ @
| | |@
|- -|@
|__|__|@
@@
@
__ __ @
| | |@
|_ _|@
|_| @
@@
@
_____ @
|__ |@
| __|@
|_____|@
@@
___ @
| _|@
| | @
| | @
| |_ @
|___|@@
@
_ @
| \ @
\ \ @
\_|@
@@
___ @
|_ |@
| |@
| |@
_| |@
|___|@@
_____ @
| _ |@
|_| |_|@
$$$$$ @
$$$$$ @
$$$$$ @@
$$$$$ @
$$$$$ @
$$$$$ @
$$$$$ @
_____ @
|_____|@@
___ @
|_ |@
|_|@
$$$ @
$$$ @
$$$ @@
@
@
___ @
| .'|@
|__,|@
@@
@
_ @
| |_ @
| . |@
|___|@
@@
@
@
___ @
| _|@
|___|@
@@
@
_ @
_| |@
| . |@
|___|@
@@
@
@
___ @
| -_|@
|___|@
@@
@
___ @
| _|@
| _|@
|_| @
@@
@
@
___ @
| . |@
|_ |@
|___|@@
@
_ @
| |_ @
| |@
|_|_|@
@@
@
_ @
|_|@
| |@
|_|@
@@
@
_ @
|_|@
| |@
_| |@
|___|@@
@
_ @
| |_ @
| '_|@
|_,_|@
@@
@
_ @
| |@
| |@
|_|@
@@
@
@
_____ @
| |@
|_|_|_|@
@@
@
@
___ @
| |@
|_|_|@
@@
@
@
___ @
| . |@
|___|@
@@
@
@
___ @
| . |@
| _|@
|_| @@
@
@
___ @
| . |@
|_ |@
|_|@@
@
@
___ @
| _|@
|_| @
@@
@
@
___ @
|_ -|@
|___|@
@@
@
_ @
| |_ @
| _|@
|_| @
@@
@
@
_ _ @
| | |@
|___|@
@@
@
@
_ _ @
| | |@
\_/ @
@@
@
@
_ _ _ @
| | | |@
|_____|@
@@
@
@
_ _ @
|_'_|@
|_,_|@
@@
@
@
_ _ @
| | |@
|_ |@
|___|@@
@
@
___ @
|- _|@
|___|@
@@
___ @
| _|@
_| | @
|_ | @
| |_ @
|___|@@
_ @
| |@
| |@
| |@
| |@
|_|@@
___ @
|_ | @
| |_ @
| _|@
_| | @
|___| @@
_____ @
| | |@
|_|___|@
$$$$$ @
$$$$$ @
$$$$$ @@
__ __ @
|__|__|@
| _ |@
| |@
|__|__|@
@@
__ __ @
|__|__|@
| |@
| | |@
|_____|@
@@
__ __ @
|__|__|@
| | |@
| | |@
|_____|@
@@
_ _ @
|_|_|@
___ @
| .'|@
|__,|@
@@
_ _ @
|_|_|@
___ @
| . |@
|___|@
@@
_ _ @
|_|_|@
_ _ @
| | |@
|___|@
@@
@
_____ @
| __ |@
| __ -|@
| ___|@
|_| @@

1295
fonts/slant.flf Normal file

File diff suppressed because it is too large Load Diff

2227
fonts/standard.flf Normal file

File diff suppressed because it is too large Load Diff

25
go.mod Normal file
View File

@ -0,0 +1,25 @@
module github.com/ekzyis/echo-htmx-templ-tailwindcss
go 1.24.6
require (
github.com/a-h/templ v0.3.960
github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v4 v4.13.4
github.com/lib/pq v1.10.9
github.com/lukesampson/figlet v0.0.0-20190211215653-8a3ef4a6ac42
github.com/namsral/flag v1.7.4-pre
)
require (
github.com/labstack/gommon v0.4.2 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.11.0 // indirect
)

43
go.sum Normal file
View File

@ -0,0 +1,43 @@
github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM=
github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lukesampson/figlet v0.0.0-20190211215653-8a3ef4a6ac42 h1:UtyD+eBVdLYSj5/pjfSR6mtnzMgIiOVcFT024G2l4CY=
github.com/lukesampson/figlet v0.0.0-20190211215653-8a3ef4a6ac42/go.mod h1:/peI0OaxVYh7fzA72CD7rUsyGVdF7sCiFw7GcYqOcCw=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/namsral/flag v1.7.4-pre h1:b2ScHhoCUkbsq0d2C15Mv+VU8bl8hAXV8arnWiOHNZs=
github.com/namsral/flag v1.7.4-pre/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

38
lib/figlet.go Normal file
View File

@ -0,0 +1,38 @@
package lib
import (
"log"
"os"
"strings"
"math/rand/v2"
"github.com/lukesampson/figlet/figletlib"
)
func Figlet(fontName string, text string) string {
var (
fontsDir = "fonts"
// download figlet fonts from http://www.figlet.org/
font *figletlib.Font
err error
)
if fontName == "random" {
if files, err := os.ReadDir(fontsDir); err != nil {
log.Printf("error reading directory %s: %v\n", fontsDir, err)
return ""
} else {
fontName = files[rand.IntN(len(files))].Name()
}
}
if font, err = figletlib.GetFontByName(fontsDir, fontName); err != nil {
log.Printf("could not find font %s: %v\n", fontName, err)
return ""
}
b := new(strings.Builder)
figletlib.FPrintMsg(b, text, font, 80, font.Settings(), "left")
return b.String()
}

42
main.go Normal file
View File

@ -0,0 +1,42 @@
package main
import (
"fmt"
"log"
"github.com/ekzyis/echo-htmx-templ-tailwindcss/db"
"github.com/ekzyis/echo-htmx-templ-tailwindcss/env"
"github.com/ekzyis/echo-htmx-templ-tailwindcss/server"
)
func main() {
if err := env.Load(); err != nil {
log.Fatalf("error loading env: %v", err)
}
env.Parse()
log.Printf("url: %s", env.PublicUrl)
log.Printf("commit: %s", env.CommitShortSha)
log.Printf("postgres: %s", env.PostgresUrlWithoutPassword)
db, err := db.New(env.PostgresUrl)
if err != nil {
log.Fatal(err)
}
if err := db.Migrate(); err != nil {
log.Fatal(err)
}
s := server.New(server.Context{
Env: env.Env,
PublicURL: env.PublicUrl,
CommitShortSha: env.CommitShortSha,
CommitLongSha: env.CommitLongSha,
Db: db,
})
if err := s.Start(fmt.Sprintf(":%d", env.Port)); err != nil {
log.Fatal(err)
}
}

View File

@ -0,0 +1,9 @@
package components
templ Body() {
<body>
<div id="indicator" class="htmx-indicator fixed top-0"></div>
{ children... }
@Modal(false)
</body>
}

View File

@ -0,0 +1,14 @@
package components
templ Content() {
<div
id="content"
hx-target="#content"
hx-select="#content"
hx-swap="outerHTML"
hx-push-url="true"
hx-indicator="#indicator"
>
{ children... }
</div>
}

View File

@ -0,0 +1,13 @@
package components
import "github.com/ekzyis/echo-htmx-templ-tailwindcss/lib"
templ Figlet(font string, text string) {
<code>
<strong>
<pre>
{ lib.Figlet(font, text) }
</pre>
</strong>
</code>
}

View File

@ -0,0 +1,25 @@
package components
templ Head() {
<head>
<title>template</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico"/>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/>
<link rel="stylesheet" href="/css/tailwind.css"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="theme-color" content="#191d21"/>
<meta
name="htmx-config"
content='{
"responseHandling": [
{ "code": "204", "swap": false },
{ "code": "[23]..", "swap": true },
{ "code": "[45]..", "swap": true, "error": true }
]
}'
/>
<script src="/js/htmx.js" integrity="sha384-AjJ8PRsVaowawSMNJuYhxWbcW/0vOGpORfI4EmV1WnIrAM7a27/CagZ3ZtjfcYh8" crossorigin="anonymous"></script>
</head>
}

View File

@ -0,0 +1,25 @@
package components
templ Modal(show bool) {
<div
id="modal"
class={
"flex justify-center items-center",
"fixed inset-0",
"backdrop-blur-sm bg-black/30",
templ.KV("hidden", !show) }>
<div class="border border-muted rounded">
<div class="relative">
<button id="close" class="absolute top-2 right-2 w-fit text-muted hover:text-reset">X</button>
{ children... }
</div>
</div>
<script type="text/javascript" id="modal-js">
htmx.on("#close", "click", function () {
const modal = htmx.find("#modal")
modal.replaceChildren([])
modal.classList.add("hidden")
})
</script>
</div>
}

23
pages/context/context.go Normal file
View File

@ -0,0 +1,23 @@
package context
import (
"context"
"github.com/ekzyis/echo-htmx-templ-tailwindcss/env"
)
type ContextKey string
var (
Env ContextKey = "env"
Session ContextKey = "session"
Commit ContextKey = "commit"
Origin ContextKey = "origin"
)
func GetOrigin(ctx context.Context) string {
if u, ok := ctx.Value(Origin).(string); ok {
return u
}
return env.PublicUrl
}

19
pages/error.templ Normal file
View File

@ -0,0 +1,19 @@
package pages
import (
"net/http"
"strconv"
"github.com/ekzyis/echo-htmx-templ-tailwindcss/pages/components"
)
templ Error(code int) {
<html>
@components.Head()
@components.Body() {
@components.Content() {
@components.Figlet("random", strconv.Itoa(code))
<div class="font-mono mb-3">{ http.StatusText(code) }</div>
}
}
</html>
}

14
pages/index.templ Normal file
View File

@ -0,0 +1,14 @@
package pages
import "github.com/ekzyis/echo-htmx-templ-tailwindcss/pages/components"
templ Index() {
<html>
@components.Head()
@components.Body() {
@components.Content() {
<div>hello world</div>
}
}
</html>
}

46
pages/render.go Normal file
View File

@ -0,0 +1,46 @@
package pages
import (
"context"
"github.com/a-h/templ"
"github.com/ekzyis/echo-htmx-templ-tailwindcss/env"
"github.com/ekzyis/echo-htmx-templ-tailwindcss/pages/components"
pCtx "github.com/ekzyis/echo-htmx-templ-tailwindcss/pages/context"
"github.com/labstack/echo/v4"
)
func GetEnv(ctx context.Context) string {
if u, ok := ctx.Value(pCtx.Env).(string); ok {
return u
}
return "development"
}
func Render(t templ.Component, statusCode int, eCtx echo.Context) error {
buf := templ.GetBuffer()
defer templ.ReleaseBuffer(buf)
rCtx := context.WithValue(eCtx.Request().Context(), pCtx.Env, env.Env)
req := eCtx.Request()
rCtx = context.WithValue(rCtx, pCtx.Origin, req.Pattern+req.Host)
if err := t.Render(rCtx, buf); err != nil {
return err
}
return eCtx.HTML(statusCode, buf.String())
}
func RenderModal(child templ.Component, statusCode int, eCtx echo.Context) error {
buf := templ.GetBuffer()
defer templ.ReleaseBuffer(buf)
rCtx := templ.WithChildren(eCtx.Request().Context(), child)
if err := components.Modal(true).Render(rCtx, buf); err != nil {
return err
}
return eCtx.HTML(statusCode, buf.String())
}

25
public/css/htmx.css Normal file
View File

@ -0,0 +1,25 @@
.htmx-indicator {
opacity: 0;
height: .5vh;
left: 0;
width: 100%;
background-color: var(--color);
transition: opacity .2s ease-out, width .2s ease-out;
}
.htmx-indicator.htmx-request {
opacity: 1;
/* if I'd be smart, I would use the Content-Length header in some way to determine the duration */
animation: progressbar 1s ease-in-out;
}
@keyframes progressbar {
from {
left: 0;
width: 0;
}
to {
left: 0;
width: 100%;
}
}

5130
public/js/htmx.js Normal file

File diff suppressed because it is too large Load Diff

16
server/context.go Normal file
View File

@ -0,0 +1,16 @@
package server
import (
"context"
"github.com/ekzyis/echo-htmx-templ-tailwindcss/db"
)
type Context struct {
context.Context
Env string
PublicURL string
CommitShortSha string
CommitLongSha string
Db *db.Db
}

14
server/handler.go Normal file
View File

@ -0,0 +1,14 @@
package server
import (
"net/http"
"github.com/ekzyis/echo-htmx-templ-tailwindcss/pages"
"github.com/labstack/echo/v4"
)
func index(sCtx Context) echo.HandlerFunc {
return func(eCtx echo.Context) error {
return pages.Render(pages.Index(), http.StatusOK, eCtx)
}
}

72
server/server.go Normal file
View File

@ -0,0 +1,72 @@
package server
import (
"bytes"
"database/sql"
"net/http"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/ekzyis/echo-htmx-templ-tailwindcss/pages"
)
type Server struct {
*echo.Echo
}
func New(sCtx Context) *Server {
var (
e *echo.Echo
s *Server
)
e = echo.New()
e.Static("/", "public")
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: "${time_custom} ${method} ${uri} ${status}\n",
CustomTimeFormat: "2006-01-02 15:04:05.00000-0700",
}))
e.HTTPErrorHandler = httpErrorHandler(sCtx)
s = &Server{e}
s.GET("/", index(sCtx))
return s
}
func httpErrorHandler(sCtx Context) echo.HTTPErrorHandler {
return func(err error, eCtx echo.Context) {
var (
code = http.StatusInternalServerError
page = pages.Error
buf *bytes.Buffer
)
eCtx.Logger().Error(err)
if err == sql.ErrNoRows {
code = http.StatusNotFound
}
if httpError, ok := err.(*echo.HTTPError); ok {
code = httpError.Code
}
// make sure that HTMX selects and targets correct element
eCtx.Response().Header().Add("HX-Retarget", "#content")
eCtx.Response().Header().Add("HX-Reselect", "#content")
if err = pages.Render(page(code), code, eCtx); err != nil {
eCtx.Logger().Error(err)
code = http.StatusInternalServerError
}
if err = eCtx.HTML(code, buf.String()); err != nil {
eCtx.Logger().Error(err)
}
}
}

3
tailwind.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
content: ["./pages/**/*.templ"],
}

27
tailwind.css Normal file
View File

@ -0,0 +1,27 @@
@import "tailwindcss";
@theme {
/* https://tailwindcss.com/docs/theme */
--color-background: #191d21;
--color-reset: #d3d3d3;
--color-muted: #6c757d;
}
@layer base {
body {
background-color: var(--color-background);
color: var(--color-reset);
}
#content {
min-height: 100svh;
margin: 0 auto;
padding: 0 1rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
}
@layer utilities {}

3
template.env Normal file
View File

@ -0,0 +1,3 @@
PORT=4444
PUBLIC_URL=
POSTGRES_DB=