From bc5e2a4e20994e1f69299c9a941eb67ac9ca7880 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Wed, 6 Aug 2025 04:18:19 +0100 Subject: [PATCH] Icon improvements * Added support for hero icons * Added support for specifying icon colors * Added iconWithClass custom API func * Refactored usage of icons across widget templates --- internal/glance/config-fields.go | 61 +++++++++++++++++++ internal/glance/static/css/utils.css | 33 +++++++++- .../glance/static/css/widget-bookmarks.css | 7 --- .../static/css/widget-docker-containers.css | 26 -------- internal/glance/static/css/widget-monitor.css | 36 ----------- internal/glance/static/css/widgets.css | 2 - internal/glance/templates/bookmarks.html | 4 +- .../glance/templates/docker-containers.html | 12 ++-- .../glance/templates/monitor-compact.html | 4 +- internal/glance/templates/monitor.html | 8 +-- internal/glance/widget-custom-api.go | 4 ++ 11 files changed, 110 insertions(+), 87 deletions(-) delete mode 100644 internal/glance/static/css/widget-docker-containers.css delete mode 100644 internal/glance/static/css/widget-monitor.css diff --git a/internal/glance/config-fields.go b/internal/glance/config-fields.go index 22d7276..41bfa5f 100644 --- a/internal/glance/config-fields.go +++ b/internal/glance/config-fields.go @@ -15,6 +15,7 @@ import ( ) var hslColorFieldPattern = regexp.MustCompile(`^(?:hsla?\()?([\d\.]+)(?: |,)+([\d\.]+)%?(?: |,)+([\d\.]+)%?\)?$`) +var inStringPropertyPattern = regexp.MustCompile(`(?m)([a-zA-Z]+)\[(.*?)\]`) const ( hslHueMax = 360 @@ -133,9 +134,30 @@ func (d *durationField) UnmarshalYAML(node *yaml.Node) error { type customIconField struct { URL template.URL + Color string AutoInvert bool } +func (h *customIconField) Elem() template.HTML { + return h.ElemWithClass("") +} + +func (h *customIconField) ElemWithClass(class string) template.HTML { + if h.AutoInvert && h.Color == "" { + class = "flat-icon " + class + } + + if h.Color != "" { + return template.HTML( + `
`, + ) + } + + return template.HTML( + ``, + ) +} + func newCustomIconField(value string) customIconField { const autoInvertPrefix = "auto-invert " field := customIconField{} @@ -145,6 +167,25 @@ func newCustomIconField(value string) customIconField { value = strings.TrimPrefix(value, autoInvertPrefix) } + value, properties := parseInStringProperties(value) + + if color, ok := properties["color"]; ok { + switch color { + case "primary": + color = "var(--color-primary)" + case "positive": + color = "var(--color-positive)" + case "negative": + color = "var(--color-negative)" + case "base": + color = "var(--color-text-base)" + case "subdue": + color = "var(--color-text-subdue)" + } + + field.Color = color + } + prefix, icon, found := strings.Cut(value, ":") if !found { field.URL = template.URL(value) @@ -172,6 +213,9 @@ func newCustomIconField(value string) customIconField { field.URL = template.URL("https://cdn.jsdelivr.net/npm/@mdi/svg@latest/svg/" + basename + ".svg") case "sh": field.URL = template.URL("https://cdn.jsdelivr.net/gh/selfhst/icons/" + ext + "/" + basename + "." + ext) + case "hi": + field.AutoInvert = true + field.URL = template.URL("https://cdn.jsdelivr.net/npm/heroicons@2.2.0/24/" + basename + ".svg") default: field.URL = template.URL(value) } @@ -189,6 +233,23 @@ func (i *customIconField) UnmarshalYAML(node *yaml.Node) error { return nil } +func parseInStringProperties(value string) (string, map[string]string) { + properties := make(map[string]string) + + value = inStringPropertyPattern.ReplaceAllStringFunc(value, func(match string) string { + matches := inStringPropertyPattern.FindStringSubmatch(match) + if len(matches) != 3 { + return "" + } + + properties[matches[1]] = matches[2] + + return "" + }) + + return strings.TrimSpace(value), properties +} + type proxyOptionsField struct { URL string `yaml:"url"` AllowInsecure bool `yaml:"allow-insecure"` diff --git a/internal/glance/static/css/utils.css b/internal/glance/static/css/utils.css index 3bbb48c..75c75a0 100644 --- a/internal/glance/static/css/utils.css +++ b/internal/glance/static/css/utils.css @@ -376,7 +376,27 @@ details[open] .summary::after { gap: 0.5rem; } -:root:not([data-scheme=light]) .flat-icon { +.icon { + object-fit: contain; +} + +.icon:not(.colored-icon) { + opacity: 0.9; + filter: grayscale(0.2); + transition: opacity 0.2s, filter 0.2s; +} + +.icon-parent:hover .icon { + opacity: 1; + filter: none; +} + +.colored-icon { + mask: var(--icon-url) no-repeat center / contain; + background-color: var(--icon-color); +} + +:root:not([data-scheme=light]) .flat-icon:not(.colored-icon) { filter: invert(1); } @@ -459,7 +479,6 @@ details[open] .summary::after { filter: none; } - .hide-scrollbars { scrollbar-width: none; } @@ -547,6 +566,13 @@ details[open] .summary::after { visibility: hidden; } +.square-18 { width: 1.8rem; height: 1.8rem; } +.square-20 { width: 2rem; height: 2rem; } +.square-27 { width: 2.7rem; height: 2.7rem; } +.square-30 { width: 3rem; height: 3rem; } +.square-32 { width: 3.2rem; height: 3.2rem; } +.square-40 { width: 4rem; height: 4rem; } + .cursor-help { cursor: help; } .rounded { border-radius: var(--border-radius); } .break-all { word-break: break-all; } @@ -588,8 +614,11 @@ details[open] .summary::after { .gap-15 { gap: 1.5rem; } .gap-20 { gap: 2rem; } .gap-25 { gap: 2.5rem; } +.gap-30 { gap: 3rem; } .gap-35 { gap: 3.5rem; } +.gap-40 { gap: 4rem; } .gap-45 { gap: 4.5rem; } +.gap-50 { gap: 5rem; } .gap-55 { gap: 5.5rem; } .margin-left-auto { margin-left: auto; } .margin-top-3 { margin-top: 0.3rem; } diff --git a/internal/glance/static/css/widget-bookmarks.css b/internal/glance/static/css/widget-bookmarks.css index 7f2dabd..0e205fe 100644 --- a/internal/glance/static/css/widget-bookmarks.css +++ b/internal/glance/static/css/widget-bookmarks.css @@ -20,12 +20,5 @@ background-color: var(--color-widget-background-highlight); border-radius: var(--border-radius); padding: 0.5rem; - opacity: 0.7; flex-shrink: 0; } - -.bookmarks-icon { - width: 20px; - height: 20px; - opacity: 0.8; -} diff --git a/internal/glance/static/css/widget-docker-containers.css b/internal/glance/static/css/widget-docker-containers.css deleted file mode 100644 index ae08788..0000000 --- a/internal/glance/static/css/widget-docker-containers.css +++ /dev/null @@ -1,26 +0,0 @@ -.docker-container-icon { - display: block; - filter: grayscale(0.4); - object-fit: contain; - aspect-ratio: 1 / 1; - width: 2.7rem; - opacity: 0.8; - transition: filter 0.3s, opacity 0.3s; -} - -.docker-container-icon.flat-icon { - opacity: 0.7; -} - -.docker-container:hover .docker-container-icon { - opacity: 1; -} - -.docker-container:hover .docker-container-icon:not(.flat-icon) { - filter: grayscale(0); -} - -.docker-container-status-icon { - width: 2rem; - height: 2rem; -} diff --git a/internal/glance/static/css/widget-monitor.css b/internal/glance/static/css/widget-monitor.css deleted file mode 100644 index 8bc629b..0000000 --- a/internal/glance/static/css/widget-monitor.css +++ /dev/null @@ -1,36 +0,0 @@ -.monitor-site-icon { - display: block; - opacity: 0.8; - filter: grayscale(0.4); - object-fit: contain; - aspect-ratio: 1 / 1; - width: 3.2rem; - position: relative; - top: -0.1rem; - transition: filter 0.3s, opacity 0.3s; -} - -.monitor-site-icon.flat-icon { - opacity: 0.7; -} - -.monitor-site:hover .monitor-site-icon { - opacity: 1; -} - -.monitor-site:hover .monitor-site-icon:not(.flat-icon) { - filter: grayscale(0); -} - -.monitor-site-status-icon { - flex-shrink: 0; - margin-left: auto; - width: 2rem; - height: 2rem; -} - -.monitor-site-status-icon-compact { - width: 1.8rem; - height: 1.8rem; - flex-shrink: 0; -} diff --git a/internal/glance/static/css/widgets.css b/internal/glance/static/css/widgets.css index 07b41c8..59af852 100644 --- a/internal/glance/static/css/widgets.css +++ b/internal/glance/static/css/widgets.css @@ -2,10 +2,8 @@ @import "widget-calendar.css"; @import "widget-clock.css"; @import "widget-dns-stats.css"; -@import "widget-docker-containers.css"; @import "widget-group.css"; @import "widget-markets.css"; -@import "widget-monitor.css"; @import "widget-reddit.css"; @import "widget-releases.css"; @import "widget-rss.css"; diff --git a/internal/glance/templates/bookmarks.html b/internal/glance/templates/bookmarks.html index a1c9c51..43821ed 100644 --- a/internal/glance/templates/bookmarks.html +++ b/internal/glance/templates/bookmarks.html @@ -10,10 +10,10 @@