mirror of
https://github.com/glanceapp/glance.git
synced 2025-06-21 02:18:22 +02:00
Merge branch 'dev'
This commit is contained in:
commit
c88fd526e5
@ -9,8 +9,5 @@ FROM alpine:3.21
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /app/glance .
|
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
|
EXPOSE 8080/tcp
|
||||||
ENTRYPOINT ["/app/glance", "--config", "/app/config/glance.yml"]
|
ENTRYPOINT ["/app/glance", "--config", "/app/config/glance.yml"]
|
||||||
|
@ -3,8 +3,5 @@ FROM alpine:3.21
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY glance .
|
COPY glance .
|
||||||
|
|
||||||
HEALTHCHECK --timeout=10s --start-period=60s --interval=60s \
|
|
||||||
CMD wget --spider -q http://localhost:8080/api/healthz
|
|
||||||
|
|
||||||
EXPOSE 8080/tcp
|
EXPOSE 8080/tcp
|
||||||
ENTRYPOINT ["/app/glance", "--config", "/app/config/glance.yml"]
|
ENTRYPOINT ["/app/glance", "--config", "/app/config/glance.yml"]
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
- [Environment variables](#environment-variables)
|
- [Environment variables](#environment-variables)
|
||||||
- [Other ways of providing tokens/passwords/secrets](#other-ways-of-providing-tokenspasswordssecrets)
|
- [Other ways of providing tokens/passwords/secrets](#other-ways-of-providing-tokenspasswordssecrets)
|
||||||
- [Including other config files](#including-other-config-files)
|
- [Including other config files](#including-other-config-files)
|
||||||
|
- [Icons](#icons)
|
||||||
- [Config schema](#config-schema)
|
- [Config schema](#config-schema)
|
||||||
- [Authentication](#authentication)
|
- [Authentication](#authentication)
|
||||||
- [Server](#server)
|
- [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`.
|
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 or 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
|
## 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!
|
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!
|
||||||
@ -1962,17 +1987,7 @@ If the monitored service returns an error, the user will be redirected here. If
|
|||||||
|
|
||||||
`icon`
|
`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:
|
See [Icons](#icons) for more information on how to specify icons.
|
||||||
|
|
||||||
```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.
|
|
||||||
|
|
||||||
`timeout`
|
`timeout`
|
||||||
|
|
||||||
@ -2280,7 +2295,7 @@ Whether to only show running containers. If set to `true` only containers that a
|
|||||||
| Name | Description |
|
| Name | Description |
|
||||||
| ---- | ----------- |
|
| ---- | ----------- |
|
||||||
| glance.name | The name displayed in the UI. If not specified, the name of the container will be used. |
|
| 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.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.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. |
|
| glance.description | A short description displayed in the UI. Default is empty. |
|
||||||
@ -2595,17 +2610,7 @@ An array of groups which can optionally have a title and a custom color.
|
|||||||
|
|
||||||
`icon`
|
`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:
|
See [Icons](#icons) for more information on how to specify icons.
|
||||||
|
|
||||||
```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.
|
|
||||||
|
|
||||||
`same-tab`
|
`same-tab`
|
||||||
|
|
||||||
|
@ -131,37 +131,24 @@ func (d *durationField) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
|
|
||||||
type customIconField struct {
|
type customIconField struct {
|
||||||
URL template.URL
|
URL template.URL
|
||||||
IsFlatIcon bool
|
AutoInvert 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCustomIconField(value string) customIconField {
|
func newCustomIconField(value string) customIconField {
|
||||||
const autoInvertPrefix = "auto-invert "
|
const autoInvertPrefix = "auto-invert "
|
||||||
field := customIconField{}
|
field := customIconField{}
|
||||||
|
|
||||||
prefix, icon, found := strings.Cut(value, ":")
|
|
||||||
if !found {
|
|
||||||
if strings.HasPrefix(value, autoInvertPrefix) {
|
if strings.HasPrefix(value, autoInvertPrefix) {
|
||||||
field.IsFlatIcon = true
|
field.AutoInvert = true
|
||||||
value = strings.TrimPrefix(value, autoInvertPrefix)
|
value = strings.TrimPrefix(value, autoInvertPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prefix, icon, found := strings.Cut(value, ":")
|
||||||
|
if !found {
|
||||||
field.URL = template.URL(value)
|
field.URL = template.URL(value)
|
||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
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:<icon_name>[.svg|.png]
|
|
||||||
// syntax: sh:<icon_name>[.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, ".")
|
basename, ext, found := strings.Cut(icon, ".")
|
||||||
if !found {
|
if !found {
|
||||||
ext = "svg"
|
ext = "svg"
|
||||||
@ -172,11 +159,17 @@ func newCustomIconField(value string) customIconField {
|
|||||||
ext = "svg"
|
ext = "svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
if prefix == "di" {
|
switch prefix {
|
||||||
|
case "si":
|
||||||
|
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)
|
field.URL = template.URL("https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/" + ext + "/" + basename + "." + ext)
|
||||||
} else {
|
case "mdi":
|
||||||
|
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)
|
field.URL = template.URL("https://cdn.jsdelivr.net/gh/selfhst/icons/" + ext + "/" + basename + "." + ext)
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
field.URL = template.URL(value)
|
field.URL = template.URL(value)
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@ function setupSearchBoxes() {
|
|||||||
|
|
||||||
document.addEventListener("keydown", (event) => {
|
document.addEventListener("keydown", (event) => {
|
||||||
if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) return;
|
if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) return;
|
||||||
if (event.key != "s") return;
|
if (event.code != "KeyS") return;
|
||||||
|
|
||||||
inputElement.focus();
|
inputElement.focus();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -643,14 +643,15 @@ async function setupCalendars() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function setupTodos() {
|
async function setupTodos() {
|
||||||
const elems = document.getElementsByClassName("todo");
|
const elems = Array.from(document.getElementsByClassName("todo"));
|
||||||
if (elems.length == 0) return;
|
if (elems.length == 0) return;
|
||||||
|
|
||||||
const todo = await import ('./todo.js');
|
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]);
|
todo.default(elems[i]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setupTruncatedElementTitles() {
|
function setupTruncatedElementTitles() {
|
||||||
const elements = document.querySelectorAll(".text-truncate, .single-line-titles .title, .text-truncate-2-lines, .text-truncate-3-lines");
|
const elements = document.querySelectorAll(".text-truncate, .single-line-titles .title, .text-truncate-2-lines, .text-truncate-3-lines");
|
||||||
@ -661,7 +662,8 @@ function setupTruncatedElementTitles() {
|
|||||||
|
|
||||||
for (let i = 0; i < elements.length; i++) {
|
for (let i = 0; i < elements.length; i++) {
|
||||||
const element = elements[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+/g, " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ function repositionContainer() {
|
|||||||
} else if (left + containerBounds.width > window.innerWidth) {
|
} else if (left + containerBounds.width > window.innerWidth) {
|
||||||
containerElement.style.removeProperty("left");
|
containerElement.style.removeProperty("left");
|
||||||
containerElement.style.right = 0;
|
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 {
|
} else {
|
||||||
containerElement.style.removeProperty("right");
|
containerElement.style.removeProperty("right");
|
||||||
containerElement.style.left = left + "px";
|
containerElement.style.left = left + "px";
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<div class="flex items-center gap-10">
|
<div class="flex items-center gap-10">
|
||||||
{{- if ne "" .Icon.URL }}
|
{{- if ne "" .Icon.URL }}
|
||||||
<div class="bookmarks-icon-container">
|
<div class="bookmarks-icon-container">
|
||||||
<img class="bookmarks-icon{{ if .Icon.IsFlatIcon }} flat-icon{{ end }}" src="{{ .Icon.URL }}" alt="" loading="lazy">
|
<img class="bookmarks-icon{{ if .Icon.AutoInvert }} flat-icon{{ end }}" src="{{ .Icon.URL }}" alt="" loading="lazy">
|
||||||
</div>
|
</div>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
<a href="{{ .URL | safeURL }}" class="bookmarks-link {{ if .HideArrow }}bookmarks-link-no-arrow {{ end }}color-highlight size-h4" {{ if .Target }}target="{{ .Target }}"{{ end }} rel="noreferrer">{{ .Title }}</a>
|
<a href="{{ .URL | safeURL }}" class="bookmarks-link {{ if .HideArrow }}bookmarks-link-no-arrow {{ end }}color-highlight size-h4" {{ if .Target }}target="{{ .Target }}"{{ end }} rel="noreferrer">{{ .Title }}</a>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{{- range .Containers }}
|
{{- range .Containers }}
|
||||||
<li class="docker-container flex items-center gap-15">
|
<li class="docker-container flex items-center gap-15">
|
||||||
<div class="shrink-0" data-popover-type="html" data-popover-position="above" data-popover-offset="0.25" data-popover-margin="0.1rem" data-popover-max-width="400px" aria-hidden="true">
|
<div class="shrink-0" data-popover-type="html" data-popover-position="above" data-popover-offset="0.25" data-popover-margin="0.1rem" data-popover-max-width="400px" aria-hidden="true">
|
||||||
<img class="docker-container-icon{{ if .Icon.IsFlatIcon }} flat-icon{{ end }}" src="{{ .Icon.URL }}" alt="" loading="lazy">
|
<img class="docker-container-icon{{ if .Icon.AutoInvert }} flat-icon{{ end }}" src="{{ .Icon.URL }}" alt="" loading="lazy">
|
||||||
<div data-popover-html>
|
<div data-popover-html>
|
||||||
<div class="color-highlight text-truncate block">{{ .Image }}</div>
|
<div class="color-highlight text-truncate block">{{ .Image }}</div>
|
||||||
<div>{{ .StateText }}</div>
|
<div>{{ .StateText }}</div>
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
{{ define "site" }}
|
{{ define "site" }}
|
||||||
{{ if .Icon.URL }}
|
{{ if .Icon.URL }}
|
||||||
<img class="monitor-site-icon{{ if .Icon.IsFlatIcon }} flat-icon{{ end }}" src="{{ .Icon.URL }}" alt="" loading="lazy">
|
<img class="monitor-site-icon{{ if .Icon.AutoInvert }} flat-icon{{ end }}" src="{{ .Icon.URL }}" alt="" loading="lazy">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="grow min-width-0">
|
<div class="grow min-width-0">
|
||||||
<a class="size-h3 color-highlight text-truncate block" href="{{ .URL | safeURL }}" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
|
<a class="size-h3 color-highlight text-truncate block" href="{{ .URL | safeURL }}" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user