From 2aaff02db8ff11e91b1b5e07877bc0f5ed3670e6 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 20 May 2025 16:46:06 +0100 Subject: [PATCH 01/10] Fix for extra whitespace in titles --- internal/glance/static/js/page.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/glance/static/js/page.js b/internal/glance/static/js/page.js index 56e9c2e..cc2a649 100644 --- a/internal/glance/static/js/page.js +++ b/internal/glance/static/js/page.js @@ -661,7 +661,8 @@ function setupTruncatedElementTitles() { for (let i = 0; i < elements.length; i++) { const element = elements[i]; - if (element.getAttribute("title") === null) element.title = element.innerText; + if (element.getAttribute("title") === null) + element.title = element.innerText.trim().replace(/\s{2,}/, " "); } } From ab093cb2328a9ae9159713a760528f320ebc0e91 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Wed, 21 May 2025 09:41:36 +0100 Subject: [PATCH 02/10] Fix extra whitespace in titles (again) --- internal/glance/static/js/page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/glance/static/js/page.js b/internal/glance/static/js/page.js index cc2a649..10a289d 100644 --- a/internal/glance/static/js/page.js +++ b/internal/glance/static/js/page.js @@ -662,7 +662,7 @@ function setupTruncatedElementTitles() { for (let i = 0; i < elements.length; i++) { const element = elements[i]; if (element.getAttribute("title") === null) - element.title = element.innerText.trim().replace(/\s{2,}/, " "); + element.title = element.innerText.trim().replace(/\s+/g, " "); } } From e52374fa247138f8c0e1929f3a554c947f567d31 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Sat, 24 May 2025 14:58:19 +0100 Subject: [PATCH 03/10] Fix popover triangle misalignment --- internal/glance/static/js/popover.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/glance/static/js/popover.js b/internal/glance/static/js/popover.js index 98b1051..bada463 100644 --- a/internal/glance/static/js/popover.js +++ b/internal/glance/static/js/popover.js @@ -130,7 +130,7 @@ function repositionContainer() { } else if (left + containerBounds.width > window.innerWidth) { containerElement.style.removeProperty("left"); containerElement.style.right = 0; - containerElement.style.setProperty("--triangle-offset", containerBounds.width - containerInlinePadding - (window.innerWidth - targetBounds.left - targetBoundsWidthOffset) + -1 + "px"); + containerElement.style.setProperty("--triangle-offset", containerBounds.width - containerInlinePadding - (document.documentElement.clientWidth - targetBounds.left - targetBoundsWidthOffset) + -1 + "px"); } else { containerElement.style.removeProperty("right"); containerElement.style.left = left + "px"; From 5a093f42b0a5d865dc83f9bc0408b2ff8addb104 Mon Sep 17 00:00:00 2001 From: Ramzi H Date: Tue, 27 May 2025 12:54:22 +0200 Subject: [PATCH 04/10] setupTodos using incorrect logic and skipping to-do item. --- internal/glance/static/js/page.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/glance/static/js/page.js b/internal/glance/static/js/page.js index 10a289d..e0ca000 100644 --- a/internal/glance/static/js/page.js +++ b/internal/glance/static/js/page.js @@ -643,13 +643,14 @@ async function setupCalendars() { } async function setupTodos() { - const elems = document.getElementsByClassName("todo"); + var elems = Array.prototype.slice.call(document.getElementsByClassName("todo")); if (elems.length == 0) return; const todo = await import ('./todo.js'); - for (let i = 0; i < elems.length; i++) + for (let i = 0; i < elems.length; i++){ todo.default(elems[i]); + } } function setupTruncatedElementTitles() { From d103c81df19421b170961bfc390e66d23c5da073 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 10 Jun 2025 07:02:38 +0100 Subject: [PATCH 05/10] Fix search shortcut #719 --- internal/glance/static/js/page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/glance/static/js/page.js b/internal/glance/static/js/page.js index e0ca000..b921797 100644 --- a/internal/glance/static/js/page.js +++ b/internal/glance/static/js/page.js @@ -194,7 +194,7 @@ function setupSearchBoxes() { document.addEventListener("keydown", (event) => { if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) return; - if (event.key != "s") return; + if (event.code != "KeyS") return; inputElement.focus(); event.preventDefault(); From 808f3c14368f65c4d58adc3f82903d3d88aafd5d Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 10 Jun 2025 07:11:25 +0100 Subject: [PATCH 06/10] Update page.js --- internal/glance/static/js/page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/glance/static/js/page.js b/internal/glance/static/js/page.js index b921797..0212a4f 100644 --- a/internal/glance/static/js/page.js +++ b/internal/glance/static/js/page.js @@ -643,7 +643,7 @@ async function setupCalendars() { } async function setupTodos() { - var elems = Array.prototype.slice.call(document.getElementsByClassName("todo")); + const elems = Array.from(document.getElementsByClassName("todo")); if (elems.length == 0) return; const todo = await import ('./todo.js'); From 88d8fa56fb613e0b890d9de4820c57b8b13b0f3e Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 10 Jun 2025 07:43:34 +0100 Subject: [PATCH 07/10] Make auto-invert work with prefixed icons --- internal/glance/config-fields.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/glance/config-fields.go b/internal/glance/config-fields.go index d368140..1527c72 100644 --- a/internal/glance/config-fields.go +++ b/internal/glance/config-fields.go @@ -141,13 +141,13 @@ func newCustomIconField(value string) customIconField { const autoInvertPrefix = "auto-invert " field := customIconField{} + if strings.HasPrefix(value, autoInvertPrefix) { + field.IsFlatIcon = true + value = strings.TrimPrefix(value, autoInvertPrefix) + } + prefix, icon, found := strings.Cut(value, ":") if !found { - if strings.HasPrefix(value, autoInvertPrefix) { - field.IsFlatIcon = true - value = strings.TrimPrefix(value, autoInvertPrefix) - } - field.URL = template.URL(value) return field } From de9a192ba4806e61aad25381fb687b69fcabf34a Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 10 Jun 2025 07:45:58 +0100 Subject: [PATCH 08/10] Remove healthcheck #676 --- Dockerfile | 3 --- Dockerfile.goreleaser | 3 --- 2 files changed, 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0c4cc63..bb9f260 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,8 +9,5 @@ FROM alpine:3.21 WORKDIR /app COPY --from=builder /app/glance . -HEALTHCHECK --timeout=10s --start-period=60s --interval=60s \ - CMD wget --spider -q http://localhost:8080/api/healthz - EXPOSE 8080/tcp ENTRYPOINT ["/app/glance", "--config", "/app/config/glance.yml"] diff --git a/Dockerfile.goreleaser b/Dockerfile.goreleaser index bbfa8ad..244b177 100644 --- a/Dockerfile.goreleaser +++ b/Dockerfile.goreleaser @@ -3,8 +3,5 @@ FROM alpine:3.21 WORKDIR /app COPY glance . -HEALTHCHECK --timeout=10s --start-period=60s --interval=60s \ - CMD wget --spider -q http://localhost:8080/api/healthz - EXPOSE 8080/tcp ENTRYPOINT ["/app/glance", "--config", "/app/config/glance.yml"] From 8f986f1403b886fd56b921541792a52909a30722 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 10 Jun 2025 08:17:38 +0100 Subject: [PATCH 09/10] Refactor icons field and add mdi --- docs/configuration.md | 51 ++++++++++++++++++-------------- internal/glance/config-fields.go | 40 +++++++++++-------------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 43fb192..8b975b0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -6,6 +6,7 @@ - [Environment variables](#environment-variables) - [Other ways of providing tokens/passwords/secrets](#other-ways-of-providing-tokenspasswordssecrets) - [Including other config files](#including-other-config-files) + - [Icons](#icons) - [Config schema](#config-schema) - [Authentication](#authentication) - [Server](#server) @@ -185,6 +186,30 @@ docker run --rm -v ./glance.yml:/app/config/glance.yml glanceapp/glance config:p This assumes that the config you want to print is in your current working directory and is named `glance.yml`. +## Icons + +For widgets which provide you with the ability to specify icons such as the monitor, bookmarks, docker containers, etc, you can use the `icon` property to specify a URL to an image. Alternatively, you can also use icon names from multiple libraries via prefixes: + +```yml +icon: si:immich # si for Simple icons https://simpleicons.org/ +icon: sh:immich # sh for selfh.st icons https://selfh.st/icons/ +icon: di:immich # di for Dashboard icons https://github.com/homarr-labs/dashboard-icons +icon: mdi:camera # mdi for Material Design icons https://pictogrammers.com/library/mdi/ +``` + +> [!NOTE] +> +> The icons are loaded externally and are hosted on `cdn.jsdelivr.net`, if you do not wish to depend on a 3rd party you are free to download the icons individually and host them locally. + +Icons from the Simple icons library as well as Material Design icons will automatically invert their color to match your light or dark theme, however you may want to enable this manually for other icons. To do this, you can use the `auto-invert` prefix: + +```yaml +icon: auto-invert https://example.com/path/to/icon.png # with a URL +icon: auto-invert sh:glance-dark # with a selfh.st icon +``` + +This expects the icon to be black and will automatically invert it to white when using a dark theme. + ## Config schema For property descriptions, validation and autocompletion of the config within your IDE, @not-first has kindly created a [schema](https://github.com/not-first/glance-schema). Massive thanks to them for this, go check it out and give them a star! @@ -1957,17 +1982,7 @@ If the monitored service returns an error, the user will be redirected here. If `icon` -Optional URL to an image which will be used as the icon for the site. Can be an external URL or internal via [server configured assets](#assets-path). You can also directly use [Simple Icons](https://simpleicons.org/) via a `si:` prefix or [Dashboard Icons](https://github.com/walkxcode/dashboard-icons) via a `di:` prefix: - -```yaml -icon: si:jellyfin -icon: si:gitea -icon: si:adguard -``` - -> [!WARNING] -> -> Simple Icons are loaded externally and are hosted on `cdn.jsdelivr.net`, if you do not wish to depend on a 3rd party you are free to download the icons individually and host them locally. +See [Icons](#icons) for more information on how to specify icons. `timeout` @@ -2275,7 +2290,7 @@ Whether to only show running containers. If set to `true` only containers that a | Name | Description | | ---- | ----------- | | glance.name | The name displayed in the UI. If not specified, the name of the container will be used. | -| glance.icon | The icon displayed in the UI. Can be an external URL or an icon prefixed with si:, sh: or di: like with the bookmarks and monitor widgets | +| glance.icon | See [Icons](#icons) for more information on how to specify icons | | glance.url | The URL that the user will be redirected to when clicking on the container. | | glance.same-tab | Whether to open the link in the same or a new tab. Default is `false`. | | glance.description | A short description displayed in the UI. Default is empty. | @@ -2590,17 +2605,7 @@ An array of groups which can optionally have a title and a custom color. `icon` -URL pointing to an image. You can also directly use [Simple Icons](https://simpleicons.org/) via a `si:` prefix or [Dashboard Icons](https://github.com/walkxcode/dashboard-icons) via a `di:` prefix: - -```yaml -icon: si:gmail -icon: si:youtube -icon: si:reddit -``` - -> [!WARNING] -> -> Simple Icons are loaded externally and are hosted on `cdn.jsdelivr.net`, if you do not wish to depend on a 3rd party you are free to download the icons individually and host them locally. +See [Icons](#icons) for more information on how to specify icons. `same-tab` diff --git a/internal/glance/config-fields.go b/internal/glance/config-fields.go index 1527c72..608ffff 100644 --- a/internal/glance/config-fields.go +++ b/internal/glance/config-fields.go @@ -152,31 +152,27 @@ func newCustomIconField(value string) customIconField { return field } + basename, ext, found := strings.Cut(icon, ".") + if !found { + ext = "svg" + basename = icon + } + + if ext != "svg" && ext != "png" { + ext = "svg" + } + switch prefix { case "si": - field.URL = template.URL("https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/" + icon + ".svg") field.IsFlatIcon = true - case "di", "sh": - // syntax: di:[.svg|.png] - // syntax: sh:[.svg|.png] - // if the icon name is specified without extension, it is assumed to be wanting the SVG icon - // otherwise, specify the extension of either .svg or .png to use either of the CDN offerings - // any other extension will be interpreted as .svg - basename, ext, found := strings.Cut(icon, ".") - if !found { - ext = "svg" - basename = icon - } - - if ext != "svg" && ext != "png" { - ext = "svg" - } - - if prefix == "di" { - field.URL = template.URL("https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/" + ext + "/" + basename + "." + ext) - } else { - field.URL = template.URL("https://cdn.jsdelivr.net/gh/selfhst/icons/" + ext + "/" + basename + "." + ext) - } + field.URL = template.URL("https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/" + basename + ".svg") + case "di": + field.URL = template.URL("https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/" + ext + "/" + basename + "." + ext) + case "mdi": + field.IsFlatIcon = true + 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) default: field.URL = template.URL(value) } From f0541ea5c8c9b1868b9ee0d80f771b5ae30a82b3 Mon Sep 17 00:00:00 2001 From: Svilen Markov <7613769+svilenmarkov@users.noreply.github.com> Date: Tue, 10 Jun 2025 08:25:34 +0100 Subject: [PATCH 10/10] Refactor icon stuff --- docs/configuration.md | 2 +- internal/glance/config-fields.go | 11 ++++------- internal/glance/templates/bookmarks.html | 2 +- internal/glance/templates/docker-containers.html | 2 +- internal/glance/templates/monitor.html | 2 +- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 8b975b0..58fb997 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -188,7 +188,7 @@ This assumes that the config you want to print is in your current working direct ## Icons -For widgets which provide you with the ability to specify icons such as the monitor, bookmarks, docker containers, etc, you can use the `icon` property to specify a URL to an image. Alternatively, you can also use icon names from multiple libraries via prefixes: +For widgets which provide you with the ability to specify icons such as the monitor, bookmarks, docker containers, etc, you can use the `icon` property to specify a URL to an image or use icon names from multiple libraries via prefixes: ```yml icon: si:immich # si for Simple icons https://simpleicons.org/ diff --git a/internal/glance/config-fields.go b/internal/glance/config-fields.go index 608ffff..2eec599 100644 --- a/internal/glance/config-fields.go +++ b/internal/glance/config-fields.go @@ -131,10 +131,7 @@ func (d *durationField) UnmarshalYAML(node *yaml.Node) error { type customIconField struct { URL template.URL - IsFlatIcon bool - // TODO: along with whether the icon is flat, we also need to know - // whether the icon is black or white by default in order to properly - // invert the color based on the theme being light or dark + AutoInvert bool } func newCustomIconField(value string) customIconField { @@ -142,7 +139,7 @@ func newCustomIconField(value string) customIconField { field := customIconField{} if strings.HasPrefix(value, autoInvertPrefix) { - field.IsFlatIcon = true + field.AutoInvert = true value = strings.TrimPrefix(value, autoInvertPrefix) } @@ -164,12 +161,12 @@ func newCustomIconField(value string) customIconField { switch prefix { case "si": - field.IsFlatIcon = true + field.AutoInvert = true field.URL = template.URL("https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/" + basename + ".svg") case "di": field.URL = template.URL("https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/" + ext + "/" + basename + "." + ext) case "mdi": - field.IsFlatIcon = true + field.AutoInvert = true 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) diff --git a/internal/glance/templates/bookmarks.html b/internal/glance/templates/bookmarks.html index 1952cdb..a1c9c51 100644 --- a/internal/glance/templates/bookmarks.html +++ b/internal/glance/templates/bookmarks.html @@ -13,7 +13,7 @@
{{- if ne "" .Icon.URL }}
- +
{{- end }} {{ .Title }} diff --git a/internal/glance/templates/docker-containers.html b/internal/glance/templates/docker-containers.html index afbbb17..1e694ae 100644 --- a/internal/glance/templates/docker-containers.html +++ b/internal/glance/templates/docker-containers.html @@ -5,7 +5,7 @@ {{- range .Containers }}