Glance

Productivity

A self-hosted dashboard that puts your feeds, calendars and services on one page.

Latest v0.8.5 · by GlanceWebsiteglanceapp/glance

Changelog

v0.8.5

Added 1
  • Add a way to bypass Reddit's API restrictions so the widget should work again without configuration changes
Changed 1
  • Bump Go and Alpine versions
Changes
  • Added a way to bypass Reddit's API restrictions, so the widget should be working again without any config changes. I don't know how long this will work for (or if it will work for everybody), see #1016 for the ongoing discussion in case it breaks again or to report issues.
  • Bumped Go and Alpine versions
View originalPermalink
How v0.8.5 went

v0.8.4

Added 1
  • Added support for Material Design icons via mdi prefix
Changed 1
  • Made the auto-invert icon prefix work for icons from libraries
Fixed 2
  • Fixed multiple to-do widgets on a single page not working
  • Fixed the search widget shortcut (S) not working when using a keyboard layout other than English
Removed 1
  • Removed the Docker image health check
New
  • Added support for Material Design icons via mdi prefix (thanks @NextBlaubeere)
  • Made the auto-invert icon prefix work for icons from libraries, so you can now do things like icon: auto-invert sh:glance-dark which will automatically invert dark icons to light depending on your theme
Fixes
  • Fixed multiple to-do widgets on a single page not working (thanks @rhijjawi & @tty2)
  • Fixed the search widget shortcut (S) not working when using a keyboard layout other than English (thanks @NikD0T)
Changed
  • The Docker image health check has been removed, for more information see #676
New Contributors
View originalPermalink
How v0.8.4 went

v0.8.3

Added 5
  • Added the ability to disable the theme picker via `disable-picker: true` under `theme`
  • Added `mod` function (modulo) to the custom API widget
  • Added `safeHTML` function to the custom API widget which prevents HTML from being escaped
  • Added `.Options.JSON` to the custom API widget which takes any nested option value and turns it into a JSON string
  • Added the theme key to the document root so that you can apply styles based on the selected theme
Fixed 2
  • Fixed `HTTP_PROXY` and `HTTPS_PROXY` environment variables not getting used
  • Fixed truncated links' hover text having unnecessary whitespace
New
  • Added the ability to disable the theme picker via disable-picker: true under theme (thanks @NeoNyaa)
  • Added mod function (modulo) to the custom API widget (thanks @anant-j)
  • Added safeHTML function to the custom API widget which prevents HTML from being escaped (thanks @sudoexec)
  • Added .Options.JSON to the custom API widget which takes any nested option value and turns it into a JSON string (thanks @ralphocdol)
- type: custom-api
  options: 
    request-body: 
      service: Session
      method: login
      params: 
        username: ${USERNAME}
        password: ${PASSWORD}

  # useful if you need to send JSON in a request body
  template: |
    ...
    | withStringBody (.Options.JSON "request-body")
    ...
  • Added the theme key to the document root so that you can apply styles based on the selected theme (thanks @EricKotato)
:root[data-theme="default-dark"] {
    .color-primary-if-not-visited {
        color: #ff0000;
    }
}

:root[data-theme="default-light"] {
    .color-primary-if-not-visited {
        color: #00ff00;
    }
}

:root[data-theme="your-theme-name"] {
    .color-primary-if-not-visited { 
        color: #0000ff;
    }
}
Fixes
  • Fixed HTTP_PROXY and HTTPS_PROXY environment variables not getting used (thanks @mazzz1y)
  • Fixed truncated links' hover text having unnecessary whitespace (thanks @ralphocdol)

Visit the release notes for v0.8.0 to learn about all new features

View originalPermalink
How v0.8.3 went

v0.8.2

Added 1
  • Added startOfDay and endOfDay functions to the custom API widget
Fixed 4
  • Fixed hide-deskop-navigation not working
  • Fixed custom favicons not working in Chromium browsers
  • Fixed server crash when using head-widgets with the releases widget
  • Fixed URLs getting escaped when using video-url-template in the videos widget with the vertical-list style
  • Fixed hide-deskop-navigation not working (thanks @LZStealth)
  • Fixed custom favicons not working in Chromium browsers (thanks @AWildLeon)
  • Fixed server crash when using head-widgets with the releases widget (thanks @aykhans)
  • Fixed URLs getting escaped when using video-url-template in the videos widget with the vertical-list style (thanks @Kamalaja)
  • Added startOfDay and endOfDay functions to the custom API widget (thanks @ralphocdol)

Visit the release notes for v0.8.0 to learn about all new features

View originalPermalink
How v0.8.2 went

v0.8.0

Added 10
  • Support for nested config includes allowing files to be included from within other included files
  • New $include syntax for configuration includes as a valid YAML alternative to !include
  • Support for Docker secrets in configuration files using ${secret:name} syntax
  • Theme picker allowing multiple theme presets to be defined and switched in the UI
  • Authentication support
  • To-do widget
Changed 6
  • Docker containers widget now supports container customization in config, proxies, category and format-container-names properties
  • Custom API widget now supports synchronous API calls and options property
  • Reddit widget now supports application authentication
  • RSS widget now supports conditional requests
  • Monitor widget now supports basic-auth and timeout properties
  • DNS Stats widget now supports Technitium

Another big update is here! This one includes authentication, a theme picker, new customization options, a to-do widget and lots of improvements to existing widgets. Before we get to details of all that...

Feedback wanted

Deciding what features to add to a project like this is no easy task. Most self-hosted services focus on doing one thing and adding features around that thing, but with a personal dashboard/feed aggregator you can practically display anything, so how do you decide what does and doesn't belong? So far I've been prioritizing feature requests with a high number of upvotes and those I believe fit with the vision of the project, although admittedly Glance has deviated quite a bit from its initial purpose.

As with almost all communities, a minority of people are very vocal about their wants and needs, while the majority are just riding along. Rather than guessing what the community as a whole would benefit from the most moving forward, I thought I could just ask through a short survey.

Link to the survey

It's entirely anonymous, should only take about 1 to 3 minutes to complete, and I'd be happy to share the results once there are enough responses. Thank you to those who take the time to fill it out.

Jump to section
Community widgets

If you weren't aware, there's now a repository for widgets made by the community with 40+ widgets available so far. Most are made using the custom-api widget, so adding them is as simple as copying them into your glance.yml. It's a great place to find new widgets, and if you have a widget that you think others would benefit from, please consider sharing it there. Thank you to everyone who has contributed!

New logo

Big thanks to @Tom607 for offering help with the logo and going over dozens of different iterations!

logo

I believe it does a great job at conveying the widgety layout of Glance, while also being simple and clean, as is the rest of the design language.

New features
Nested config includes and new include syntax

Up until now, you could only use the !include directive in your glance.yml file, but not within any of the included files. This has been changed so that you can includes files from within included files as many times as you need, meaning you can have a separate file for each page, a separate file for each column, and even a separate file for each widget if you wanted to:

glance.yml
├── home.yml
│   ├── column1.yml
│   │   ├── widget1.yml
│   │   └── widget2.yml
│   └── column2.yml
│       ├── widget3.yml
│       └── widget4.yml

In addition, the !include directive can now instead be written as $include. The reason for this change is that it turns out !include, with the way it was used in Glance, is not actually valid YAML (whoops), meaning it makes YAML validators and syntax highlighters angry. The new $include syntax is valid YAML and will be the preferred syntax moving forward. You can also add a - at the beginning of a line when including array items, since that's the expected syntax for YAML arrays.

Before:

theme:
  !include: theme.yml

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

Now:

theme:
  $include: theme.yml

pages:
  - $include: home.yml
  - $include: homelab.yml
  - $include: startpage.yml
Using Docker secrets within the config

Along with environment variables, you can now use Docker secrets in your config file using the following syntax:

token: ${secret:github_token} # Reads the contents of /run/secrets/github_token
token: ${GITHUB_TOKEN} # Environment variable

(thanks @gomeology)

Theme picker

You can now define multiple theme presets and switch between them in the UI, no more config changes needed to change the theme!

theme-switcher-preview

theme:
  presets:
    neon-focus:
      background-color: 255 14 6
      primary-color: 156 50 65
      negative-color: 342 65 65
      contrast-multiplier: 0.9
      text-saturation-multiplier: 0.8

    gruvbox-dark:
      background-color: 0 0 16
      primary-color: 43 59 81
      positive-color: 61 66 44
      negative-color: 6 96 59

    catppuccin-macchiato:
      background-color: 232 23 18
      contrast-multiplier: 1.2
      primary-color: 220 83 75
      positive-color: 105 48 72
      negative-color: 351 74 73

    peachy:
      light: true
      background-color: 28 40 77
      primary-color: 155 100 20
      negative-color: 0 100 60
      contrast-multiplier: 1.1
      text-saturation-multiplier: 0.5

Glance will include its own default dark and light themes in the list of presets, which you can override by using the default-dark and default-light keys.

(thanks @hecht-a)

Authentication

You can now add authentication via username and password:

auth-preview

auth:
  secret-key: # this must be set to a random value generated using the secret:make CLI command
  users:
    admin:
      password: ${ADMIN_PASSWORD}

While there is support for multiple users, you cannot customize the dashboard per user at this time, so all users will see the same dashboard.

To generate the secret key, use the secret:make CLI command:

docker run --rm glanceapp/glance secret:make

Or with the binary:

/opt/glance/glance secret:make

If you do not want to store plain passwords in environment variables or in files, you can hash your password and provide it via the password-hash property instead:

auth:
  secret-key: # this must be set to a random value generated using the secret:make CLI command
  users:
    admin:
      password-hash: $2y$10$...

To hash your password, use the password:hash <password> CLI command:

docker run --rm glanceapp/glance password:hash mypasswordishunter2

Or with the binary:

/opt/glance/glance password:hash mypasswordishunter2
To-do widget

A fairly simple to-do widget with the ability to add, edit, reorder and delete tasks:

todo-preview

- type: to-do

There are a number of keyboard shortcuts available that make it easier to use, which you can find over at the config docs.

The tasks are stored in the browser's local storage, so they won't be available on all devices. If you want to add multiple todo widgets, you must specify a unique id for each one:

- type: to-do
  title: Work tasks
  id: work

- type: to-do
  title: Personal tasks
  id: personal

(thanks @AMS003010)

Hiding widget headers

A fairly simple addition, but one that's been requested quite a few times - you can now hide widget headers by setting the hide-header property to true:

widgets:
  - type: calendar
    hide-header: true

If you do this for all widgets, you get something like this:

no-headers-preview

Note that you cannot hide the header of the group widget since it's used to switch between each widget.

Head widgets

You can now define "head widgets" on each page, which are basically widgets that sit above all columns and take up the full width. Combined with the hide-header property, you get something like this:

head-widgets-preview

pages:
  - name: Home
    head-widgets:
      - type: markets
        hide-header: true
        markets:
          - symbol: SPY
            name: S&P 500
          - symbol: BTC-USD
            name: Bitcoin
          - symbol: NVDA
            name: NVIDIA
          - symbol: AAPL
            name: Apple
          - symbol: MSFT
            name: Microsoft

    columns:
      - size: small
        widgets:
          - type: calendar
      - size: full
        widgets:
          - type: hacker-news
      - size: small
        widgets:
          - type: weather
            location: London, United Kingdom

This works well for displaying mostly horizontal widgets such as the videos widget, RSS widget with horizontal-cards style, or widgets which span their items horizontally, such as the markets widget.

Separate navigation and page widths

You can now have different desktop navigation and content widths:

pages:
  - name: Home
    width: slim
    desktop-navigation-width: wide
    columns:
      ...

By default desktop-navigation-width will inherit the value of width, but can explicitly be set to a different value (default, slim or wide). Using this property, if one of your pages has a different width than the rest, you can set the navigation to be the same width as the rest of the pages, meaning no more jankiness when switching between pages of different widths!

PWA configuration

You can now configure PWA things such as the app name, icon and theme color:

branding:
  app-name: Not Glance
  app-icon: /assets/path/to/icon.png
  app-background-color: '#000000' # ideally in hex format, since support for other formats is inconsistent between browsers

(thanks @dvdpearson)

Widget enhancements
[Docker containers] Container customization in the config, support for proxies and category property

Instead of using docker labels to customize the containers, you can now do so within your glance.yml:

- type: docker-containers
  containers:
    container_name_1:
      name: Container Name
      description: Description of the container
      url: https://container.domain.com
      icon: si:container-icon
      hide: false

    container_name_2:
      name: Other Container Name
      description: Other description of the container
      url: https://other-container.domain.com
      icon: si:container-icon
      hide: false

(thanks @mike391)

You can now also specify a proxied docker socket URI instead of a path to a socket file:

- type: docker-containers
  sock-path: http://docker-socket-proxy:2375 # tcp:// also works

(thanks @retmas-gh)

The new category property allows you to split up categories of containers into different widgets:

glance.yml:

# This will only list containers which have the glance.category label "tools"
- type: docker-containers
  title: Tools
  category: tools

# This will only list containers which have the glance.category label "arr"
- type: docker-containers
  title: Arr
  category: arr

docker-compose.yml:

services:
  my-container:
    image: my-image
    labels:
      glance.category: tools

  my-other-container:
    image: my-image
    labels:
      glance.category: arr

(thanks @shareef945)

[Custom API] Synchronous API calls and options property

So far you could only run asynchronous API calls, meaning you couldn't use the result of one API call in another. There is now support for synchronous API calls within the template itself:

- type: custom-api
  url: https://api.example.com/get-id-of-something
  template: |
    {{/* The ID is taken from the first API call */}}
    {{ $theID := .JSON.String "id" }}

    {{/* Then used in the second API call */}}
    {{
      $something := newRequest (concat "https://api.example.com/something/" $theID)
        | withParameter "key" "value"
        | withHeader "Accept" "application/json"
        | getResponse
    }}

    {{ $something.JSON.String "title" }}

You can optionally add query parameters using withParameter, headers with withHeader, and turn the request method into POST with a string body via withStringBody. This also means that you can conditionally execute requests within if statements, unlike with the subrequests property which executes all requests every time the cache expires.

In addition, the url property is now optional, meaning you can execute requests solely within the template. This is useful if you need to supply dynamic parameters to the endpoint:

- type: custom-api
  title: Events from the last 24h
  template: |
    {{
      $events := newRequest "https://api.example.com/events"
        | withParameter "after" (offsetNow "-24h" | formatTime "rfc3339")
        | getResponse
    }}

    {{ if eq $events.Response.StatusCode 200 }}
      {{ range $events.JSON.Array "events" }}
        <div>{{ .String "title" }}</div>
        <div {{ .String "date" | parseTime "rfc3339" | toRelativeTime }}></div>
      {{ end }}
    {{ else }}
      <p>Failed to fetch data: {{ $events.Response.Status }}</p>
    {{ end }}

There's a new options property which allows you to specify configurable options within the widget, rather than within the template.

Instead of defining options within the template and having to modify the template itself like such:

- type: custom-api
  template: |
    {{ /* User configurable options */ }}
    {{ $collapseAfter := 5 }}
    {{ $showThumbnails := true }}
    {{ $showFlairs := false }}

     <ul class="list list-gap-10 collapsible-container" data-collapse-after="{{ $collapseAfter }}">
      {{ if $showThumbnails }}
        <li>
          <img src="{{ .JSON.String "thumbnail" }}" alt="" />
        </li>
      {{ end }}
      {{ if $showFlairs }}
        <li>
          <span class="flair">{{ .JSON.String "flair" }}</span>
        </li>
      {{ end }}
     </ul>

You can use the .Options object to retrieve and define default values for these variables:

- type: custom-api
  template: |
    {{ $collapseAfter := .Options.IntOr "collapse-after" 5 }}
    {{ $showThumbnails := .Options.BoolOr "show-thumbnails" true }}
    {{ $showFlairs := .Options.BoolOr "show-flairs" false }}

    <ul class="list list-gap-10 collapsible-container" data-collapse-after="{{ $collapseAfter }}">
      {{ if $showThumbnails }}
        <li>
          <img src="{{ .JSON.String "thumbnail" }}" alt="" />
        </li>
      {{ end }}
      {{ if $showFlairs }}
        <li>
          <span class="flair">{{ .JSON.String "flair" }}</span>
        </li>
      {{ end }}
    </ul>

This way, you can optionally specify the collapse-after, show-thumbnails and show-flairs properties in the widget configuration:

- type: custom-api
  options:
    collapse-after: 5
    show-thumbnails: true
    show-flairs: false

Which allows you to reuse the same template multiple times with different options:

# Note that `custom-widgets` isn't a special property, it's just used to define the reusable "anchor", see https://support.atlassian.com/bitbucket-cloud/docs/yaml-anchors/
# The contents of the widget could also be included from its own file
custom-widgets:
  - &example-widget
    type: custom-api
    template: |
      {{ .Options.StringOr "custom-option" "not defined" }}

pages:
  - name: Home
    columns:
      - size: full
        widgets:
          - <<: *example-widget
            options:
              custom-option: "Value 1"

          - <<: *example-widget
            options:
              custom-option: "Value 2"

Currently, the available methods on the .Options object are: StringOr, IntOr, BoolOr and FloatOr.

[Reddit] Support for application auth

Instead of using a proxy to bypass Reddit's restrictions of VPS IPs, you can now use your own Reddit application credentials to authenticate with Reddit's API:

- type: reddit
  subreddit: technology
  app-auth:
    name: ${REDDIT_APP_NAME}
    id: ${REDDIT_APP_CLIENT_ID}
    secret: ${REDDIT_APP_SECRET}

To create an app on Reddit, go to this page.

(thanks @s0ders)

[RSS] Conditional requests

RSS feeds are usually the slowest widgets to load, leading to longer than necessary loading times. To help with this, the RSS widget now supports conditional requests, meaning it will use either the ETag or Last-Modified headers provided by the server, if available, to conditionally request the feed only if it has changed since the last request. This should speed things up in some cases and make sysadmins a little happier.

(thanks @charlesharries)

[Monitor] basic-auth and timeout properties

The monitor widget now has support for basic authentication and a timeout property:

- type: monitor
  cache: 1m
  title: Services
  sites:
    - title: Jellyfin
      url: https://jellyfin.yourdomain.com
      icon: si:jellyfin
      timeout: 10s

    - title: Gitea
      url: https://gitea.yourdomain.com
      icon: si:gitea
      basic-auth:
        username: ${GITEA_USERNAME}
        password: ${GITEA_PASSWORD}

(thanks @logoff, @ejsadiarin and @FabianK3)

[DNS Stats] Support for Technitium

The DNS stats widget now has support for Technitium DNS:

- type: dns-stats
  service: technitium
  token: ${TECHNITIUM_TOKEN}

(thanks @anarqist and @KevinFumbles)

Fixes
  • Fixed an issue with the weather widget where some locations were not being returned properly (thanks @MooToYou)
  • Fixed an issue with some widgets where user-provided URLs were being escaped when they shouldn't be (thanks @Kamalaja)
Thank you

Thank you to everyone who has continued to sponsor the project, and to those who have contributed in any way. I really appreciate it!

Merged PRs
New Contributors

Full Changelog: https://github.com/glanceapp/glance/compare/v0.7.13...v0.8.0

View originalPermalink
How v0.8.0 went

v0.7.13

Added 2
  • Added parseLocalTime function to Custom API, which works like parseTime but uses local timezone in the absence of a timezone instead of UTC
  • Added target property to Search widget, which is used when opening search results in a new tab
Fixed 2
  • Fixed being unable to retrieve temperature sensors in Server stats widget in some instances
  • Fixed URL not being correctly escaped in Reddit widget when using request-url-template
New
  • [Custom API] Added parseLocalTime function, which works like parseTime, however in the absence of a timezone it uses the local timezone instead of UTC.
  • [Search widget] Added target property which is used when opening the search results in a new tab (thanks @Jacksaur)
Fixes
  • [Server stats widget] Fixed being unable to retrieve temperature sensors in some instances (thanks @anxdpanic)
  • [Reddit widget] Fixed the URL not being correctly escaped when using request-url-template (thanks @yurigiu)
View originalPermalink
How v0.7.13 went

v0.7.12

Fixed 1
  • Server crash in some configurations when a widget is provided with a single resource to fetch instead of multiple

Fixed server crash in some configurations when a widget is provided with a single resource to fetch instead of multiple

View originalPermalink
How v0.7.12 went

v0.7.11

Added 2
  • Custom API widget added replaceMatches and unique functions
  • Added mountpoint:info CLI command
Changed 1
  • sensors:print CLI command now outputs more details
Fixed 4
  • RSS widget now filters out duplicate post links
  • Twitch channels widget now sorts live channels with 0 viewers correctly relative to offline channels
  • Server stats widget potential fix for missing mountpoint stats
  • Fixed Yahoo finance test request in diagnose CLI command
What's changed
  • [RSS widget] Duplicate post links will now be filtered out (thanks @HtFilia)
  • [Custom API widget] Added replaceMatches and unique functions (thanks @ralphocdol)
  • Added mountpoint:info CLI command
  • Added more details to the output of sensors:print CLI command
Fixes
  • [Twitch channels widget] Fixed live channels with 0 viewers getting incorrectly sorted lower than offline channels
  • [Server stats widget] Potential fix for missing mountpoint stats
  • Fixed the Yahoo finance test request in the diagnose CLI command
View originalPermalink
How v0.7.11 went

v0.7.10

Added 1
  • Added sensors:print CLI command that lists all temperature sensors that Glance has access to
Changed 1
  • Slightly increased the scale of everything on mobile to make things easier to tap on
What's Changed
  • Slightly increased the scale of everything on mobile to make things easier to tap on
  • Added sensors:print CLI command that lists all temperature sensors that Glance has access to
  • Bumped versions of dependencies
New Contributors

Full Changelog: https://github.com/glanceapp/glance/compare/v0.7.9...v0.7.10

View originalPermalink
How v0.7.10 went
View all

Discussion