From 8843b2a2d9a8d4396830a97ec787a26e8201cdb4 Mon Sep 17 00:00:00 2001 From: dzonerzy Date: Sun, 5 May 2024 03:39:18 +0200 Subject: [PATCH] added custom css --- README.md | 22 ++++ internal/config/config_test.go | 25 +++++ internal/config/validate.go | 12 +++ internal/engine/engine.go | 36 ++++++- internal/engine/static/styles/style.css | 122 ++++++++++++++++++++++ internal/engine/template/index.html | 128 +----------------------- internal/theme/theme.go | 1 + 7 files changed, 216 insertions(+), 130 deletions(-) create mode 100644 internal/engine/static/styles/style.css diff --git a/README.md b/README.md index 2f17a42..3e8a988 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,28 @@ Example of a dark mode theme: } ``` +

+It is also possible to use a custom css file by providing the path to the file: +

+ +```json +"theme": { + "custom_css": "/path/to/custom.css" +} +``` + +

+Keep in mind, even if you provide a custom css file, the background and foreground colors will still be applied as long as you provide the correct templating keys in your custom css file like so: +

+ +```css +body { + background: {{.Background}}; + color: {{.Foreground}}; +} +``` + + #### YAML ```yml diff --git a/internal/config/config_test.go b/internal/config/config_test.go index a743ede..5520686 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -171,3 +171,28 @@ func TestValidate(t *testing.T) { t.Fatal() } } + +func TestCustomCSS(t *testing.T) { + var config = `{ + "addr": "0.0.0.0:8080", + "use_tls": false, + "cert_file": "", + "key_file": "", + "behind_proxy": false, + "title": "Easy Gate", + "theme": { + "custom_css": "tmp.css", + }, + "groups": [], + "services": [], + "notes": [] + }` + cfg, err := Unmarshal([]byte(config)) + if err != nil { + t.Fatal(err) + } + + if cfg.Theme.CustomCss != "tmp.css" { + t.Fatal() + } +} diff --git a/internal/config/validate.go b/internal/config/validate.go index d3057bb..db2903a 100644 --- a/internal/config/validate.go +++ b/internal/config/validate.go @@ -2,6 +2,7 @@ package config import ( "fmt" + "os" "regexp" ) @@ -40,6 +41,17 @@ func validateConfig(cfg *Config) error { return fmt.Errorf("invalid foreground color") } + if cfg.Theme.CustomCss != "" { + fi, err := os.Stat(cfg.Theme.CustomCss) + if err != nil { + return fmt.Errorf("invalid custom CSS file path") + } + + if fi.IsDir() { + return fmt.Errorf("custom CSS path is a directory") + } + } + for _, service := range cfg.Services { if !isURL(service.URL) { return fmt.Errorf("invalid URL for service %s", service.Name) diff --git a/internal/engine/engine.go b/internal/engine/engine.go index 700d2ae..088dd48 100644 --- a/internal/engine/engine.go +++ b/internal/engine/engine.go @@ -2,8 +2,11 @@ package engine import ( "embed" + "fmt" + "html/template" "log" "net/http" + "os" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" @@ -33,6 +36,17 @@ func (e Engine) Serve() { status, _ := e.Routine.GetStatus() htmlEngine := html.NewFileSystem(http.FS(templateFS), ".html") + + var styleData []byte + + if status.Theme.CustomCss != "" { + data, _ := os.ReadFile(status.Theme.CustomCss) + styleData = data + } else { + data, _ := staticFS.ReadFile("static/styles/style.css") + styleData = data + } + app := fiber.New(fiber.Config{ Views: htmlEngine, DisableStartupMessage: true, @@ -63,6 +77,20 @@ func (e Engine) Serve() { return c.Send(data) }) + app.Get("/style.css", func(c *fiber.Ctx) error { + + tmpl, err := template.New("").Parse(string(styleData)) + if err != nil { + return c.SendStatus(http.StatusInternalServerError) + } + + c.Set("Content-type", "text/css") + return tmpl.Execute(c, fiber.Map{ + "Background": status.Theme.Background, + "Foreground": status.Theme.Foreground, + }) + }) + app.Get("/", func(c *fiber.Ctx) error { status, err := e.Routine.GetStatus() if err != nil { @@ -73,11 +101,11 @@ func (e Engine) Serve() { addr := getAddr(status, c) data := getData(status, addr) + fmt.Println(data) + return c.Render("template/index", fiber.Map{ - "Title": status.Title, - "Background": status.Theme.Background, - "Foreground": status.Theme.Foreground, - "Data": data, + "Title": status.Title, + "Data": data, }) }) diff --git a/internal/engine/static/styles/style.css b/internal/engine/static/styles/style.css new file mode 100644 index 0000000..cbab309 --- /dev/null +++ b/internal/engine/static/styles/style.css @@ -0,0 +1,122 @@ +@font-face { + font-family: 'Roboto'; + src: url('/roboto-regular.ttf') format('truetype'); + } + + body { + background-color: {{.Background}}; + color: {{.Foreground}}; + font-family: 'Roboto', + sans-serif; + } + + a { + text-decoration: none; + color: inherit; + } + + .main { + padding-top: 1.5rem; + padding-bottom: 1.5rem; + padding-left: 3rem; + padding-right: 3rem; + } + + .category-title { + margin-left: 0.25rem; + } + + .category-block { + display: grid; + grid-template-columns: repeat(1, minmax(0, 1fr)); + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + .service-block { + padding: 0.3rem; + border-radius: 0.5rem; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: box-shadow 0.2s ease; + margin: 0.25rem; + } + + .service-block:hover { + box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.2); + } + + .service-icon { + fill: {{.Foreground}}; + margin: auto; + } + + .service-title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 600; + width: 83.33%; + } + + .notes-block { + margin-top: 3rem; + margin-bottom: 0.5rem; + display: grid; + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .note-block { + padding: 1rem; + border-radius: 0.25rem; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); + margin: 0.25rem; + } + + .note-icon { + fill: {{.Foreground}}; + margin-right: 0.5rem; + } + + .note-title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 600; + width: 83.33%; + } + + .flex-center { + align-items: center; + display: flex; + } + + @media (min-width: 768px) { + .category-block { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .notes-block { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + } + + @media (min-width: 1024px) { + .category-block { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .notes-block { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + } + + @media (min-width: 1280px) { + .category-block { + grid-template-columns: repeat(7, minmax(0, 1fr)); + } + + .notes-block { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + } \ No newline at end of file diff --git a/internal/engine/template/index.html b/internal/engine/template/index.html index cfbffae..63dbf6d 100644 --- a/internal/engine/template/index.html +++ b/internal/engine/template/index.html @@ -6,130 +6,7 @@ {{.Title}} - + @@ -167,8 +44,7 @@

{{$category}}

{{range $index, $note := .Data.Notes}}
- + diff --git a/internal/theme/theme.go b/internal/theme/theme.go index 299ba1d..66fbd31 100644 --- a/internal/theme/theme.go +++ b/internal/theme/theme.go @@ -4,4 +4,5 @@ package theme type Theme struct { Background string `json:"background" yaml:"background"` Foreground string `json:"foreground" yaml:"foreground"` + CustomCss string `json:"custom_css" yaml:"custom_css"` }