github glanceapp/glance v0.7.0

latest release: v0.7.1
one day ago

Jump to section

  • Docker container breaking changes
  • CLI breaking changes
  • New widgets
    • Docker containers
    • Custom API
    • Server stats
    • Split column
    • Calendar (reworked)
  • New features
    • Auto reload on config change
    • Environment variables anywhere in the config
    • Include files in the config
    • Custom HTML in document <head>
    • Support for selfh.st and Dashboard icons
  • New styles
    • [Monitor] compact
    • [Videos] vertical-list
  • Widget enhancements
    • [Twitch channels] stream preview on hover
    • [RSS] request headers, limit per feed, preserve-order
    • [Videos] playlists
    • [Group] support for title-url
    • [Monitor] alt-status-codes, error-url
    • [Extension] specify if content should have no frame
    • [Bookmarks] target for links, same-tab and hide-arrow for groups
    • [Reddit] proxy
    • [Search] placeholder and clear input on search
    • [Markets] chart-link-template, symbol-link-template and sort-by
    • [Releases] include-prereleases
    • [DNS stats] hide-graph, hide-top-domains and allow-insecure
    • [Clock] now displays exact time difference for timezones
  • Fixes
  • Been a while
  • Community overhaul
  • Thank you

Docker container breaking changes

Until now, setting up Glance using its Docker container involved a somewhat unusual step where the config file was solely mounted as a volume. This tripped up some users and caused unnecessary confusion due to a few odd behaviors.

The default location of the config file has now been changed to /app/config/glance.yml from /app/glance.yml. That means your docker-compose.yml file should now look like the following:

services:
  glance:
    image: glanceapp/glance
    volumes:
      - ./config:/app/config

This also requires that you place your glance.yml file in a config directory in the same location as your docker-compose.yml file. If you're unsure what changes you need to make, there is an upgrade guide available here as well as a new recommended docker compose directory structure available here.

CLI breaking changes

Previously, you could run the following to validate your config file without starting Glance:

glance --config /path/to/config.yml --check-config

For the sake of consistency with the few new added comands (and because this shouldn't have been a flag in the first place), this has now been changed to:

glance --config /path/to/config.yml config:validate

New widgets

Docker containers

Display the status of your Docker containers along with an icon and an optional short description.

docker-containers-preview

- type: docker-containers
  hide-by-default: false

Note

The widget requires access to docker.sock. If you're running Glance inside a container, this can be done by mounting the socket as a volume:

services:
  glance:
    image: glanceapp/glance
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

Configuration of the containers is done via labels applied to each container:

  jellyfin:
    image: jellyfin/jellyfin:latest
    labels:
      glance.name: Jellyfin
      glance.icon: si:jellyfin
      glance.url: https://jellyfin.domain.com
      glance.description: Movies & shows

For services with multiple containers you can specify a glance.id on the "main" container and glance.parent on each "child" container:

View docker-compose.yml
services:
  immich-server:
    image: ghcr.io/immich-app/immich-server
    labels:
      glance.name: Immich
      glance.icon: si:immich
      glance.url: https://immich.domain.com
      glance.description: Image & video management
      glance.id: immich

  redis:
    image: docker.io/redis:6.2-alpine
    labels:
      glance.parent: immich
      glance.name: Redis

  database:
    image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0
    labels:
      glance.parent: immich
      glance.name: DB

  proxy:
    image: nginx:stable
    labels:
      glance.parent: immich
      glance.name: Proxy

This will place all child containers under the Immich container when hovering over its icon:

docker-container-parent

If any of the child containers are down, their status will propagate up to the parent container:

docker-container-parent2

(thanks @baranovskis & @reality-exe, inspired by https://github.com/DVDAndroid/glance-docker-container-ext)

Custom API

Display data from any JSON API in an entirely customizable template.

Note

With great power come great requirements.

The configuration of this widget requires some basic knowledge of programming, HTML, CSS, the Go template language and Glance-specific concepts.

custom-api-preview-1

View glance.yml
- type: custom-api
  title: Random Fact
  cache: 6h
  url: https://uselessfacts.jsph.pl/api/v2/facts/random
  template: |
    <p class="size-h4 color-paragraph">{{ .JSON.String "text" }}</p>

custom-api-preview-2

View glance.yml
- type: custom-api
  title: Immich stats
  cache: 1d
  url: https://${IMMICH_URL}/api/server/statistics
  headers:
    x-api-key: ${IMMICH_API_KEY}
    Accept: application/json
  template: |
    <div class="flex justify-between text-center">
      <div>
          <div class="color-highlight size-h3">{{ .JSON.Int "photos" | formatNumber }}</div>
          <div class="size-h6">PHOTOS</div>
      </div>
      <div>
          <div class="color-highlight size-h3">{{ .JSON.Int "videos" | formatNumber }}</div>
          <div class="size-h6">VIDEOS</div>
      </div>
      <div>
          <div class="color-highlight size-h3">{{ div (.JSON.Int "usage" | toFloat) 1073741824 | toInt | formatNumber }}GB</div>
          <div class="size-h6">USAGE</div>
      </div>
    </div>

custom-api-preview-3

View glance.yml
- type: custom-api
  title: Steam Specials
  cache: 12h
  url: https://store.steampowered.com/api/featuredcategories?cc=us
  template: |
    <ul class="list list-gap-10 collapsible-container" data-collapse-after="5">
    {{ range .JSON.Array "specials.items" }}
      <li>
        <a class="size-h4 color-highlight block text-truncate" href="https://store.steampowered.com/app/{{ .Int "id" }}/">{{ .String "name" }}</a>
        <ul class="list-horizontal-text">
          <li>{{ div (.Int "final_price" | toFloat) 100 | printf "$%.2f" }}</li>
          {{ $discount := .Int "discount_percent" }}
          <li{{ if ge $discount 40 }} class="color-positive"{{ end }}>{{ $discount }}% off</li>
        </ul>
      </li>
    {{ end }}
    </ul>

(thanks @reality-exe)

Server stats

There are other projects dedicated to providing this kind of functionality and this is not meant to be a replacement for any of them, it's intended for people who just want the most basic information about their server without setting up a separate monitoring solution. Things will get improved based on feedback, but the goal is to keep it simple and minimal. If you're looking for a more feature-rich server monitoring solution, Beszel looks like a good choice. With that out of the way:

server-stats-preview

- type: server-stats
  servers:
    - type: local
      name: Services
      # optionally override the sensor used to determine CPU temperature
      # cpu-temp-sensor: thermal_zone0

      # optionally hide swap
      # hide-swap: true

      # optionally hide or rename mountpoints
      # mountpoints:
      #   "/boot/efi":
      #     hide: true
      #     name: EFI

There's a bunch of bars and their purpose might not be immediately obvious:

  • CPU - 1m and 15m average load
  • RAM - RAM and SWAP
  • Disk – the two mountpoints with the highest usage percentages

If your CPU is feeling a little spicy (assuming Glance was able to determine your CPU sensor) and reaches >=80°C, you'll see a little flame icon next to it. The progress indicators will also turn red (or the equivalent of your negative color) to hopefully grab your attention if anything is unusually high:

server-stats-preview-1

Now, it's not uncommon to have multiple servers at home these days, and you'd probably want to see their stats as well, so there's support for that too:

server-stats-preview-2

View glance.yml
- type: server-stats
  servers:
    - type: remote
      name: Services
      url: http://${SERVER_IP}:27973
      token: ${AGENT_TOKEN}

    - type: remote
      name: DNS
      url: http://${SERVER_IP}:27973
      token: ${AGENT_TOKEN}

After looking for an existing solution to delegate this job to, nothing quite fit all of the following criteria:

  • Super minimal and lightweight
  • Does nothing in the background, no storage, just provides system info when asked
  • HTTP JSON API
  • Optional authentication
  • Easy, configurable installation with both a single binary and a Docker container
  • Supports x86_64 and ARM

As a result, somewhat contradictory to the first paragraph, I decided to build one (it will be made available in the next couple of days). It's quite simple, very much work in progress, and I'm not sure how well it will work for everyone, but it's an option for those who want to go with this approach. It's not required if you just want to see the stats of the server Glance is installed on, only if you want to see the stats of multiple servers, in which case you'd have to install it on each. Here's what the setup process looks like:

agent-install-demo.mp4

Installed, configured and uninstalled like it never existed in less than a minute.

Because the agent provides this information via a simple HTTP JSON API, you could use the custom API widget to display the stats in any way you like:

custom-api-server-stats

View glance.yml
- type: custom-api
  title: Server stats
  url: http://${SERVER_IP}:27973/api/sysinfo/all
  headers:
    Authorization: Bearer ${AGENT_TOKEN}
  template: |
    <div class="flex justify-between text-center">
      <div>
          <div class="color-highlight size-h3">{{ .JSON.Int "cpu.load1_percent" }}%</div>
          <div class="size-h6">CPU</div>
      </div>
      <div>
          <div class="color-highlight size-h3">{{ .JSON.Int "memory.used_percent" }}%</div>
          <div class="size-h6">RAM</div>
      </div>
      <div>
          <div class="color-highlight size-h3">{{ .JSON.Int "mountpoints.0.used_percent" }}%</div>
          <div class="size-h6">DISK</div>
      </div>
    </div>

- type: custom-api
  title: Server stats
  url: http://${SERVER_IP}:27973/api/sysinfo/all
  headers:
    Authorization: Bearer ${AGENT_TOKEN}
  template: |
    <div class="flex flex-column gap-5">
      <div class="flex gap-20 items-center">
          <div class="size-h5" style="width: 4ch">CPU</div>
          <div class="progress-bar grow">
            <div class="progress-value" style="--percent: {{ .JSON.Int "cpu.load1_percent" }}"></div>
          </div>
          <div class="color-highlight text-very-compact text-right" style="width: 4ch">{{ .JSON.Int "cpu.load1_percent" }} <span class="color-base size-h5">%</span></div>
      </div>
      <div class="flex gap-20 items-center">
          <div class="size-h5" style="width: 4ch">RAM</div>
          <div class="progress-bar grow">
            <div class="progress-value" style="--percent: {{ .JSON.Int "memory.used_percent" }}"></div>
          </div>
          <div class="color-highlight text-very-compact text-right" style="width: 4ch">{{ .JSON.Int "memory.used_percent" }} <span class="color-base size-h5">%</span></div>
      </div>
      <div class="flex gap-20 items-center">
          <div class="size-h5" style="width: 4ch">DISK</div>
          <div class="progress-bar grow">
            <div class="progress-value" style="--percent: {{ .JSON.Int "mountpoints.0.used_percent" }}"></div>
          </div>
          <div class="color-highlight text-very-compact text-right" style="width: 4ch">{{ .JSON.Int "mountpoints.0.used_percent" }} <span class="color-base size-h5">%</span></div>
      </div>
    </div>

There are plans to also add the ability to view the stats of Docker containers, so you could combine this with the Docker containers widget and see the status of your containers across multiple servers.

Support for other providers (such as Glances) are likely be added once everything else gets fleshed out a bit more.

(thanks @FedeP14 and shirou for the excellent module)

Split column

The ways in which you could organize your widgets has so far been somewhat limited. This widget brings a new dimension that you play around with by allowing you to vertically split a full sized column into multiple smaller columns of equal width.

Two widgets side by side in a full column:

split-column-widget-preview

View glance.yml
# ...
- size: full
  widgets:
    - type: split-column
      widgets:
        - type: hacker-news
          collapse-after: 3
        - type: lobsters
          collapse-after: 3

    - type: videos
# ...

You can also achieve a number of new full page layouts that were previously not possible, such as:

3 column layout where all columns have equal width:

split-column-widget-3-columns

View glance.yml
pages:
  - name: Home
    columns:
      - size: full
        widgets:
          - type: split-column
            max-columns: 3
            widgets:
              - type: reddit
                subreddit: selfhosted
                collapse-after: 15
              - type: reddit
                subreddit: homelab
                collapse-after: 15
              - type: reddit
                subreddit: sysadmin
                collapse-after: 15

4 column layout where all columns have equal width (and the page is set to width: wide):

split-column-widget-4-columns

View glance.yml
pages:
  - name: Home
    width: wide
    columns:
      - size: full
        widgets:
          - type: split-column
            max-columns: 4
            widgets:
              - type: reddit
                subreddit: selfhosted
                collapse-after: 15
              - type: reddit
                subreddit: homelab
                collapse-after: 15
              - type: reddit
                subreddit: linux
                collapse-after: 15
              - type: reddit
                subreddit: sysadmin
                collapse-after: 15

Masonry layout with up to 5 columns where all columns have equal width (and the page is set to width: wide):

split-column-widget-masonry

View glance.yml
define:
  - &subreddit-settings
    type: reddit
    collapse-after: 5

pages:
  - name: Home
    width: wide
    columns:
      - size: full
        widgets:
          - type: split-column
            max-columns: 5
            widgets:
              - subreddit: selfhosted
                <<: *subreddit-settings
              - subreddit: homelab
                <<: *subreddit-settings
              - subreddit: linux
                <<: *subreddit-settings
              - subreddit: sysadmin
                <<: *subreddit-settings
              - subreddit: DevOps
                <<: *subreddit-settings
              - subreddit: Networking
                <<: *subreddit-settings
              - subreddit: DataHoarding
                <<: *subreddit-settings
              - subreddit: OpenSource
                <<: *subreddit-settings
              - subreddit: Privacy
                <<: *subreddit-settings
              - subreddit: FreeSoftware
                <<: *subreddit-settings

Just like the group widget, you can insert any widget type, you can even insert a group widget inside of a split-column widget, but you can't insert a split-column widget inside of a group widget.

Note

This does not create new columns in the usual sense, meaning that you won't see a "dot" in the mobile navigation for each of these columns, it will just be a single dot. When viewing the page on a mobile device, all columns will turn into in a single one with the widgets stacked on top of each other in the order they were defined.

Calendar

The calendar widget has been reworked to look and behave more like an actual calendar.

calendar-widget-preview

- type: calendar
  first-day-of-week: monday # make it any day of the week you need

Previously, the calendar would display the time of the server running Glance. This has now been changed to display the current time in the user's timezone based on their browser settings. As a result, mounting /etc/timezone and /etc/localtime in your Docker container is no longer necessary.

Because the new calendar is considered WIP and does not yet include the same 3 week view, the old calendar widget is still available under the widget type calendar-legacy. The old calendar widget also has a new property for setting the first day of the week as sunday via start-sunday: true (thanks @xendke).

Note

If you do want to use the legacy calendar you would still need to mount /etc/timezone and /etc/localtime in your Docker container.

This release was also meant to include the ability to add calendar events from ICS feeds, however unfortunately this feature turned out to be more complex than anticipated and will be included in a future release instead.

New features

Auto reload on config change

Finally, the highly requested feature of automatically reloading Glance when the config changes has been implemented. There are some things that break this functionality, such as deleting the config file, but for the most part it should work as expected. Note that changing environment variable values still requires a full manual restart.

Caution

Reloading the config clears your cached data, so be mindful when making lots of changes as you may end up hitting rate limits on APIs.

(thanks @bcurran3 & @helloteemo & @arminus & everyone else who suggested this or tried implementing it)

Environment variables anywhere in the config

Up until now, the ability to use environment variables worked only on specific properties in the config and needed to be added individually for each property. This functionality has now been expanded to work anywhere in the config:

pages:
  - name: Home
    columns:
      - size: ${WORKS_HERE}
        widgets:
          - type: ${WORKS_HERE}
            limit: ${WORKS_HERE}
            collapse-after: ${AND_HERE}
            cache: ${AS_WELL_AS_HERE}
            feeds:
              - url: ${AND_OF_COURSE_HERE}
              - ${EVEN_HERE}: https://${HERE_TOO}/feed

If you need to use the syntax ${NAME} in your config without it being interpreted as an environment variable, you can escape it by prefixing with a backslash \:

something: \${NOT_AN_ENV_VAR}

(thanks @jfroy & @mgrimace)

Include files in the config

Having a single, ever-growing config file can get a little overwhelming, especially with how picky YAML can be about indentation. To help with this, you can now split your config into multiple files and include them in the main config:

theme:
  !include: theme.yml

pages:
  !include: home.yml
  !include: news.yml
  !include: homelab.yml

  # these paths also work:
  !include: ../pages/home.yml
  !include: pages/news.yml
  !include: /etc/opt/glance/homelab.yml

  # can be anywhere in the config, so long as they are on their own line:
  - name: Example
    columns:
      - size: small
        !include: example-left.yml
      - size: full
        !include: example-middle.yml
      - size: small
        !include: example-right.yml

The path of each file is relative to the main config file but you can also specify an absolute path. Changes to these included files will also trigger an automatic reload.

The proper indentation is automatically applied to the included content based on where it's included. The included file itself should have no extra indentation.

Note

For now, the !include directive only works in the main config file and not in included files.

Note

The !include directive is a preprocessing step, meaning that all files first get "stitched" together and then parsed as a single YAML file. Any YAML syntax errors will report the line number in the concatenated text, not the original file. To help with debugging, there is a new command that prints the full parsed config file with all includes resolved:

glance --config /path/to/config.yml config:print

You can pipe the output to less -N which will display line numbers.

Note

(yes, that's 3 notes in a row)

Because of the above note, including a file does not override existing keys at the same level. This will result in an error because the key collapse-after is defined twice:

reddit-settings.yml:

collapse-after: 5
limit: 10

glance.yml:

- type: reddit
  subreddit: linux
  collapse-after: 10
  !include: reddit-settings.yml

To reuse properties like this use YAML anchors instead.

(thanks @p5 & @Krojack)

Custom HTML in document <head>

Previously, if you wanted to insert HTML into the document across all pages, you had to do so through the custom-footer property which wasn't ideal. You can now insert custom HTML in the document's <head>:

# this is a top level property
document:
  head: |
    <meta http-equiv="refresh" content="120">
    <script>alert("probably don't do this... but you could if you wanted to!");</script>

(thank you to the peeps from the Discord server for bringing this up)

Support for selfh.st and Dashboard icons

You can now use selfh.st and Dashboard icons in the icon property of widgets such as the monitor and bookmarks:

  icon: si:github # simple icons
  icon: sh:github # selfh.st icons
  icon: di:github # Dashboard icons

(thanks @2q2code)

New styles

[Monitor] compact

monitor-widget-compact-preview

- type: monitor
  style: compact

(thanks for the idea Gato :D)

[Videos] vertical-list

videos-widget-vertical-list-preview

- type: videos
  style: vertical-list

Widget enhancements

[Twitch channels] stream preview on hover

stream-hover-preview

(thanks for the idea @uykukacinca)

[RSS] request headers, limit per feed, preserve-order

You can now specify request headers on a per-feed basis:

- type: rss
  feeds:
    - url: https://domain.com/rss
      headers:
        User-Agent: Custom User Agent
        Authorization: Bearer ${FEED_TOKEN}
        # etc...

You can also limit the number of items per feed, for those feeds which post a little too frequently and push the rest of the articles from other feeds down:

- type: rss
  feeds:
    - url: https://domain.com/rss
      limit: 5

And finally, you can now preserve the order of the items in each feed, useful for feeds which place their most important articles at the top:

- type: rss
  preserve-order: true
  feeds:
    - url: https://domain.com/rss

(thanks @paulkakell & @lunik1)

[Videos] playlists

You can now add YouTube playlists to the videos widget:

- type: videos
  playlists:
    - PL8mG-RkN2uTyZZ00ObwZxxoG_nJbs3qec
    - PL8mG-RkN2uTxTK4m_Vl2dYR9yE41kRdBg

(thanks @DickenSerm & @vishalkadam47)

[Group] support for title-url

The title-url property now works for widgets placed inside of a group. To open their link in a new tab you can either middle click on any of the titles, or click again on the currently selected widget's title.

[Monitor] alt-status-codes, error-url

You can now specify alternative status codes to be considered as "up" for the monitor widget:

- type: monitor
  title: Services
  sites:
    - title: Jellyfin
      url: https://jellyfin.yourdomain.com
      icon: sh:jellyfin
      alt-status-codes: [401, 403]

You can also specify an alternative URL for the site for when it is down:

- type: monitor
  title: Services
  sites:
    - title: Jellyfin
      url: https://jellyfin.yourdomain.com
      icon: sh:jellyfin
      error-url: https://status.yourdomain.com

(thanks @cmeadowstech & @JeckDev)

[Extension] specify if content should have no frame

Extensions can now return a Widget-Content-Frameless header with a value of true to have their content displayed without a frame (the lighter background and border).

(thanks @DallasHoff)

[Bookmarks] target for links, same-tab and hide-arrow for groups

You can now specify the target for links in the bookmarks widget:

- type: bookmarks
  groups:
    - links:
        - title: Gmail
          url: https://mail.google.com/mail/u/0/
          target: _parent

In addition, you can now specify same-tab, hide-arrow and target on groups and have each link in the group inherit those properties, or override them for a specific link:

- type: bookmarks
  groups:
    - same-tab: true   # ╺┓
      hide-arrow: true # ╺╋╸ default for all links in this group
      target: _parent  # ╺┛
      links:
        - title: Link
          url: ...
        - title: Link
          url: ...
          same-tab: false # override for this link
        - title: Link
          url: ...

(thanks @Jacksaur & @K0ng2)

[Reddit] proxy

You can now use a standard HTTP proxy in the Reddit widget for instances where Reddit is blocked:

- type: reddit
  subreddit: linux
  proxy:
    url: http://proxy.com:8080
    allow-insecure: true
    timeout: 10s

(thanks Bifrons)

[Search] placeholder and clear input on search

You can now specify a placeholder text for the search widget:

- type: search
  placeholder: Search...

In addition, when performing a search in a new tab, the search query will be cleared from the input field. You can press Up arrow to bring back the last search query.

(thanks @hecht-a & @KSJaay)

[Markets] chart-link-template, symbol-link-template and sort-by

You can now specify a sort order of change for descending order based on the stock's percentage change (e.g. 1% would be sorted higher than -1%).

- type: markets
  sort-by: change
  markets:
    - symbol: AAPL

You can also specify a template for the chart-link and symbol-link so you don't have to repeat the same URL for each market:

- type: markets
  chart-link-template: https://tradingview.com/chart?symbol={SYMBOL}
  symbol-link-template: https://finance.yahoo.com/quote/{SYMBOL}
  markets:
    - symbol: AAPL
    - symbol: TSLA
    - symbol: MSFT
    - symbol: GOOGL

(thanks @ehaughee)

[Releases] include-prereleases

You can now include prereleases in the releases widget for individual repositories (only supports GitHub for now):

- type: releases
  repositories:
    - repository: glanceapp/glance
      include-prereleases: true
    - immich/immich-server
    - jellyfin/jellyfin
    - go-gitea/gitea

(thanks @Mystically11)

[DNS stats] hide-graph, hide-top-domains and allow-insecure

You can now hide the graph as well as top domains in the DNS stats widget:

- type: dns-stats
  hide-graph: true
  hide-top-domains: true

You can also use an insecure connection to the API by setting allow-insecure: true if you're using a self-signed certificate.

(thanks @zatevakhin)

[Clock] now displays exact time difference for other timezones

(thanks @oliver-mitchell)

Fixes

  • URLs in the config with non-http/https schemas will no longer be escaped (thanks @kamilkosek)
  • Titles in RSS feeds with special characters will now be properly displayed (thanks @marb08)
  • Various visual bug fixes on iOS (thanks @DallasHoff)

Been a while

It's been almost 5 months since v0.6.0 and that's a long time to wait for new stuff. As new feature requests kept coming in I got stuck in a "I'll add this to the next release" loop, so the amount of things needing to be done grew out of hand. I want to avoid making this mistake again in the future by releasing smaller but more frequent updates.

I'm sorry for the wait y'all, I'll try and do better. I hope there's at least one thing in this release for everyone to get excited about.

There's a lot of new stuff out at once so inevitably there may be some bugs, I'll try and prioritize fixing those and releasing hotfixes over the next few weeks.

Community overhaul

The community side of the repository has gone through a bit of an overhaul. Previously, there were no guidelines around where and how to submit feature requests, bug reports or ask for help, so they were scattered across the issues and discussions which made them difficult to keep track of. Now, clear guidelines are in place: feature requests and bug reports should be submitted exclusively via issues. The discussions as well as Discord server will be for general discussions, help and sharing guides/resources.

Thank you

To all the sponsors who have continued to support the project even in the lack of activity during the last couple of months and to everyone who has contributed in the meantime. Also, I'd like to thank Ethan for featuring Glance on selfh.st! It's been a great motivator to see Glance listed alongside so many other great projects.

Don't miss a new glance release

NewReleases is sending notifications on new releases.