diff --git a/.gitignore b/.gitignore
index f7e0f6c..e466992 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
/assets
/build
/playground
+/.idea
glance*.yml
diff --git a/Dockerfile b/Dockerfile
index e4019ba..b89541a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.22.5-alpine3.20 AS builder
+FROM golang:1.23.1-alpine3.20 AS builder
WORKDIR /app
COPY . /app
@@ -9,5 +9,8 @@ FROM alpine:3.20
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"]
+ENTRYPOINT ["/app/glance", "--config", "/app/config/glance.yml"]
diff --git a/Dockerfile.goreleaser b/Dockerfile.goreleaser
index dec9ac4..eaf8336 100644
--- a/Dockerfile.goreleaser
+++ b/Dockerfile.goreleaser
@@ -3,6 +3,8 @@ FROM alpine:3.20
WORKDIR /app
COPY glance .
-EXPOSE 8080/tcp
+HEALTHCHECK --timeout=10s --start-period=60s --interval=60s \
+ CMD wget --spider -q http://localhost:8080/api/healthz
-ENTRYPOINT ["/app/glance"]
+EXPOSE 8080/tcp
+ENTRYPOINT ["/app/glance", "--config", "/app/config/glance.yml"]
diff --git a/README.md b/README.md
index 0e8cfb4..da6fb58 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@
* Twitch channels & top games
* GitHub releases
* Repository overview
+* Docker containers
* Site monitor
* Search box
@@ -51,6 +52,8 @@ Checkout the [releases page](https://github.com/glanceapp/glance/releases) for a
```
#### Docker
+
+
> [!IMPORTANT]
>
> Make sure you have a valid `glance.yml` file in the same directory before running the container.
diff --git a/docs/configuration.md b/docs/configuration.md
index fd31f39..7bc11d8 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -15,6 +15,8 @@
- [Reddit](#reddit)
- [Search](#search-widget)
- [Group](#group)
+ - [Split Column](#split-column)
+ - [Custom API](#custom-api)
- [Extension](#extension)
- [Weather](#weather)
- [Monitor](#monitor)
@@ -30,8 +32,10 @@
- [Twitch Top Games](#twitch-top-games)
- [iframe](#iframe)
- [HTML](#html)
+ - [Docker](#docker)
## Intro
+
Configuration is done via a single YAML file and a server restart is required in order for any changes to take effect. Trying to start the server with an invalid config file will result in an error.
## Preconfigured page
@@ -111,6 +115,8 @@ This will give you a page that looks like the following:
Configure the widgets, add more of them, add extra pages, etc. Make it your own!
+
+
## Server
Server configuration is done through a top level `server` property. Example:
@@ -314,6 +320,7 @@ pages:
| width | string | no | |
| center-vertically | boolean | no | false |
| hide-desktop-navigation | boolean | no | false |
+| expand-mobile-page-navigation | boolean | no | false |
| show-mobile-header | boolean | no | false |
| columns | array | yes | |
@@ -340,6 +347,9 @@ When set to `true`, vertically centers the content on the page. Has no effect if
#### `hide-desktop-navigation`
Whether to show the navigation links at the top of the page on desktop.
+#### `expand-mobile-page-navigation`
+Whether the mobile page navigation should be expanded by default.
+
#### `show-mobile-header`
Whether to show a header displaying the name of the page on mobile. The header purposefully has a lot of vertical whitespace in order to push the content down and make it easier to reach on tall devices.
@@ -526,10 +536,22 @@ An array of RSS/atom feeds. The title can optionally be changed.
| hide-categories | boolean | no | false | Only applicable for `detailed-list` style |
| hide-description | boolean | no | false | Only applicable for `detailed-list` style |
| item-link-prefix | string | no | | |
+| headers | key (string) & value (string) | no | | |
###### `item-link-prefix`
If an RSS feed isn't returning item links with a base domain and Glance has failed to automatically detect the correct domain you can manually add a prefix to each link with this property.
+###### `headers`
+Optionally specify the headers that will be sent with the request. Example:
+
+```yaml
+- type: rss
+ feeds:
+ - url: https://domain.com/rss
+ headers:
+ User-Agent: Custom User Agent
+```
+
##### `limit`
The maximum number of articles to show.
@@ -561,12 +583,21 @@ Preview:
| channels | array | yes | |
| limit | integer | no | 25 |
| style | string | no | horizontal-cards |
+| collapse-after | integer | no | 7 |
| collapse-after-rows | integer | no | 4 |
| include-shorts | boolean | no | false |
| video-url-template | string | no | https://www.youtube.com/watch?v={VIDEO-ID} |
##### `channels`
-A list of channel IDs. One way of getting the ID of a channel is going to the channel's page and clicking on its description:
+A list of channel or playlist IDs. To specify a playlist, use the `playlist:` prefix like such:
+
+```yaml
+channels:
+ - playlist:PL8mG-RkN2uTyZZ00ObwZxxoG_nJbs3qec
+ - playlist:PL8mG-RkN2uTxTK4m_Vl2dYR9yE41kRdBg
+```
+
+One way of getting the ID of a channel is going to the channel's page and clicking on its description:

@@ -577,11 +608,18 @@ Then scroll down and click on "Share channel", then "Copy channel ID":
##### `limit`
The maximum number of videos to show.
+##### `collapse-after`
+Specify the number of videos to show when using the `vertical-list` style before the "SHOW MORE" button appears.
+
##### `collapse-after-rows`
Specify the number of rows to show when using the `grid-cards` style before the "SHOW MORE" button appears.
##### `style`
-Used to change the appearance of the widget. Possible values are `horizontal-cards` and `grid-cards`.
+Used to change the appearance of the widget. Possible values are `horizontal-cards`, `vertical-list` and `grid-cards`.
+
+Preview of `vertical-list`:
+
+
Preview of `grid-cards`:
@@ -716,6 +754,7 @@ Example:
| collapse-after | integer | no | 5 |
| comments-url-template | string | no | https://www.reddit.com/{POST-PATH} |
| request-url-template | string | no | |
+| proxy | string or multiple parameters | no | |
| sort-by | string | no | hot |
| top-period | string | no | day |
| search | string | no | |
@@ -777,7 +816,7 @@ r/selfhosted/comments/bsp01i/welcome_to_rselfhosted_please_read_this_first/
`{SUBREDDIT}` - the subreddit name
##### `request-url-template`
-A custom request url that will be used to fetch the data instead. This is useful when you're hosting Glance on a VPS and Reddit is blocking the requests, and you want to route it through an HTTP proxy.
+A custom request URL that will be used to fetch the data. This is useful when you're hosting Glance on a VPS where Reddit is blocking the requests and you want to route them through a proxy that accepts the URL as either a part of the path or a query parameter.
Placeholders:
@@ -788,6 +827,29 @@ https://proxy/{REQUEST-URL}
https://your.proxy/?url={REQUEST-URL}
```
+##### `proxy`
+A custom HTTP/HTTPS proxy URL that will be used to fetch the data. This is useful when you're hosting Glance on a VPS where Reddit is blocking the requests and you want to bypass the restriction by routing the requests through a proxy. Example:
+
+```yaml
+proxy: http://user:pass@proxy.com:8080
+proxy: https://user:pass@proxy.com:443
+```
+
+Alternatively, you can specify the proxy URL as well as additional options by using multiple parameters:
+
+```yaml
+proxy:
+ url: http://proxy.com:8080
+ allow-insecure: true
+ timeout: 10s
+```
+
+###### `allow-insecure`
+When set to `true`, allows the use of insecure connections such as when the proxy has a self-signed certificate.
+
+###### `timeout`
+The maximum time to wait for a response from the proxy. The value is a string and must be a number followed by one of s, m, h, d. Example: `10s` for 10 seconds, `1m` for 1 minute, etc
+
##### `sort-by`
Can be used to specify the order in which the posts should get returned. Possible values are `hot`, `new`, `top` and `rising`.
@@ -829,6 +891,7 @@ Preview:
| Enter | Perform search in the same tab | Search input is focused and not empty |
| Ctrl + Enter | Perform search in a new tab | Search input is focused and not empty |
| Escape | Leave focus | Search input is focused |
+| Up | Insert the last search query since the page was opened into the input field | Search input is focused |
> [!TIP]
>
@@ -840,6 +903,7 @@ Preview:
| search-engine | string | no | duckduckgo |
| new-tab | boolean | no | false |
| autofocus | boolean | no | false |
+| placeholder | string | no | Type here to search… |
| bangs | array | no | |
##### `search-engine`
@@ -856,6 +920,9 @@ When set to `true`, swaps the shortcuts for showing results in the same or new t
##### `autofocus`
When set to `true`, automatically focuses the search input on page load.
+##### `placeholder`
+When set, modifies the text displayed in the input field before typing.
+
##### `bangs`
What now? [Bangs](https://duckduckgo.com/bangs). They're shortcuts that allow you to use the same search box for many different sites. Assuming you have it configured, if for example you start your search input with `!yt` you'd be able to perform a search on YouTube:
@@ -891,7 +958,7 @@ url: https://www.amazon.com/s?k={QUERY}
```
### Group
-Group multiple widgets into one using tabs. Widgets are defined using a `widgets` property exactly as you would on a page column. The only limitation is that you cannot place a group widget within a group widget.
+Group multiple widgets into one using tabs. Widgets are defined using a `widgets` property exactly as you would on a page column. The only limitation is that you cannot place a group widget or a split column widget within a group widget.
Example:
@@ -934,6 +1001,67 @@ Example:
<<: *shared-properties
```
+### Split Column
+
+Splits a full sized column in half, allowing you to place widgets side by side. This is converted to a single column on mobile devices or if not enough width is available. Widgets are defined using a `widgets` property exactly as you would on a page column.
+
+Example of a full page with an effective 4 column layout using two split column widgets inside of two full sized columns:
+
+
+View config
+
+```yaml
+shared:
+ - &reddit-props
+ type: reddit
+ collapse-after: 4
+ show-thumbnails: true
+
+pages:
+ - name: Split Column Demo
+ width: wide
+ columns:
+ - size: full
+ widgets:
+ - type: split-column
+ widgets:
+ - subreddit: gaming
+ <<: *reddit-props
+ - subreddit: worldnews
+ <<: *reddit-props
+ - subreddit: lifeprotips
+ <<: *reddit-props
+ show-thumbnails: false
+ - subreddit: askreddit
+ <<: *reddit-props
+ show-thumbnails: false
+
+ - size: full
+ widgets:
+ - type: split-column
+ widgets:
+ - subreddit: todayilearned
+ <<: *reddit-props
+ collapse-after: 2
+ - subreddit: aww
+ <<: *reddit-props
+ - subreddit: science
+ <<: *reddit-props
+ - subreddit: showerthoughts
+ <<: *reddit-props
+ show-thumbnails: false
+```
+
+
+
+
+Preview:
+
+
+
+### Custom API
+
+
### Extension
Display a widget provided by an external source (3rd party). If you want to learn more about developing extensions, checkout the [extensions documentation](extensions.md) (WIP).
@@ -949,12 +1077,16 @@ Display a widget provided by an external source (3rd party). If you want to lear
| Name | Type | Required | Default |
| ---- | ---- | -------- | ------- |
| url | string | yes | |
+| fallback-content-type | string | no | |
| allow-potentially-dangerous-html | boolean | no | false |
| parameters | key & value | no | |
##### `url`
The URL of the extension. **Note that the query gets stripped from this URL and the one defined by `parameters` gets used instead.**
+##### `fallback-content-type`
+Optionally specify the fallback content type of the extension if the URL does not return a valid `Widget-Content-Type` header. Currently the only supported value for this property is `html`.
+
##### `allow-potentially-dangerous-html`
Whether to allow the extension to display HTML.
@@ -1066,11 +1198,19 @@ You can hover over the "ERROR" text to view more information.
| Name | Type | Required | Default |
| ---- | ---- | -------- | ------- |
| sites | array | yes | |
+| style | string | no | |
| show-failing-only | boolean | no | false |
##### `show-failing-only`
Shows only a list of failing sites when set to `true`.
+##### `style`
+Used to change the appearance of the widget. Possible values are `compact`.
+
+Preview of `compact`:
+
+
+
##### `sites`
Properties for each site:
@@ -1080,9 +1220,11 @@ Properties for each site:
| title | string | yes | |
| url | string | yes | |
| check-url | string | no | |
+| error-url | string | no | |
| icon | string | no | |
| allow-insecure | boolean | no | false |
| same-tab | boolean | no | false |
+| alt-status-codes | array | no | |
`title`
@@ -1096,9 +1238,13 @@ The public facing URL of a monitored service, the user will be redirected here.
The URL which will be requested and its response will determine the status of the site. If not specified, the `url` property is used.
+`error-url`
+
+If the monitored service returns an error, the user will be redirected here. If not specified, the `url` property is used.
+
`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:
+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
@@ -1108,7 +1254,7 @@ icon: si:adguard
> [!WARNING]
>
-> Simple Icons are loaded externally and are hosted on `cdnjs.cloudflare.com`, if you do not wish to depend on a 3rd party you are free to download the icons individually and host them locally.
+> 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.
`allow-insecure`
@@ -1118,6 +1264,15 @@ Whether to ignore invalid/self-signed certificates.
Whether to open the link in the same or a new tab.
+`alt-status-codes`
+
+Status codes other than 200 that you want to return "OK".
+
+```yaml
+alt-status-codes:
+ - 403
+```
+
### Releases
Display a list of latest releases for specific repositories on Github, GitLab, Codeberg or Docker Hub.
@@ -1160,7 +1315,7 @@ repositories:
- codeberg:redict/redict
```
-Official images on Docker Hub can be specified by ommiting the owner:
+Official images on Docker Hub can be specified by omitting the owner:
```yaml
repositories:
@@ -1169,7 +1324,7 @@ repositories:
- dockerhub:alpine
```
-You can also specify specific tags for Docker Hub images:
+You can also specify exact tags for Docker Hub images:
```yaml
repositories:
@@ -1239,15 +1394,21 @@ Preview:
| Name | Type | Required | Default |
| ---- | ---- | -------- | ------- |
| service | string | no | pihole |
+| allow-insecure | bool | no | false |
| url | string | yes | |
| username | string | when service is `adguard` | |
| password | string | when service is `adguard` | |
| token | string | when service is `pihole` | |
+| hide-graph | bool | no | false |
+| hide-top-domains | bool | no | false |
| hour-format | string | no | 12h |
##### `service`
Either `adguard` or `pihole`.
+##### `allow-insecure`
+Whether to allow invalid/self-signed certificates when making the request to the service.
+
##### `url`
The base URL of the service. Can be specified from an environment variable using the syntax `${VARIABLE_NAME}`.
@@ -1260,6 +1421,12 @@ Only required when using AdGuard Home. The password used to log into the admin d
##### `token`
Only required when using Pi-hole. The API token which can be found in `Settings -> API -> Show API token`. Can be specified from an environment variable using the syntax `${VARIABLE_NAME}`.
+##### `hide-graph`
+Whether to hide the graph showing the number of queries over time.
+
+##### `hide-top-domains`
+Whether to hide the list of top blocked domains.
+
##### `hour-format`
Whether to display the relative time in the graph in `12h` or `24h` format.
@@ -1364,6 +1531,13 @@ An array of groups which can optionally have a title and a custom color.
| title | string | no | |
| color | HSL | no | the primary color of the theme |
| links | array | yes | |
+| same-tab | boolean | no | false |
+| hide-arrow | boolean | no | false |
+| target | string | no | |
+
+> [!TIP]
+>
+> You can set `same-tab`, `hide-arrow` and `target` either on the group which will apply them to all links in that group, or on each individual link which will override the value set on the group.
###### Properties for each link
| Name | Type | Required | Default |
@@ -1373,10 +1547,11 @@ An array of groups which can optionally have a title and a custom color.
| icon | string | no | |
| same-tab | boolean | no | false |
| hide-arrow | boolean | no | false |
+| target | string | no | |
`icon`
-URL pointing to an image. You can also directly use [Simple Icons](https://simpleicons.org/) via a `si:` prefix:
+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
@@ -1386,7 +1561,7 @@ icon: si:reddit
> [!WARNING]
>
-> Simple Icons are loaded externally and are hosted on `cdnjs.cloudflare.com`, if you do not wish to depend on a 3rd party you are free to download the icons individually and host them locally.
+> 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`
@@ -1396,6 +1571,10 @@ Whether to open the link in the same tab or a new one.
Whether to hide the colored arrow on each link.
+`target`
+
+Set a custom value for the link's `target` attribute. Possible values are `_blank`, `_self`, `_parent` and `_top`, you can read more about what they do [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target). This property has precedence over `same-tab`.
+
### ChangeDetection.io
Display a list watches from changedetection.io.
@@ -1495,15 +1674,25 @@ Example:
```yaml
- type: calendar
+ start-sunday: false
```
Preview:

+#### Properties
+
+| Name | Type | Required | Default |
+| ---- | ---- | -------- | ------- |
+| start-sunday | boolean | no | false |
+
+##### `start-sunday`
+Whether calendar weeks start on Sunday or Monday.
+
> [!NOTE]
>
-> There is currently no customizability available for the calendar. Extra features will be added in the future.
+> There is currently little customizability available for the calendar. Extra features will be added in the future.
### Markets
Display a list of markets, their current value, change for the day and a small 21d chart. Data is taken from Yahoo Finance.
@@ -1535,14 +1724,30 @@ Preview:
| ---- | ---- | -------- |
| markets | array | yes |
| sort-by | string | no |
+| chart-link-template | string | no |
+| symbol-link-template | string | no |
##### `markets`
An array of markets for which to display information about.
##### `sort-by`
-By default the markets are displayed in the order they were defined. You can customize their ordering by setting the `sort-by` property to `absolute-change` for descending order based on the stock's absolute price change.
+By default the markets are displayed in the order they were defined. You can customize their ordering by setting the `sort-by` property to `change` for descending order based on the stock's percentage change (e.g. 1% would be sorted higher than -1%) or `absolute-change` for descending order based on the stock's absolute price change (e.g. -1% would be sorted higher than +0.5%).
-###### Properties for each stock
+##### `chart-link-template`
+A template for the link to go to when clicking on the chart that will be applied to all markets. The value `{SYMBOL}` will be replaced with the symbol of the market. You can override this on a per-market basis by specifying a `chart-link` property. Example:
+
+```yaml
+chart-link-template: https://www.tradingview.com/chart/?symbol={SYMBOL}
+```
+
+##### `symbol-link-template`
+A template for the link to go to when clicking on the symbol that will be applied to all markets. The value `{SYMBOL}` will be replaced with the symbol of the market. You can override this on a per-market basis by specifying a `symbol-link` property. Example:
+
+```yaml
+symbol-link-template: https://www.google.com/search?tbm=nws&q={SYMBOL}
+```
+
+###### Properties for each market
| Name | Type | Required |
| ---- | ---- | -------- |
| symbol | string | yes |
@@ -1559,9 +1764,11 @@ The symbol, as seen in Yahoo Finance.
The name that will be displayed under the symbol.
`symbol-link`
+
The link to go to when clicking on the symbol.
`chart-link`
+
The link to go to when clicking on the chart.
### Twitch Channels
@@ -1675,3 +1882,75 @@ Example:
```
Note the use of `|` after `source:`, this allows you to insert a multi-line string.
+
+### Docker Containers
+
+The Docker widget allows you to monitor your Docker containers.
+To enable this feature, ensure that your setup provides access to the **docker.sock** file (also you may use a TCP connection).
+
+Add the following to your `docker-compose` or `docker run` command to enable the Docker widget:
+
+**Docker Example:**
+```bash
+docker run -d -p 8080:8080 \
+ -v ./glance.yml:/app/glance.yml \
+ -v /etc/timezone:/etc/timezone:ro \
+ -v /etc/localtime:/etc/localtime:ro \
+ -v /var/run/docker.sock:/var/run/docker.sock:ro \
+ glanceapp/glance
+```
+
+**Docker Compose Example:**
+```yaml
+services:
+ glance:
+ image: glanceapp/glance
+ volumes:
+ - ./glance.yml:/app/glance.yml
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ ports:
+ - 8080:8080
+ restart: unless-stopped
+```
+
+#### Configuration
+To integrate the Docker widget into your dashboard, include the following snippet in your `glance.yml` file:
+
+```yaml
+- type: docker
+ host-url: tcp://localhost:2375
+ cache: 1m
+```
+
+#### Properties
+
+| Name | Type | Required | Default |
+| ---- | ---- | -------- | ------- |
+| host-url | string | no | `unix:///var/run/docker.sock` |
+
+#### Leveraging Container Labels
+You can use container labels to control visibility, URLs, icons, and titles within the Docker widget. Add the following labels to your container configuration for enhanced customization:
+
+```yaml
+labels:
+ - "glance.enable=true" # Enable or disable visibility of the container (default: true)
+ - "glance.title=Glance" # Optional friendly name (defaults to container name)
+ - "glance.url=https://app.example.com" # Optional URL associated with the container
+ - "glance.iconUrl=si:docker" # Optional URL to an image which will be used as the icon for the site
+
+```
+
+**Default Values:**
+
+| Name | Default |
+|----------------|------------|
+| glance.enable | true |
+| glance.title | Container name |
+| glance.url | (none) |
+| glance.iconUrl | si:docker |
+
+Preview:
+
+
diff --git a/docs/extensions.md b/docs/extensions.md
index 06db1ae..b1fa4fa 100644
--- a/docs/extensions.md
+++ b/docs/extensions.md
@@ -29,6 +29,9 @@ Used to specify the title of the widget. If not provided, the widget's title wil
### `Widget-Content-Type`
Used to specify the content type that will be returned by the extension. If not provided, the content will be shown as plain text.
+### `Widget-Content-Frameless`
+When set to `true`, the widget's content will be displayed without the default background or "frame".
+
## Content Types
> [!NOTE]
diff --git a/docs/images/docker-widget-preview.png b/docs/images/docker-widget-preview.png
new file mode 100644
index 0000000..5b644d4
Binary files /dev/null and b/docs/images/docker-widget-preview.png differ
diff --git a/docs/images/monitor-widget-compact-preview.png b/docs/images/monitor-widget-compact-preview.png
new file mode 100644
index 0000000..3e81fce
Binary files /dev/null and b/docs/images/monitor-widget-compact-preview.png differ
diff --git a/docs/images/split-column-widget-preview.png b/docs/images/split-column-widget-preview.png
new file mode 100644
index 0000000..f1931f8
Binary files /dev/null and b/docs/images/split-column-widget-preview.png differ
diff --git a/docs/images/videos-widget-vertical-list-preview.png b/docs/images/videos-widget-vertical-list-preview.png
new file mode 100644
index 0000000..e33ce86
Binary files /dev/null and b/docs/images/videos-widget-vertical-list-preview.png differ
diff --git a/go.mod b/go.mod
index 7034fe5..aa66fa8 100644
--- a/go.mod
+++ b/go.mod
@@ -1,19 +1,24 @@
module github.com/glanceapp/glance
-go 1.22.5
+go 1.23.1
require (
+ github.com/fsnotify/fsnotify v1.8.0
github.com/mmcdole/gofeed v1.3.0
+ github.com/tidwall/gjson v1.18.0
golang.org/x/text v0.21.0
gopkg.in/yaml.v3 v3.0.1
)
require (
- github.com/PuerkitoBio/goquery v1.9.2 // indirect
- github.com/andybalholm/cascadia v1.3.2 // indirect
+ github.com/PuerkitoBio/goquery v1.10.0 // indirect
+ github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mmcdole/goxpp v1.1.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/tidwall/match v1.1.1 // indirect
+ github.com/tidwall/pretty v1.2.1 // indirect
golang.org/x/net v0.33.0 // indirect
+ golang.org/x/sys v0.28.0 // indirect
)
diff --git a/go.sum b/go.sum
index 1f81f1c..7840f0b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,10 +1,15 @@
-github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
-github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
+github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
+github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
+github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
+github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
+github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
@@ -23,21 +28,45 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
+github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
+golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -45,21 +74,42 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
+golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
+golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
+golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/internal/assets/files.go b/internal/assets/files.go
deleted file mode 100644
index 2c7c09e..0000000
--- a/internal/assets/files.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package assets
-
-import (
- "crypto/md5"
- "embed"
- "encoding/hex"
- "io"
- "io/fs"
- "log/slog"
- "strconv"
- "time"
-)
-
-//go:embed static
-var _publicFS embed.FS
-
-//go:embed templates
-var _templateFS embed.FS
-
-var PublicFS, _ = fs.Sub(_publicFS, "static")
-var TemplateFS, _ = fs.Sub(_templateFS, "templates")
-
-func getFSHash(files fs.FS) string {
- hash := md5.New()
-
- err := fs.WalkDir(files, ".", func(path string, d fs.DirEntry, err error) error {
- if err != nil {
- return err
- }
-
- if d.IsDir() {
- return nil
- }
-
- file, err := files.Open(path)
-
- if err != nil {
- return err
- }
-
- if _, err := io.Copy(hash, file); err != nil {
- return err
- }
-
- return nil
- })
-
- if err == nil {
- return hex.EncodeToString(hash.Sum(nil))[:10]
- }
-
- slog.Warn("Could not compute assets cache", "err", err)
- return strconv.FormatInt(time.Now().Unix(), 10)
-}
-
-var PublicFSHash = getFSHash(PublicFS)
diff --git a/internal/assets/templates.go b/internal/assets/templates.go
deleted file mode 100644
index 85abb69..0000000
--- a/internal/assets/templates.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package assets
-
-import (
- "fmt"
- "html/template"
- "math"
- "strconv"
- "time"
-
- "golang.org/x/text/language"
- "golang.org/x/text/message"
-)
-
-var (
- PageTemplate = compileTemplate("page.html", "document.html", "page-style-overrides.gotmpl")
- PageContentTemplate = compileTemplate("content.html")
- CalendarTemplate = compileTemplate("calendar.html", "widget-base.html")
- ClockTemplate = compileTemplate("clock.html", "widget-base.html")
- BookmarksTemplate = compileTemplate("bookmarks.html", "widget-base.html")
- IFrameTemplate = compileTemplate("iframe.html", "widget-base.html")
- WeatherTemplate = compileTemplate("weather.html", "widget-base.html")
- ForumPostsTemplate = compileTemplate("forum-posts.html", "widget-base.html")
- RedditCardsHorizontalTemplate = compileTemplate("reddit-horizontal-cards.html", "widget-base.html")
- RedditCardsVerticalTemplate = compileTemplate("reddit-vertical-cards.html", "widget-base.html")
- ReleasesTemplate = compileTemplate("releases.html", "widget-base.html")
- ChangeDetectionTemplate = compileTemplate("change-detection.html", "widget-base.html")
- VideosTemplate = compileTemplate("videos.html", "widget-base.html", "video-card-contents.html")
- VideosGridTemplate = compileTemplate("videos-grid.html", "widget-base.html", "video-card-contents.html")
- MarketsTemplate = compileTemplate("markets.html", "widget-base.html")
- RSSListTemplate = compileTemplate("rss-list.html", "widget-base.html")
- RSSDetailedListTemplate = compileTemplate("rss-detailed-list.html", "widget-base.html")
- RSSHorizontalCardsTemplate = compileTemplate("rss-horizontal-cards.html", "widget-base.html")
- RSSHorizontalCards2Template = compileTemplate("rss-horizontal-cards-2.html", "widget-base.html")
- MonitorTemplate = compileTemplate("monitor.html", "widget-base.html")
- TwitchGamesListTemplate = compileTemplate("twitch-games-list.html", "widget-base.html")
- TwitchChannelsTemplate = compileTemplate("twitch-channels.html", "widget-base.html")
- RepositoryTemplate = compileTemplate("repository.html", "widget-base.html")
- SearchTemplate = compileTemplate("search.html", "widget-base.html")
- ExtensionTemplate = compileTemplate("extension.html", "widget-base.html")
- GroupTemplate = compileTemplate("group.html", "widget-base.html")
- DNSStatsTemplate = compileTemplate("dns-stats.html", "widget-base.html")
-)
-
-var globalTemplateFunctions = template.FuncMap{
- "relativeTime": relativeTimeSince,
- "formatViewerCount": formatViewerCount,
- "formatNumber": intl.Sprint,
- "absInt": func(i int) int {
- return int(math.Abs(float64(i)))
- },
- "formatPrice": func(price float64) string {
- return intl.Sprintf("%.2f", price)
- },
- "dynamicRelativeTimeAttrs": func(t time.Time) template.HTMLAttr {
- return template.HTMLAttr(fmt.Sprintf(`data-dynamic-relative-time="%d"`, t.Unix()))
- },
-}
-
-func compileTemplate(primary string, dependencies ...string) *template.Template {
- t, err := template.New(primary).
- Funcs(globalTemplateFunctions).
- ParseFS(TemplateFS, append([]string{primary}, dependencies...)...)
-
- if err != nil {
- panic(err)
- }
-
- return t
-}
-
-var intl = message.NewPrinter(language.English)
-
-func formatViewerCount(count int) string {
- if count < 1_000 {
- return strconv.Itoa(count)
- }
-
- if count < 10_000 {
- return fmt.Sprintf("%.1fk", float64(count)/1_000)
- }
-
- if count < 1_000_000 {
- return fmt.Sprintf("%dk", count/1_000)
- }
-
- return fmt.Sprintf("%.1fm", float64(count)/1_000_000)
-}
-
-func relativeTimeSince(t time.Time) string {
- delta := time.Since(t)
-
- if delta < time.Minute {
- return "1m"
- }
- if delta < time.Hour {
- return fmt.Sprintf("%dm", delta/time.Minute)
- }
- if delta < 24*time.Hour {
- return fmt.Sprintf("%dh", delta/time.Hour)
- }
- if delta < 30*24*time.Hour {
- return fmt.Sprintf("%dd", delta/(24*time.Hour))
- }
- if delta < 12*30*24*time.Hour {
- return fmt.Sprintf("%dmo", delta/(30*24*time.Hour))
- }
-
- return fmt.Sprintf("%dy", delta/(365*24*time.Hour))
-}
diff --git a/internal/assets/templates/extension.html b/internal/assets/templates/extension.html
deleted file mode 100644
index e5794c8..0000000
--- a/internal/assets/templates/extension.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{{ template "widget-base.html" . }}
-
-{{ define "widget-content" }}
-{{ .Extension.Content }}
-{{ end }}
diff --git a/internal/assets/templates/forum-posts.html b/internal/assets/templates/forum-posts.html
deleted file mode 100644
index 8a71d22..0000000
--- a/internal/assets/templates/forum-posts.html
+++ /dev/null
@@ -1,49 +0,0 @@
-{{ template "widget-base.html" . }}
-
-{{ define "widget-content" }}
-
- {{ range .Posts }}
-
-
- {{ if $.ShowThumbnails }}
- {{ if .IsCrosspost }}
-
- {{ else if ne .ThumbnailUrl "" }}
-
- {{ else if .HasTargetUrl }}
-
- {{ else }}
-
- {{ end }}
- {{ end }}
-
+{{ end }}
+{{ end }}
diff --git a/internal/assets/templates/monitor.html b/internal/glance/templates/monitor.html
similarity index 62%
rename from internal/assets/templates/monitor.html
rename to internal/glance/templates/monitor.html
index b19f0e2..7e95b99 100644
--- a/internal/assets/templates/monitor.html
+++ b/internal/glance/templates/monitor.html
@@ -21,11 +21,11 @@
{{ end }}
{{ define "site" }}
-{{ if .IconUrl }}
-
+{{ if .Icon.URL }}
+
{{ end }}
{{ end }}
diff --git a/internal/assets/templates/content.html b/internal/glance/templates/page-content.html
similarity index 100%
rename from internal/assets/templates/content.html
rename to internal/glance/templates/page-content.html
diff --git a/internal/assets/templates/page.html b/internal/glance/templates/page.html
similarity index 89%
rename from internal/assets/templates/page.html
rename to internal/glance/templates/page.html
index d2cee76..e740d03 100644
--- a/internal/assets/templates/page.html
+++ b/internal/glance/templates/page.html
@@ -14,10 +14,13 @@
{{ define "document-root-attrs" }}class="{{ if .App.Config.Theme.Light }}light-scheme {{ end }}{{ if ne "" .Page.Width }}page-width-{{ .Page.Width }} {{ end }}{{ if .Page.CenterVertically }}page-center-vertically{{ end }}"{{ end }}
{{ define "document-head-after" }}
-{{ template "page-style-overrides.gotmpl" . }}
+{{ .App.ParsedThemeStyle }}
+
{{ if ne "" .App.Config.Theme.CustomCSSFile }}
{{ end }}
+
+{{ if ne "" .App.Config.Document.Head }}{{ .App.Config.Document.Head }}{{ end }}
{{ end }}
{{ define "navigation-links" }}
@@ -27,7 +30,7 @@
{{ end }}
{{ define "document-body" }}
-
+
{{ if not .Page.HideDesktopNavigation }}
@@ -44,9 +47,9 @@
↑
{{ range $i, $column := .Page.Columns }}
-
+
{{ end }}
-
+
{{ template "navigation-links" . }}
diff --git a/internal/assets/templates/reddit-horizontal-cards.html b/internal/glance/templates/reddit-horizontal-cards.html
similarity index 81%
rename from internal/assets/templates/reddit-horizontal-cards.html
rename to internal/glance/templates/reddit-horizontal-cards.html
index 2012435..9ee31b3 100644
--- a/internal/assets/templates/reddit-horizontal-cards.html
+++ b/internal/glance/templates/reddit-horizontal-cards.html
@@ -18,10 +18,10 @@
{{ else }}
+{{ end }}
+
+{{ end }}
diff --git a/internal/assets/templates/rss-detailed-list.html b/internal/glance/templates/rss-detailed-list.html
similarity index 100%
rename from internal/assets/templates/rss-detailed-list.html
rename to internal/glance/templates/rss-detailed-list.html
diff --git a/internal/assets/templates/rss-horizontal-cards-2.html b/internal/glance/templates/rss-horizontal-cards-2.html
similarity index 91%
rename from internal/assets/templates/rss-horizontal-cards-2.html
rename to internal/glance/templates/rss-horizontal-cards-2.html
index 0404fce..496e56a 100644
--- a/internal/assets/templates/rss-horizontal-cards-2.html
+++ b/internal/glance/templates/rss-horizontal-cards-2.html
@@ -16,7 +16,7 @@
{{ end }}
+ The default location of glance.yml in the Docker image has
+ changed since v0.7.0, please see the migration guide
+ for instructions or visit the release notes
+ to find out more about why this change was necessary. Sorry for the inconvenience.
+
+
+
Migration should take around 5 minutes.
+
+
+
+
+
diff --git a/internal/assets/templates/video-card-contents.html b/internal/glance/templates/video-card-contents.html
similarity index 78%
rename from internal/assets/templates/video-card-contents.html
rename to internal/glance/templates/video-card-contents.html
index 375fd08..c6340c5 100644
--- a/internal/assets/templates/video-card-contents.html
+++ b/internal/glance/templates/video-card-contents.html
@@ -1,7 +1,7 @@
{{ define "video-card-contents" }}
+{{- end }}
diff --git a/internal/assets/templates/videos.html b/internal/glance/templates/videos.html
similarity index 100%
rename from internal/assets/templates/videos.html
rename to internal/glance/templates/videos.html
diff --git a/internal/assets/templates/weather.html b/internal/glance/templates/weather.html
similarity index 100%
rename from internal/assets/templates/weather.html
rename to internal/glance/templates/weather.html
diff --git a/internal/assets/templates/widget-base.html b/internal/glance/templates/widget-base.html
similarity index 60%
rename from internal/assets/templates/widget-base.html
rename to internal/glance/templates/widget-base.html
index bdd30b9..0a8e3f2 100644
--- a/internal/assets/templates/widget-base.html
+++ b/internal/glance/templates/widget-base.html
@@ -1,7 +1,7 @@