From be32011cfaeb0a56bfadb875e2b1bb30ec9aee64 Mon Sep 17 00:00:00 2001 From: Christian Ditaputratama Date: Tue, 29 Oct 2024 20:41:22 +0700 Subject: [PATCH] feat!: templ + htmx build system --- .air.toml | 6 +- .gitignore | 2 + Makefile | 39 +++- README.md | 15 +- bun.lockb | Bin 0 -> 44655 bytes cmd/server/serve.go | 3 + frontend/README.md | 8 +- go.mod | 3 +- go.sum | 8 +- internal/handler/response.go | 22 ++ internal/handler/routes.go | 1 + internal/handler/views/embed.go | 20 ++ internal/handler/views/home.templ | 4 + internal/handler/views/home_templ.go | 36 +++ internal/handler/views/layout.templ | 64 ++++++ internal/handler/views/layout_templ.go | 284 ++++++++++++++++++++++++ internal/handler/views/src/css/main.css | 3 + package.json | 9 + tailwind.config.js | 17 ++ 19 files changed, 527 insertions(+), 17 deletions(-) create mode 100755 bun.lockb create mode 100644 internal/handler/views/embed.go create mode 100644 internal/handler/views/home.templ create mode 100644 internal/handler/views/home_templ.go create mode 100644 internal/handler/views/layout.templ create mode 100644 internal/handler/views/layout_templ.go create mode 100644 internal/handler/views/src/css/main.css create mode 100644 package.json create mode 100644 tailwind.config.js diff --git a/.air.toml b/.air.toml index 85efed1..2966485 100644 --- a/.air.toml +++ b/.air.toml @@ -7,14 +7,14 @@ tmp_dir = "tmp" bin = "./tmp/main" cmd = "make dev" delay = 0 - exclude_dir = ["assets", "tmp", "testdata", "frontend/node_modules", "data", "bin"] + exclude_dir = ["assets", "tmp", "testdata", "node_modules", "data", "bin", "internal/handler/views/assets"] exclude_file = [] - exclude_regex = ["_test.go"] + exclude_regex = ["_test.go", ".*_templ.go"] exclude_unchanged = false follow_symlink = false full_bin = "" include_dir = [] - include_ext = ["go", "tpl", "tmpl", "html", "css", "js"] + include_ext = ["go", "templ", "html", "css", "js"] include_file = [] kill_delay = "0s" log = "build-errors.log" diff --git a/.gitignore b/.gitignore index df7bc84..4f8f074 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ /node_modules /tmp /assets/geoip +/internal/handler/views/assets/css/**/* +/internal/handler/views/assets/js/**/* diff --git a/Makefile b/Makefile index 5333259..f4ee109 100644 --- a/Makefile +++ b/Makefile @@ -31,16 +31,12 @@ BUILD_LDFLAGS := -s -w -X github.com/ditatompel/xmr-remote-nodes/internal/config # This called from air cmd (see .air.toml) .PHONY: dev -dev: +dev: templ tailwind go build -ldflags="$(BUILD_LDFLAGS)" -tags server -o ./tmp/main . .PHONY: build build: client server -.PHONY: ui -ui: - go generate ./... - .PHONY: client client: CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build \ @@ -51,7 +47,7 @@ client: -o bin/${BINARY_NAME}-client-linux-arm64 .PHONY: server -server: ui +server: prepare templ tailwind CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build \ -ldflags="$(BUILD_LDFLAGS)" -tags server \ -o bin/${BINARY_NAME}-server-linux-amd64 @@ -59,11 +55,34 @@ server: ui -ldflags="$(BUILD_LDFLAGS)" -tags server \ -o bin/${BINARY_NAME}-server-linux-arm64 +.PHONY: prepare +prepare: + bun install --frozen-lockfile + @mkdir -p ./internal/handler/views/assets/js + cp ./node_modules/htmx.org/dist/htmx.min.js ./internal/handler/views/assets/js + +# Compile template +.PHONY: templ +templ: + @echo "Compiling Templ template..." + templ generate + +.PHONY: tailwind +tailwind: + mkdir -p ./internal/handler/views/assets/css + @echo "Compiling TailwindCSS..." + bun tailwindcss -i ./internal/handler/views/src/css/main.css \ + -o ./internal/handler/views/assets/css/main.min.css \ + -c ./tailwind.config.js \ + --minify + .PHONY: clean clean: go clean rm -rfv ./bin - rm -rf ./frontend/build + rm -rfv ./tmp/main + rm -rf ./internal/handler/views/*_templ.go + rm -rf ./internal/handler/views/assets/css/ .PHONY: lint lint: @@ -82,8 +101,10 @@ bench: # And make sure the inventory and deploy-*.yml file is properly configured. .PHONY: deploy-server deploy-server: - ansible-playbook -i ./deployment/ansible/inventory.ini -l server ./deployment/ansible/deploy-server.yml -K + ansible-playbook -i ./deployment/ansible/inventory.ini \ + -l server ./deployment/ansible/deploy-server.yml -K .PHONY: deploy-prober deploy-prober: - ansible-playbook -i ./deployment/ansible/inventory.ini -l prober ./deployment/ansible/deploy-prober.yml -K + ansible-playbook -i ./deployment/ansible/inventory.ini \ + -l prober ./deployment/ansible/deploy-prober.yml -K diff --git a/README.md b/README.md index d6e4ec6..81d72a5 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,15 @@ serves the `/api` endpoint that is used by the clients and the Web UI itself. To build the executable binaries, you need: - Go >= 1.22 -- NodeJS >= 20 +- Bun >= 1.1.26 +- templ v0.2.778 + +> **Note**: +> +> - If you want to contribute to the code, please use exact templ version +> (v0.2.778). +> - The UI is using Preline UI that uses [Lucide Icons][lucide-icons], use +> that for SVG icons. ### Server & Prober requirements @@ -67,6 +75,10 @@ Systemd example: [xmr-nodes-prober.service][prober-systemd-service] and ## Development and Deployment +1. Clone or fork this repository. +2. Prepare the assets: `make prepare`, +3. Run `air serve` (live reload using [air-verse/air][air-repo]). + See the [Makefile](./Makefile). ## ToDo's @@ -113,6 +125,7 @@ This project is licensed under [GLWTPL](./LICENSE). [server-systemd-service]: ./deployment/init/xmr-nodes-server.service "systemd service example for server" [prober-systemd-service]: ./deployment/init/xmr-nodes-prober.service "systemd service example for prober" [prober-systemd-timer]: ./deployment/init/xmr-nodes-prober.timer "systemd timer example for prober" +[air-repo]: https://github.com/air-verse/air "Air - Live reload for Go apps" [jtgrassie-monero-pool]: https://github.com/jtgrassie/monero-pool "A Monero mining pool server written in C" [rclone]: https://github.com/rclone/rclone "rclone GitHub repository" [monerofail-repo]: https://github.com/lalanza808/monero.fail "Lalanza808's monero.fail GitHub repository" diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..0178cf30ac99cfc385bf4b2953e39a07581ea45b GIT binary patch literal 44655 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 literal 0 HcmV?d00001 diff --git a/cmd/server/serve.go b/cmd/server/serve.go index 34e0b1c..645fc23 100644 --- a/cmd/server/serve.go +++ b/cmd/server/serve.go @@ -13,6 +13,7 @@ import ( "github.com/ditatompel/xmr-remote-nodes/internal/cron" "github.com/ditatompel/xmr-remote-nodes/internal/database" "github.com/ditatompel/xmr-remote-nodes/internal/handler" + "github.com/ditatompel/xmr-remote-nodes/internal/handler/views" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" @@ -79,6 +80,8 @@ func serve() { AllowCredentials: true, })) + app.Use("/assets", views.EmbedAssets()) + handler.V1Api(app) app.Use("/", filesystem.New(filesystem.Config{ Root: frontend.SvelteKitHandler(), diff --git a/frontend/README.md b/frontend/README.md index 0e4ddb5..cf6b32b 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,3 +1,9 @@ # UI -The UI is generated and embedded when the Go project is built. See [./frontend/embed.go](https://github.com/ditatompel/xmr-remote-nodes/blob/main/frontend/embed.go#L10-L13). +The UI is generated and embedded when the Go project is built. See +[./frontend/embed.go](https://github.com/ditatompel/xmr-remote-nodes/blob/main/frontend/embed.go#L10-L13). + +> **NOTE**: +> +> Since this project will not using Svelte anymore, anything under this +> directory will soon be removed. diff --git a/go.mod b/go.mod index 62ce0c7..5795639 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/ditatompel/xmr-remote-nodes go 1.22.2 require ( + github.com/a-h/templ v0.2.778 github.com/go-sql-driver/mysql v1.8.1 github.com/gofiber/fiber/v2 v2.52.5 github.com/google/uuid v1.6.0 @@ -15,7 +16,7 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect - github.com/andybalholm/brotli v1.0.5 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.17.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index d3b5266..b63f146 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,9 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/a-h/templ v0.2.778 h1:VzhOuvWECrwOec4790lcLlZpP4Iptt5Q4K9aFxQmtaM= +github.com/a-h/templ v0.2.778/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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= @@ -9,6 +11,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +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/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= diff --git a/internal/handler/response.go b/internal/handler/response.go index 4c1e954..2c4c124 100644 --- a/internal/handler/response.go +++ b/internal/handler/response.go @@ -1,13 +1,35 @@ package handler import ( + "fmt" "strconv" + "github.com/a-h/templ" + "github.com/ditatompel/xmr-remote-nodes/internal/handler/views" "github.com/ditatompel/xmr-remote-nodes/internal/monero" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/adaptor" ) +// Render Home Page +func homeHandler(c *fiber.Ctx) error { + p := views.Meta{ + Title: "Monero Remote Node", + Description: "A website that helps you monitor your favourite Monero remote nodes, but YOU BETTER RUN AND USE YOUR OWN NODE.", + Keywords: "monero,monero,xmr,monero node,xmrnode,cryptocurrency,monero remote node,monero testnet,monero stagenet", + Robots: "INDEX,FOLLOW", + Permalink: "https://xmr.ditatompel.com", + Identifier: "/", + } + + c.Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, p.Permalink)) + home := views.BaseLayout(p, views.Home()) + handler := adaptor.HTTPHandler(templ.Handler(home)) + + return handler(c) +} + // Returns a single node information based on `id` query param func Node(c *fiber.Ctx) error { nodeId, err := c.ParamsInt("id", 0) diff --git a/internal/handler/routes.go b/internal/handler/routes.go index 3535ea6..ff84ef3 100644 --- a/internal/handler/routes.go +++ b/internal/handler/routes.go @@ -6,6 +6,7 @@ import ( // V1 API routes func V1Api(app *fiber.App) { + app.Get("/", homeHandler) v1 := app.Group("/api/v1") // these routes are public, they don't require a prober api key diff --git a/internal/handler/views/embed.go b/internal/handler/views/embed.go new file mode 100644 index 0000000..8030e1e --- /dev/null +++ b/internal/handler/views/embed.go @@ -0,0 +1,20 @@ +package views + +import ( + "embed" + "net/http" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/filesystem" +) + +//go:embed assets/* +var embedStatic embed.FS + +func EmbedAssets() fiber.Handler { + return filesystem.New(filesystem.Config{ + Root: http.FS(embedStatic), + PathPrefix: "assets", + Browse: false, + }) +} diff --git a/internal/handler/views/home.templ b/internal/handler/views/home.templ new file mode 100644 index 0000000..6e7a6f7 --- /dev/null +++ b/internal/handler/views/home.templ @@ -0,0 +1,4 @@ +package views + +templ Home() { +} diff --git a/internal/handler/views/home_templ.go b/internal/handler/views/home_templ.go new file mode 100644 index 0000000..44d399d --- /dev/null +++ b/internal/handler/views/home_templ.go @@ -0,0 +1,36 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.778 +package views + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +func Home() templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/internal/handler/views/layout.templ b/internal/handler/views/layout.templ new file mode 100644 index 0000000..c78cb3f --- /dev/null +++ b/internal/handler/views/layout.templ @@ -0,0 +1,64 @@ +package views + +import ( + "fmt" + "github.com/ditatompel/xmr-remote-nodes/internal/config" + "time" +) + +var buildTime = time.Now().Unix() + +type Meta struct { + Title string + Description string + Keywords string + Robots string + Permalink string + Identifier string +} + +templ base(m Meta) { + + + + + { m.Title } — xmr.ditatompel.com + + + + + + + + + + + + + + + + + + +
+ { children... } +
+
+
+

XMR Nodes { config.Version }, source code licensed under GLWTPL.

+
+
+ + +} + +templ BaseLayout(m Meta, cmp templ.Component) { + @base(m) { + @cmp + } +} + +templ BlankLayout(cmp templ.Component) { + @cmp +} diff --git a/internal/handler/views/layout_templ.go b/internal/handler/views/layout_templ.go new file mode 100644 index 0000000..5dec309 --- /dev/null +++ b/internal/handler/views/layout_templ.go @@ -0,0 +1,284 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.2.778 +package views + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + "fmt" + "github.com/ditatompel/xmr-remote-nodes/internal/config" + "time" +) + +var buildTime = time.Now().Unix() + +type Meta struct { + Title string + Description string + Keywords string + Robots string + Permalink string + Identifier string +} + +func base(m Meta) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var2 string + templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(m.Title) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 25, Col: 19} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" — xmr.ditatompel.com
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func BaseLayout(m Meta, cmp templ.Component) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var13 := templ.GetChildren(ctx) + if templ_7745c5c3_Var13 == nil { + templ_7745c5c3_Var13 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var14 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = cmp.Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) + templ_7745c5c3_Err = base(m).Render(templ.WithChildren(ctx, templ_7745c5c3_Var14), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func BlankLayout(cmp templ.Component) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var15 := templ.GetChildren(ctx) + if templ_7745c5c3_Var15 == nil { + templ_7745c5c3_Var15 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Err = cmp.Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +var _ = templruntime.GeneratedTemplate diff --git a/internal/handler/views/src/css/main.css b/internal/handler/views/src/css/main.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/internal/handler/views/src/css/main.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/package.json b/package.json new file mode 100644 index 0000000..baf6565 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "@tailwindcss/forms": "^0.5.9", + "@tailwindcss/typography": "^0.5.15", + "htmx.org": "^1.9.12", + "preline": "^2.5.1", + "tailwindcss": "^3.4.14" + } +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..b12eb8f --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,17 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./internal/handler/views/*.templ", + "node_modules/preline/dist/*.js", + ], + // enable dark mode via class strategy + // darkMode: "class", + theme: { + extend: {}, + }, + plugins: [ + require("@tailwindcss/typography"), + require("@tailwindcss/forms"), + require("preline/plugin"), + ], +};