Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea04aec0b4 | ||
|
|
b8e2717faf | ||
|
|
502950709c | ||
|
|
2c3e14d84c | ||
|
|
8e71afe158 | ||
|
|
03b616622e | ||
|
|
50e1da01fe | ||
|
|
f8f50b26d8 | ||
|
|
a3bc133bcb | ||
|
|
50dae22ff4 | ||
|
|
5a91154eab | ||
|
|
d5b89d512a | ||
|
|
97f43f88eb | ||
|
|
3177af9524 | ||
|
|
f99d22738c | ||
|
|
ea7124db52 | ||
|
|
b1247e5de6 | ||
|
|
5ab962634e | ||
|
|
d99944dcff | ||
|
|
920bcb06dc | ||
|
|
3679cc21ab | ||
|
|
07e5626469 | ||
|
|
6bb6f87800 | ||
|
|
cb6b52ff4c | ||
|
|
03c377dff3 | ||
|
|
0e3f172032 | ||
|
|
fd704419d9 | ||
|
|
32ac72f592 | ||
|
|
9c77b87435 | ||
|
|
d4d9e94f44 | ||
|
|
47cef2abf0 | ||
|
|
0a5cdf380f | ||
|
|
75555f0426 | ||
|
|
1483b355af | ||
|
|
6a5bb635bb | ||
|
|
0e0aca3844 |
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.23.6-alpine3.21 AS builder
|
FROM golang:1.24.2-alpine3.21 AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . /app
|
COPY . /app
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<p align="center"><em>What if you could see everything at a...</em></p>
|
<p align="center"><em>What if you could see everything at a...</em></p>
|
||||||
<h1 align="center">Glance</h1>
|
<h1 align="center">Glance</h1>
|
||||||
<p align="center"><a href="#installation">Install</a> • <a href="docs/configuration.md">Configuration</a> • <a href="docs/preconfigured-pages.md">Preconfigured pages</a> • <a href="docs/themes.md">Themes</a> • <a href="https://discord.com/invite/7KQ7Xa9kJd">Discord</a></p>
|
<p align="center"><a href="#installation">Install</a> • <a href="docs/configuration.md">Configuration</a> • <a href="https://discord.com/invite/7KQ7Xa9kJd">Discord</a> • <a href="https://github.com/sponsors/glanceapp">Sponsor</a></p>
|
||||||
|
<p align="center"><a href="https://github.com/glanceapp/community-widgets">Community widgets</a> • <a href="docs/preconfigured-pages.md">Preconfigured pages</a> • <a href="docs/themes.md">Themes</a></p>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -194,6 +195,7 @@ services:
|
|||||||
glance:
|
glance:
|
||||||
container_name: glance
|
container_name: glance
|
||||||
image: glanceapp/glance
|
image: glanceapp/glance
|
||||||
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ./config:/app/config
|
- ./config:/app/config
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -958,6 +958,7 @@ Preview:
|
|||||||
| search-engine | string | no | duckduckgo |
|
| search-engine | string | no | duckduckgo |
|
||||||
| new-tab | boolean | no | false |
|
| new-tab | boolean | no | false |
|
||||||
| autofocus | boolean | no | false |
|
| autofocus | boolean | no | false |
|
||||||
|
| target | string | no | _blank |
|
||||||
| placeholder | string | no | Type here to search… |
|
| placeholder | string | no | Type here to search… |
|
||||||
| bangs | array | no | |
|
| bangs | array | no | |
|
||||||
|
|
||||||
@@ -975,6 +976,9 @@ When set to `true`, swaps the shortcuts for showing results in the same or new t
|
|||||||
##### `autofocus`
|
##### `autofocus`
|
||||||
When set to `true`, automatically focuses the search input on page load.
|
When set to `true`, automatically focuses the search input on page load.
|
||||||
|
|
||||||
|
##### `target`
|
||||||
|
The target to use when opening the search results in a new tab. Possible values are `_blank`, `_self`, `_parent` and `_top`.
|
||||||
|
|
||||||
##### `placeholder`
|
##### `placeholder`
|
||||||
When set, modifies the text displayed in the input field before typing.
|
When set, modifies the text displayed in the input field before typing.
|
||||||
|
|
||||||
@@ -1950,7 +1954,7 @@ Whether to hide the swap usage.
|
|||||||
| Name | Type | Required | Default |
|
| Name | Type | Required | Default |
|
||||||
| ---- | ---- | -------- | ------- |
|
| ---- | ---- | -------- | ------- |
|
||||||
| cpu-temp-sensor | string | no | |
|
| cpu-temp-sensor | string | no | |
|
||||||
| hide-mointpoints-by-default | boolean | no | false |
|
| hide-mountpoints-by-default | boolean | no | false |
|
||||||
| mountpoints | map\[string\]object | no | |
|
| mountpoints | map\[string\]object | no | |
|
||||||
|
|
||||||
###### `cpu-temp-sensor`
|
###### `cpu-temp-sensor`
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ The following helper functions provided by Glance are available:
|
|||||||
- `offsetNow(offset string) time.Time`: Returns the current time with an offset. The offset can be positive or negative and must be in the format "3h" "-1h" or "2h30m10s".
|
- `offsetNow(offset string) time.Time`: Returns the current time with an offset. The offset can be positive or negative and must be in the format "3h" "-1h" or "2h30m10s".
|
||||||
- `duration(str string) time.Duration`: Parses a string such as `1h`, `24h`, `5h30m`, etc into a `time.Duration`.
|
- `duration(str string) time.Duration`: Parses a string such as `1h`, `24h`, `5h30m`, etc into a `time.Duration`.
|
||||||
- `parseTime(layout string, s string) time.Time`: Parses a string into time.Time. The layout must be provided in Go's [date format](https://pkg.go.dev/time#pkg-constants). You can alternatively use these values instead of the literal format: "unix", "RFC3339", "RFC3339Nano", "DateTime", "DateOnly".
|
- `parseTime(layout string, s string) time.Time`: Parses a string into time.Time. The layout must be provided in Go's [date format](https://pkg.go.dev/time#pkg-constants). You can alternatively use these values instead of the literal format: "unix", "RFC3339", "RFC3339Nano", "DateTime", "DateOnly".
|
||||||
|
- `parseLocalTime(layout string, s string) time.Time`: Same as the above, except in the absence of a timezone, it will use the local timezone instead of UTC.
|
||||||
- `parseRelativeTime(layout string, s string) time.Time`: A shorthand for `{{ .String "date" | parseTime "rfc3339" | toRelativeTime }}`.
|
- `parseRelativeTime(layout string, s string) time.Time`: A shorthand for `{{ .String "date" | parseTime "rfc3339" | toRelativeTime }}`.
|
||||||
- `add(a, b float) float`: Adds two numbers.
|
- `add(a, b float) float`: Adds two numbers.
|
||||||
- `sub(a, b float) float`: Subtracts two numbers.
|
- `sub(a, b float) float`: Subtracts two numbers.
|
||||||
@@ -389,6 +390,7 @@ The following helper functions provided by Glance are available:
|
|||||||
- `trimSuffix(suffix string, str string) string`: Trims the suffix from a string.
|
- `trimSuffix(suffix string, str string) string`: Trims the suffix from a string.
|
||||||
- `trimSpace(str string) string`: Trims whitespace from a string on both ends.
|
- `trimSpace(str string) string`: Trims whitespace from a string on both ends.
|
||||||
- `replaceAll(old string, new string, str string) string`: Replaces all occurrences of a string in a string.
|
- `replaceAll(old string, new string, str string) string`: Replaces all occurrences of a string in a string.
|
||||||
|
- `replaceMatches(pattern string, replacement string, str string) string`: Replaces all occurrences of a regular expression in a string.
|
||||||
- `findMatch(pattern string, str string) string`: Finds the first match of a regular expression in a string.
|
- `findMatch(pattern string, str string) string`: Finds the first match of a regular expression in a string.
|
||||||
- `findSubmatch(pattern string, str string) string`: Finds the first submatch of a regular expression in a string.
|
- `findSubmatch(pattern string, str string) string`: Finds the first submatch of a regular expression in a string.
|
||||||
- `sortByString(key string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a string key in either ascending or descending order.
|
- `sortByString(key string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a string key in either ascending or descending order.
|
||||||
@@ -396,6 +398,7 @@ The following helper functions provided by Glance are available:
|
|||||||
- `sortByFloat(key string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a float key in either ascending or descending order.
|
- `sortByFloat(key string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a float key in either ascending or descending order.
|
||||||
- `sortByTime(key string, layout string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a time key in either ascending or descending order. The format must be provided in Go's [date format](https://pkg.go.dev/time#pkg-constants).
|
- `sortByTime(key string, layout string, order string, arr []JSON): []JSON`: Sorts an array of JSON objects by a time key in either ascending or descending order. The format must be provided in Go's [date format](https://pkg.go.dev/time#pkg-constants).
|
||||||
- `concat(strings ...string) string`: Concatenates multiple strings together.
|
- `concat(strings ...string) string`: Concatenates multiple strings together.
|
||||||
|
- `unique(key string, arr []JSON) []JSON`: Returns a unique array of JSON objects based on the given key.
|
||||||
|
|
||||||
The following helper functions provided by Go's `text/template` are available:
|
The following helper functions provided by Go's `text/template` are available:
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -82,6 +82,17 @@ theme:
|
|||||||
negative-color: 209 88 54
|
negative-color: 209 88 54
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Dracula
|
||||||
|

|
||||||
|
```yaml
|
||||||
|
theme:
|
||||||
|
background-color: 231 15 21
|
||||||
|
primary-color: 265 89 79
|
||||||
|
contrast-multiplier: 1.2
|
||||||
|
positive-color: 135 94 66
|
||||||
|
negative-color: 0 100 67
|
||||||
|
```
|
||||||
|
|
||||||
## Light
|
## Light
|
||||||
|
|
||||||
### Catppuccin Latte
|
### Catppuccin Latte
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
module github.com/glanceapp/glance
|
module github.com/glanceapp/glance
|
||||||
|
|
||||||
go 1.23.6
|
go 1.24.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fsnotify/fsnotify v1.8.0
|
github.com/fsnotify/fsnotify v1.9.0
|
||||||
github.com/mmcdole/gofeed v1.3.0
|
github.com/mmcdole/gofeed v1.3.0
|
||||||
github.com/shirou/gopsutil/v4 v4.25.1
|
github.com/shirou/gopsutil/v4 v4.25.3
|
||||||
github.com/tidwall/gjson v1.18.0
|
github.com/tidwall/gjson v1.18.0
|
||||||
golang.org/x/text v0.22.0
|
golang.org/x/text v0.24.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.10.1 // indirect
|
github.com/PuerkitoBio/goquery v1.10.2 // indirect
|
||||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||||
github.com/ebitengine/purego v0.8.2 // indirect
|
github.com/ebitengine/purego v0.8.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
|
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
||||||
github.com/mmcdole/goxpp v1.1.1 // indirect
|
github.com/mmcdole/goxpp v1.1.1 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||||
github.com/tklauser/numcpus v0.9.0 // indirect
|
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
golang.org/x/net v0.34.0 // indirect
|
golang.org/x/net v0.39.0 // indirect
|
||||||
golang.org/x/sys v0.30.0 // indirect
|
golang.org/x/sys v0.32.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU=
|
github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU=
|
||||||
github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY=
|
github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY=
|
||||||
|
github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8=
|
||||||
|
github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU=
|
||||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
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/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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -9,16 +11,21 @@ github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z
|
|||||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
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/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
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 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
|
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
|
||||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||||
github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
|
github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
|
||||||
github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE=
|
github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE=
|
||||||
github.com/mmcdole/goxpp v1.1.1 h1:RGIX+D6iQRIunGHrKqnA2+700XMCnNv0bAOOv5MUhx8=
|
github.com/mmcdole/goxpp v1.1.1 h1:RGIX+D6iQRIunGHrKqnA2+700XMCnNv0bAOOv5MUhx8=
|
||||||
@@ -34,6 +41,8 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
|||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
|
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
@@ -47,8 +56,12 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
|||||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||||
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||||
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
|
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
|
||||||
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
|
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
|
||||||
|
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||||
|
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
@@ -74,6 +87,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
|||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||||
|
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||||
|
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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.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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -97,6 +112,8 @@ golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
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-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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -117,6 +134,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
|
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||||
|
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
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.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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
|||||||
+82
-5
@@ -5,23 +5,39 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/shirou/gopsutil/v4/disk"
|
||||||
|
"github.com/shirou/gopsutil/v4/sensors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cliIntent uint8
|
type cliIntent uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
cliIntentServe cliIntent = iota
|
cliIntentVersionPrint cliIntent = iota
|
||||||
cliIntentConfigValidate = iota
|
cliIntentServe
|
||||||
cliIntentConfigPrint = iota
|
cliIntentConfigValidate
|
||||||
cliIntentDiagnose = iota
|
cliIntentConfigPrint
|
||||||
|
cliIntentDiagnose
|
||||||
|
cliIntentSensorsPrint
|
||||||
|
cliIntentMountpointInfo
|
||||||
)
|
)
|
||||||
|
|
||||||
type cliOptions struct {
|
type cliOptions struct {
|
||||||
intent cliIntent
|
intent cliIntent
|
||||||
configPath string
|
configPath string
|
||||||
|
args []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCliOptions() (*cliOptions, error) {
|
func parseCliOptions() (*cliOptions, error) {
|
||||||
|
var args []string
|
||||||
|
|
||||||
|
args = os.Args[1:]
|
||||||
|
if len(args) == 1 && (args[0] == "--version" || args[0] == "-v" || args[0] == "version") {
|
||||||
|
return &cliOptions{
|
||||||
|
intent: cliIntentVersionPrint,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
flags := flag.NewFlagSet("", flag.ExitOnError)
|
flags := flag.NewFlagSet("", flag.ExitOnError)
|
||||||
flags.Usage = func() {
|
flags.Usage = func() {
|
||||||
fmt.Println("Usage: glance [options] command")
|
fmt.Println("Usage: glance [options] command")
|
||||||
@@ -32,6 +48,8 @@ func parseCliOptions() (*cliOptions, error) {
|
|||||||
fmt.Println("\nCommands:")
|
fmt.Println("\nCommands:")
|
||||||
fmt.Println(" config:validate Validate the config file")
|
fmt.Println(" config:validate Validate the config file")
|
||||||
fmt.Println(" config:print Print the parsed config file with embedded includes")
|
fmt.Println(" config:print Print the parsed config file with embedded includes")
|
||||||
|
fmt.Println(" sensors:print List all sensors")
|
||||||
|
fmt.Println(" mountpoint:info Print information about a given mountpoint path")
|
||||||
fmt.Println(" diagnose Run diagnostic checks")
|
fmt.Println(" diagnose Run diagnostic checks")
|
||||||
}
|
}
|
||||||
configPath := flags.String("config", "glance.yml", "Set config path")
|
configPath := flags.String("config", "glance.yml", "Set config path")
|
||||||
@@ -41,7 +59,7 @@ func parseCliOptions() (*cliOptions, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var intent cliIntent
|
var intent cliIntent
|
||||||
var args = flags.Args()
|
args = flags.Args()
|
||||||
unknownCommandErr := fmt.Errorf("unknown command: %s", strings.Join(args, " "))
|
unknownCommandErr := fmt.Errorf("unknown command: %s", strings.Join(args, " "))
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
@@ -51,11 +69,19 @@ func parseCliOptions() (*cliOptions, error) {
|
|||||||
intent = cliIntentConfigValidate
|
intent = cliIntentConfigValidate
|
||||||
} else if args[0] == "config:print" {
|
} else if args[0] == "config:print" {
|
||||||
intent = cliIntentConfigPrint
|
intent = cliIntentConfigPrint
|
||||||
|
} else if args[0] == "sensors:print" {
|
||||||
|
intent = cliIntentSensorsPrint
|
||||||
} else if args[0] == "diagnose" {
|
} else if args[0] == "diagnose" {
|
||||||
intent = cliIntentDiagnose
|
intent = cliIntentDiagnose
|
||||||
} else {
|
} else {
|
||||||
return nil, unknownCommandErr
|
return nil, unknownCommandErr
|
||||||
}
|
}
|
||||||
|
} else if len(args) == 2 {
|
||||||
|
if args[0] == "mountpoint:info" {
|
||||||
|
intent = cliIntentMountpointInfo
|
||||||
|
} else {
|
||||||
|
return nil, unknownCommandErr
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, unknownCommandErr
|
return nil, unknownCommandErr
|
||||||
}
|
}
|
||||||
@@ -63,5 +89,56 @@ func parseCliOptions() (*cliOptions, error) {
|
|||||||
return &cliOptions{
|
return &cliOptions{
|
||||||
intent: intent,
|
intent: intent,
|
||||||
configPath: *configPath,
|
configPath: *configPath,
|
||||||
|
args: args,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cliSensorsPrint() int {
|
||||||
|
tempSensors, err := sensors.SensorsTemperatures()
|
||||||
|
if err != nil {
|
||||||
|
if warns, ok := err.(*sensors.Warnings); ok {
|
||||||
|
fmt.Printf("Could not retrieve information for some sensors (%v):\n", err)
|
||||||
|
for _, w := range warns.List {
|
||||||
|
fmt.Printf(" - %v\n", w)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Failed to retrieve sensor information: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tempSensors) == 0 {
|
||||||
|
fmt.Println("No sensors found")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Sensors found:")
|
||||||
|
for _, sensor := range tempSensors {
|
||||||
|
fmt.Printf(" %s: %.1f°C\n", sensor.SensorKey, sensor.Temperature)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func cliMountpointInfo(requestedPath string) int {
|
||||||
|
usage, err := disk.Usage(requestedPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to retrieve info for path %s: %v\n", requestedPath, err)
|
||||||
|
if warns, ok := err.(*disk.Warnings); ok {
|
||||||
|
for _, w := range warns.List {
|
||||||
|
fmt.Printf(" - %v\n", w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Path:", usage.Path)
|
||||||
|
fmt.Println("FS type:", ternary(usage.Fstype == "", "unknown", usage.Fstype))
|
||||||
|
fmt.Printf("Used percent: %.1f%%\n", usage.UsedPercent)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ var diagnosticSteps = []diagnosticStep{
|
|||||||
{
|
{
|
||||||
name: "fetch data from Yahoo finance API",
|
name: "fetch data from Yahoo finance API",
|
||||||
fn: func() (string, error) {
|
fn: func() (string, error) {
|
||||||
return testHttpRequest("GET", "https://query1.finance.yahoo.com/v8/finance/chart/NVDA", 200)
|
return testHttpRequestWithHeaders("GET", "https://query1.finance.yahoo.com/v8/finance/chart/NVDA", map[string]string{
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0",
|
||||||
|
}, 200)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ func Main() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch options.intent {
|
switch options.intent {
|
||||||
|
case cliIntentVersionPrint:
|
||||||
|
fmt.Println(buildVersion)
|
||||||
case cliIntentServe:
|
case cliIntentServe:
|
||||||
// remove in v0.10.0
|
// remove in v0.10.0
|
||||||
if serveUpdateNoticeIfConfigLocationNotMigrated(options.configPath) {
|
if serveUpdateNoticeIfConfigLocationNotMigrated(options.configPath) {
|
||||||
@@ -47,6 +49,10 @@ func Main() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(string(contents))
|
fmt.Println(string(contents))
|
||||||
|
case cliIntentSensorsPrint:
|
||||||
|
return cliSensorsPrint()
|
||||||
|
case cliIntentMountpointInfo:
|
||||||
|
return cliMountpointInfo(options.args[1])
|
||||||
case cliIntentDiagnose:
|
case cliIntentDiagnose:
|
||||||
runDiagnostic()
|
runDiagnostic()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ function setupSearchBoxes() {
|
|||||||
for (let i = 0; i < searchWidgets.length; i++) {
|
for (let i = 0; i < searchWidgets.length; i++) {
|
||||||
const widget = searchWidgets[i];
|
const widget = searchWidgets[i];
|
||||||
const defaultSearchUrl = widget.dataset.defaultSearchUrl;
|
const defaultSearchUrl = widget.dataset.defaultSearchUrl;
|
||||||
|
const target = widget.dataset.target || "_blank";
|
||||||
const newTab = widget.dataset.newTab === "true";
|
const newTab = widget.dataset.newTab === "true";
|
||||||
const inputElement = widget.getElementsByClassName("search-input")[0];
|
const inputElement = widget.getElementsByClassName("search-input")[0];
|
||||||
const bangElement = widget.getElementsByClassName("search-bang")[0];
|
const bangElement = widget.getElementsByClassName("search-bang")[0];
|
||||||
@@ -143,7 +144,7 @@ function setupSearchBoxes() {
|
|||||||
const url = searchUrlTemplate.replace("!QUERY!", encodeURIComponent(query));
|
const url = searchUrlTemplate.replace("!QUERY!", encodeURIComponent(query));
|
||||||
|
|
||||||
if (newTab && !event.ctrlKey || !newTab && event.ctrlKey) {
|
if (newTab && !event.ctrlKey || !newTab && event.ctrlKey) {
|
||||||
window.open(url, '_blank').focus();
|
window.open(url, target).focus();
|
||||||
} else {
|
} else {
|
||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
}
|
}
|
||||||
@@ -649,7 +650,7 @@ function setupTruncatedElementTitles() {
|
|||||||
|
|
||||||
for (let i = 0; i < elements.length; i++) {
|
for (let i = 0; i < elements.length; i++) {
|
||||||
const element = elements[i];
|
const element = elements[i];
|
||||||
if (element.title === "") element.title = element.textContent;
|
if (element.getAttribute("title") === null) element.title = element.textContent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1985,7 +1985,7 @@ details[open] .summary::after {
|
|||||||
|
|
||||||
@media (max-width: 550px) {
|
@media (max-width: 550px) {
|
||||||
:root {
|
:root {
|
||||||
font-size: 9px;
|
font-size: 9.4px;
|
||||||
--widget-gap: 15px;
|
--widget-gap: 15px;
|
||||||
--widget-content-vertical-padding: 10px;
|
--widget-content-vertical-padding: 10px;
|
||||||
--widget-content-horizontal-padding: 10px;
|
--widget-content-horizontal-padding: 10px;
|
||||||
@@ -2038,6 +2038,7 @@ details[open] .summary::after {
|
|||||||
.color-primary { color: var(--color-primary); }
|
.color-primary { color: var(--color-primary); }
|
||||||
|
|
||||||
.cursor-help { cursor: help; }
|
.cursor-help { cursor: help; }
|
||||||
|
.rounded { border-radius: var(--border-radius); }
|
||||||
.break-all { word-break: break-all; }
|
.break-all { word-break: break-all; }
|
||||||
.text-left { text-align: left; }
|
.text-left { text-align: left; }
|
||||||
.text-right { text-align: right; }
|
.text-right { text-align: right; }
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="min-width-0">
|
<div class="min-width-0 grow">
|
||||||
{{- if .URL }}
|
{{- if .URL }}
|
||||||
<a href="{{ .URL | safeURL }}" class="color-highlight size-title-dynamic block text-truncate" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
|
<a href="{{ .URL | safeURL }}" class="color-highlight size-title-dynamic block text-truncate" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
|
||||||
{{- else }}
|
{{- else }}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{{ define "widget-content-classes" }}widget-content-frameless{{ end }}
|
{{ define "widget-content-classes" }}widget-content-frameless{{ end }}
|
||||||
|
|
||||||
{{ define "widget-content" }}
|
{{ define "widget-content" }}
|
||||||
<div class="search widget-content-frame padding-inline-widget flex gap-15 items-center" data-default-search-url="{{ .SearchEngine }}" data-new-tab="{{ .NewTab }}">
|
<div class="search widget-content-frame padding-inline-widget flex gap-15 items-center" data-default-search-url="{{ .SearchEngine }}" data-new-tab="{{ .NewTab }}" data-target="{{ .Target }}">
|
||||||
<div class="search-bangs">
|
<div class="search-bangs">
|
||||||
{{ range .Bangs }}
|
{{ range .Bangs }}
|
||||||
<input type="hidden" data-shortcut="{{ .Shortcut }}" data-title="{{ .Title }}" data-url="{{ .URL }}">
|
<input type="hidden" data-shortcut="{{ .Shortcut }}" data-title="{{ .Title }}" data-url="{{ .URL }}">
|
||||||
|
|||||||
@@ -450,11 +450,16 @@ var customAPITemplateFuncs = func() template.FuncMap {
|
|||||||
|
|
||||||
return d
|
return d
|
||||||
},
|
},
|
||||||
"parseTime": customAPIFuncParseTime,
|
"parseTime": func(layout, value string) time.Time {
|
||||||
|
return customAPIFuncParseTimeInLocation(layout, value, time.UTC)
|
||||||
|
},
|
||||||
|
"parseLocalTime": func(layout, value string) time.Time {
|
||||||
|
return customAPIFuncParseTimeInLocation(layout, value, time.Local)
|
||||||
|
},
|
||||||
"toRelativeTime": dynamicRelativeTimeAttrs,
|
"toRelativeTime": dynamicRelativeTimeAttrs,
|
||||||
"parseRelativeTime": func(layout, value string) template.HTMLAttr {
|
"parseRelativeTime": func(layout, value string) template.HTMLAttr {
|
||||||
// Shorthand to do both of the above with a single function call
|
// Shorthand to do both of the above with a single function call
|
||||||
return dynamicRelativeTimeAttrs(customAPIFuncParseTime(layout, value))
|
return dynamicRelativeTimeAttrs(customAPIFuncParseTimeInLocation(layout, value, time.UTC))
|
||||||
},
|
},
|
||||||
// The reason we flip the parameter order is so that you can chain multiple calls together like this:
|
// The reason we flip the parameter order is so that you can chain multiple calls together like this:
|
||||||
// {{ .JSON.String "foo" | trimPrefix "bar" | doSomethingElse }}
|
// {{ .JSON.String "foo" | trimPrefix "bar" | doSomethingElse }}
|
||||||
@@ -471,6 +476,13 @@ var customAPITemplateFuncs = func() template.FuncMap {
|
|||||||
"replaceAll": func(old, new, s string) string {
|
"replaceAll": func(old, new, s string) string {
|
||||||
return strings.ReplaceAll(s, old, new)
|
return strings.ReplaceAll(s, old, new)
|
||||||
},
|
},
|
||||||
|
"replaceMatches": func(pattern, replacement, s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCachedRegexp(pattern).ReplaceAllString(s, replacement)
|
||||||
|
},
|
||||||
"findMatch": func(pattern, s string) string {
|
"findMatch": func(pattern, s string) string {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return ""
|
return ""
|
||||||
@@ -521,8 +533,8 @@ var customAPITemplateFuncs = func() template.FuncMap {
|
|||||||
},
|
},
|
||||||
"sortByTime": func(key, layout, order string, results []decoratedGJSONResult) []decoratedGJSONResult {
|
"sortByTime": func(key, layout, order string, results []decoratedGJSONResult) []decoratedGJSONResult {
|
||||||
sort.Slice(results, func(a, b int) bool {
|
sort.Slice(results, func(a, b int) bool {
|
||||||
timeA := customAPIFuncParseTime(layout, results[a].String(key))
|
timeA := customAPIFuncParseTimeInLocation(layout, results[a].String(key), time.UTC)
|
||||||
timeB := customAPIFuncParseTime(layout, results[b].String(key))
|
timeB := customAPIFuncParseTimeInLocation(layout, results[b].String(key), time.UTC)
|
||||||
|
|
||||||
if order == "asc" {
|
if order == "asc" {
|
||||||
return timeA.Before(timeB)
|
return timeA.Before(timeB)
|
||||||
@@ -536,6 +548,18 @@ var customAPITemplateFuncs = func() template.FuncMap {
|
|||||||
"concat": func(items ...string) string {
|
"concat": func(items ...string) string {
|
||||||
return strings.Join(items, "")
|
return strings.Join(items, "")
|
||||||
},
|
},
|
||||||
|
"unique": func(key string, results []decoratedGJSONResult) []decoratedGJSONResult {
|
||||||
|
seen := make(map[string]struct{})
|
||||||
|
out := make([]decoratedGJSONResult, 0, len(results))
|
||||||
|
for _, result := range results {
|
||||||
|
val := result.String(key)
|
||||||
|
if _, ok := seen[val]; !ok {
|
||||||
|
seen[val] = struct{}{}
|
||||||
|
out = append(out, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range globalTemplateFunctions {
|
for key, value := range globalTemplateFunctions {
|
||||||
@@ -547,7 +571,7 @@ var customAPITemplateFuncs = func() template.FuncMap {
|
|||||||
return funcs
|
return funcs
|
||||||
}()
|
}()
|
||||||
|
|
||||||
func customAPIFuncParseTime(layout, value string) time.Time {
|
func customAPIFuncParseTimeInLocation(layout, value string, loc *time.Location) time.Time {
|
||||||
switch strings.ToLower(layout) {
|
switch strings.ToLower(layout) {
|
||||||
case "unix":
|
case "unix":
|
||||||
asInt, err := strconv.ParseInt(value, 10, 64)
|
asInt, err := strconv.ParseInt(value, 10, 64)
|
||||||
@@ -566,7 +590,7 @@ func customAPIFuncParseTime(layout, value string) time.Time {
|
|||||||
layout = time.DateOnly
|
layout = time.DateOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed, err := time.Parse(layout, value)
|
parsed, err := time.ParseInLocation(layout, value, loc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Unix(0, 0)
|
return time.Unix(0, 0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ func fetchSubredditPosts(
|
|||||||
var client requestDoer = defaultHTTPClient
|
var client requestDoer = defaultHTTPClient
|
||||||
|
|
||||||
if requestUrlTemplate != "" {
|
if requestUrlTemplate != "" {
|
||||||
requestUrl = strings.ReplaceAll(requestUrlTemplate, "{REQUEST-URL}", requestUrl)
|
requestUrl = strings.ReplaceAll(requestUrlTemplate, "{REQUEST-URL}", url.QueryEscape(requestUrl))
|
||||||
} else if proxyClient != nil {
|
} else if proxyClient != nil {
|
||||||
client = proxyClient
|
client = proxyClient
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,6 +331,7 @@ func fetchItemsFromRSSFeeds(requests []rssFeedRequest) (rssFeedItemList, error)
|
|||||||
|
|
||||||
failed := 0
|
failed := 0
|
||||||
entries := make(rssFeedItemList, 0, len(feeds)*10)
|
entries := make(rssFeedItemList, 0, len(feeds)*10)
|
||||||
|
seen := make(map[string]struct{})
|
||||||
|
|
||||||
for i := range feeds {
|
for i := range feeds {
|
||||||
if errs[i] != nil {
|
if errs[i] != nil {
|
||||||
@@ -339,7 +340,13 @@ func fetchItemsFromRSSFeeds(requests []rssFeedRequest) (rssFeedItemList, error)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
entries = append(entries, feeds[i]...)
|
for _, item := range feeds[i] {
|
||||||
|
if _, exists := seen[item.Link]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entries = append(entries, item)
|
||||||
|
seen[item.Link] = struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if failed == len(requests) {
|
if failed == len(requests) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type searchWidget struct {
|
|||||||
SearchEngine string `yaml:"search-engine"`
|
SearchEngine string `yaml:"search-engine"`
|
||||||
Bangs []SearchBang `yaml:"bangs"`
|
Bangs []SearchBang `yaml:"bangs"`
|
||||||
NewTab bool `yaml:"new-tab"`
|
NewTab bool `yaml:"new-tab"`
|
||||||
|
Target string `yaml:"target"`
|
||||||
Autofocus bool `yaml:"autofocus"`
|
Autofocus bool `yaml:"autofocus"`
|
||||||
Placeholder string `yaml:"placeholder"`
|
Placeholder string `yaml:"placeholder"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,6 +196,10 @@ func fetchChannelFromTwitchTask(channel string) (twitchChannel, error) {
|
|||||||
slog.Warn("Failed to parse Twitch stream started at", "error", err, "started_at", streamMetadata.UserOrNull.Stream.StartedAt)
|
slog.Warn("Failed to parse Twitch stream started at", "error", err, "started_at", streamMetadata.UserOrNull.Stream.StartedAt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// This prevents live channels with 0 viewers from being
|
||||||
|
// incorrectly sorted lower than offline channels
|
||||||
|
result.ViewersCount = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|||||||
@@ -181,6 +181,11 @@ func workerPoolDo[I any, O any](job *workerPoolJob[I, O]) ([]O, []error, error)
|
|||||||
return results, errs, nil
|
return results, errs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(job.data) == 1 {
|
||||||
|
results[0], errs[0] = job.task(job.data[0])
|
||||||
|
return results, errs, nil
|
||||||
|
}
|
||||||
|
|
||||||
tasksQueue := make(chan *workerPoolTask[I, O])
|
tasksQueue := make(chan *workerPoolTask[I, O])
|
||||||
resultsQueue := make(chan *workerPoolTask[I, O])
|
resultsQueue := make(chan *workerPoolTask[I, O])
|
||||||
|
|
||||||
|
|||||||
+29
-13
@@ -201,11 +201,12 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
|
|||||||
// currently disabled on Windows because it requires elevated privilidges, otherwise
|
// currently disabled on Windows because it requires elevated privilidges, otherwise
|
||||||
// keeps returning a single sensor with key "ACPI\\ThermalZone\\TZ00_0" which
|
// keeps returning a single sensor with key "ACPI\\ThermalZone\\TZ00_0" which
|
||||||
// doesn't seem to be the CPU sensor or correspond to anything useful when
|
// doesn't seem to be the CPU sensor or correspond to anything useful when
|
||||||
// compared against the temperatures Libre Hardware Monitor reports
|
// compared against the temperatures Libre Hardware Monitor reports.
|
||||||
// also disabled on openbsd because it's not implemented by go-psutil
|
// Also disabled on the bsd's because it's not implemented by go-psutil for them
|
||||||
if runtime.GOOS != "windows" && runtime.GOOS != "openbsd" {
|
if runtime.GOOS != "windows" && runtime.GOOS != "openbsd" && runtime.GOOS != "netbsd" && runtime.GOOS != "freebsd" {
|
||||||
sensorReadings, err := sensors.SensorsTemperatures()
|
sensorReadings, err := sensors.SensorsTemperatures()
|
||||||
if err == nil {
|
_, errIsWarning := err.(*sensors.Warnings)
|
||||||
|
if err == nil || errIsWarning {
|
||||||
if req.CPUTempSensor != "" {
|
if req.CPUTempSensor != "" {
|
||||||
for i := range sensorReadings {
|
for i := range sensorReadings {
|
||||||
if sensorReadings[i].SensorKey == req.CPUTempSensor {
|
if sensorReadings[i].SensorKey == req.CPUTempSensor {
|
||||||
@@ -227,22 +228,24 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filesystems, err := disk.Partitions(false)
|
addedMountpoints := map[string]struct{}{}
|
||||||
if err == nil {
|
addMountpointInfo := func(requestedPath string, mpReq MointpointRequest) {
|
||||||
for _, fs := range filesystems {
|
if _, exists := addedMountpoints[requestedPath]; exists {
|
||||||
mpReq, ok := req.Mountpoints[fs.Mountpoint]
|
return
|
||||||
|
}
|
||||||
|
|
||||||
isHidden := req.HideMountpointsByDefault
|
isHidden := req.HideMountpointsByDefault
|
||||||
if ok && mpReq.Hide != nil {
|
if mpReq.Hide != nil {
|
||||||
isHidden = *mpReq.Hide
|
isHidden = *mpReq.Hide
|
||||||
}
|
}
|
||||||
if isHidden {
|
if isHidden {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
usage, err := disk.Usage(fs.Mountpoint)
|
usage, err := disk.Usage(requestedPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
mpInfo := MountpointInfo{
|
mpInfo := MountpointInfo{
|
||||||
Path: fs.Mountpoint,
|
Path: requestedPath,
|
||||||
Name: mpReq.Name,
|
Name: mpReq.Name,
|
||||||
TotalMB: usage.Total / 1024 / 1024,
|
TotalMB: usage.Total / 1024 / 1024,
|
||||||
UsedMB: usage.Used / 1024 / 1024,
|
UsedMB: usage.Used / 1024 / 1024,
|
||||||
@@ -250,13 +253,26 @@ func Collect(req *SystemInfoRequest) (*SystemInfo, []error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
info.Mountpoints = append(info.Mountpoints, mpInfo)
|
info.Mountpoints = append(info.Mountpoints, mpInfo)
|
||||||
|
addedMountpoints[requestedPath] = struct{}{}
|
||||||
} else {
|
} else {
|
||||||
addErr(fmt.Errorf("getting filesystem usage for %s: %v", fs.Mountpoint, err))
|
addErr(fmt.Errorf("getting filesystem usage for %s: %v", requestedPath, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !req.HideMountpointsByDefault {
|
||||||
|
filesystems, err := disk.Partitions(false)
|
||||||
|
if err == nil {
|
||||||
|
for _, fs := range filesystems {
|
||||||
|
addMountpointInfo(fs.Mountpoint, req.Mountpoints[fs.Mountpoint])
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
addErr(fmt.Errorf("getting filesystems: %v", err))
|
addErr(fmt.Errorf("getting filesystems: %v", err))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for mountpoint, mpReq := range req.Mountpoints {
|
||||||
|
addMountpointInfo(mountpoint, mpReq)
|
||||||
|
}
|
||||||
|
|
||||||
sort.Slice(info.Mountpoints, func(a, b int) bool {
|
sort.Slice(info.Mountpoints, func(a, b int) bool {
|
||||||
return info.Mountpoints[a].UsedPercent > info.Mountpoints[b].UsedPercent
|
return info.Mountpoints[a].UsedPercent > info.Mountpoints[b].UsedPercent
|
||||||
|
|||||||
Reference in New Issue
Block a user