Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7bbf2b8e2 | ||
|
|
b8df34309f | ||
|
|
278fa3c397 | ||
|
|
6f48ee98e5 | ||
|
|
774b0c104b | ||
|
|
fbd4d9a74e | ||
|
|
3f2fefe4f7 | ||
|
|
fb8513bc9f | ||
|
|
e4ec958edb | ||
|
|
488a1f6070 | ||
|
|
1e12d937aa | ||
|
|
7319870289 |
@@ -356,7 +356,7 @@ pages:
|
|||||||
### Properties
|
### Properties
|
||||||
| Name | Type | Required | Default |
|
| Name | Type | Required | Default |
|
||||||
| ---- | ---- | -------- | ------- |
|
| ---- | ---- | -------- | ------- |
|
||||||
| title | string | yes | |
|
| name | string | yes | |
|
||||||
| slug | string | no | |
|
| slug | string | no | |
|
||||||
| width | string | no | |
|
| width | string | no | |
|
||||||
| center-vertically | boolean | no | false |
|
| center-vertically | boolean | no | false |
|
||||||
@@ -374,7 +374,7 @@ The URL friendly version of the title which is used to access the page. For exam
|
|||||||
#### `width`
|
#### `width`
|
||||||
The maximum width of the page on desktop. Possible values are `slim` and `wide`.
|
The maximum width of the page on desktop. Possible values are `slim` and `wide`.
|
||||||
|
|
||||||
* default: `1600px`
|
* default: `1600px` (when no value is specified)
|
||||||
* slim: `1100px`
|
* slim: `1100px`
|
||||||
* wide: `1920px`
|
* wide: `1920px`
|
||||||
|
|
||||||
@@ -1311,7 +1311,7 @@ headers:
|
|||||||
When set to `true`, removes the border and padding around the widget.
|
When set to `true`, removes the border and padding around the widget.
|
||||||
|
|
||||||
##### `template`
|
##### `template`
|
||||||
The template that will be used to display the data. It relies on Go's `html/template` package so it's recommended to go through [its documentation](https://pkg.go.dev/text/template) to understand how to do basic things such as conditionals, loops, etc. In addition, it also uses [tidwall's gjson](https://pkg.go.dev/github.com/tidwall/gjson) package to parse the JSON data so it's worth going through its documentation if you want to use more advanced JSON selectors. You can view additional examples with explanations and function definitions [here](custom-api.md).
|
The template that will be used to display the data. It relies on Go's `html/template` package so it's recommended to go through [its documentation](https://pkg.go.dev/text/template) to understand how to do basic things such as conditionals, loops, etc. In addition, it also uses [tidwall's gjson](https://github.com/tidwall/gjson) package to parse the JSON data so it's worth going through its documentation if you want to use more advanced JSON selectors. You can view additional examples with explanations and function definitions [here](custom-api.md).
|
||||||
|
|
||||||
### Extension
|
### 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).
|
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).
|
||||||
@@ -1608,7 +1608,7 @@ services:
|
|||||||
glance:
|
glance:
|
||||||
image: glanceapp/glance
|
image: glanceapp/glance
|
||||||
environment:
|
environment:
|
||||||
- GITHUB_TOKEN: <your token>
|
- GITHUB_TOKEN=<your token>
|
||||||
```
|
```
|
||||||
|
|
||||||
and then use it in your `glance.yml` like this:
|
and then use it in your `glance.yml` like this:
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -53,6 +53,16 @@ theme:
|
|||||||
primary-color: 97 13 80
|
primary-color: 97 13 80
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Gruvbox Dark
|
||||||
|

|
||||||
|
```yaml
|
||||||
|
theme:
|
||||||
|
background-color: 0 0 16
|
||||||
|
primary-color: 43 59 81
|
||||||
|
positive-color: 61 66 44
|
||||||
|
negative-color: 6 96 59
|
||||||
|
```
|
||||||
|
|
||||||
### Kanagawa Dark
|
### Kanagawa Dark
|
||||||

|

|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func newApplication(config *config) (*application, error) {
|
|||||||
|
|
||||||
for w := range column.Widgets {
|
for w := range column.Widgets {
|
||||||
widget := column.Widgets[w]
|
widget := column.Widgets[w]
|
||||||
app.widgetByID[widget.id()] = widget
|
app.widgetByID[widget.GetID()] = widget
|
||||||
|
|
||||||
widget.setProviders(providers)
|
widget.setProviders(providers)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -284,7 +284,9 @@ function setupGroups() {
|
|||||||
|
|
||||||
for (let i = 0; i < titles.length; i++) {
|
for (let i = 0; i < titles.length; i++) {
|
||||||
titles[i].classList.remove("widget-group-title-current");
|
titles[i].classList.remove("widget-group-title-current");
|
||||||
|
titles[i].setAttribute("aria-selected", "false");
|
||||||
tabs[i].classList.remove("widget-group-content-current");
|
tabs[i].classList.remove("widget-group-content-current");
|
||||||
|
tabs[i].setAttribute("aria-hidden", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current < t) {
|
if (current < t) {
|
||||||
@@ -296,7 +298,9 @@ function setupGroups() {
|
|||||||
current = t;
|
current = t;
|
||||||
|
|
||||||
title.classList.add("widget-group-title-current");
|
title.classList.add("widget-group-title-current");
|
||||||
|
title.setAttribute("aria-selected", "true");
|
||||||
tabs[t].classList.add("widget-group-content-current");
|
tabs[t].classList.add("widget-group-content-current");
|
||||||
|
tabs[t].setAttribute("aria-hidden", "false");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -670,6 +674,7 @@ async function setupPage() {
|
|||||||
setupLazyImages();
|
setupLazyImages();
|
||||||
} finally {
|
} finally {
|
||||||
pageElement.classList.add("content-ready");
|
pageElement.classList.add("content-ready");
|
||||||
|
pageElement.setAttribute("aria-busy", "false");
|
||||||
|
|
||||||
for (let i = 0; i < contentReadyCallbacks.length; i++) {
|
for (let i = 0; i < contentReadyCallbacks.length; i++) {
|
||||||
contentReadyCallbacks[i]();
|
contentReadyCallbacks[i]();
|
||||||
|
|||||||
@@ -110,7 +110,7 @@
|
|||||||
.visited-indicator:not(.text-truncate)::after,
|
.visited-indicator:not(.text-truncate)::after,
|
||||||
.visited-indicator.text-truncate::before,
|
.visited-indicator.text-truncate::before,
|
||||||
.bookmarks-link:not(.bookmarks-link-no-arrow)::after {
|
.bookmarks-link:not(.bookmarks-link-no-arrow)::after {
|
||||||
content: '↗';
|
content: '↗' / "";
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -189,7 +189,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.expand-toggle-button-icon::before {
|
.expand-toggle-button-icon::before {
|
||||||
content: '';
|
content: '' / "";
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
@@ -341,6 +341,19 @@ html, body, .body-content {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5 {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visually-hidden {
|
||||||
|
clip-path: inset(50%);
|
||||||
|
height: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
@@ -563,7 +576,7 @@ kbd:active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.summary::after {
|
.summary::after {
|
||||||
content: "◀";
|
content: "◀" / "";
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -822,7 +835,7 @@ details[open] .summary::after {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list-horizontal-text > *:not(:last-child)::after {
|
.list-horizontal-text > *:not(:last-child)::after {
|
||||||
content: '•';
|
content: '•' / "";
|
||||||
color: var(--color-text-subdue);
|
color: var(--color-text-subdue);
|
||||||
margin: 0 0.4rem;
|
margin: 0 0.4rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
<summary class="summary">Top blocked domains</summary>
|
<summary class="summary">Top blocked domains</summary>
|
||||||
<ul class="list list-gap-4 list-with-transition size-h5">
|
<ul class="list list-gap-4 list-with-transition size-h5">
|
||||||
{{ range .Stats.TopBlockedDomains }}
|
{{ range .Stats.TopBlockedDomains }}
|
||||||
<li class="flex justify-between">
|
<li class="flex justify-between gap-10">
|
||||||
<div class="text-truncate rtl">{{ .Domain }}</div>
|
<div class="text-truncate rtl">{{ .Domain }}</div>
|
||||||
<div class="text-right" style="width: 4rem;"><span class="color-highlight">{{ .PercentBlocked }}</span>%</div>
|
<div class="text-right" style="width: 4rem;"><span class="color-highlight">{{ .PercentBlocked }}</span>%</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -4,17 +4,18 @@
|
|||||||
|
|
||||||
{{ define "widget-content" }}
|
{{ define "widget-content" }}
|
||||||
<div class="widget-group-header">
|
<div class="widget-group-header">
|
||||||
<div class="widget-header gap-20">
|
<div class="widget-header gap-20" role="tablist">
|
||||||
{{ range $i, $widget := .Widgets }}
|
{{- range $i, $widget := .Widgets }}
|
||||||
<button class="widget-group-title{{ if eq $i 0 }} widget-group-title-current{{ end }}"{{ if ne "" .TitleURL }} data-title-url="{{ .TitleURL }}"{{ end }}>{{ $widget.Title }}</button>
|
<button class="widget-group-title{{ if eq $i 0 }} widget-group-title-current{{ end }}"{{ if ne "" .TitleURL }} data-title-url="{{ .TitleURL }}"{{ end }} aria-selected="{{ if eq $i 0 }}true{{ else }}false{{ end }}" arial-level="2" role="tab" aria-controls="widget-{{ .GetID }}-tabpanel-{{ $i }}" id="widget-{{ .GetID }}-tab-{{ $i }}">{{ $widget.Title }}</button>
|
||||||
{{ end }}
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="widget-group-contents">
|
<div class="widget-group-contents">
|
||||||
{{ range $i, $widget := .Widgets }}
|
{{- range $i, $widget := .Widgets }}
|
||||||
<div class="widget-group-content{{ if eq $i 0 }} widget-group-content-current{{ end }}">{{ .Render }}</div>
|
<div class="widget-group-content{{ if eq $i 0 }} widget-group-content-current{{ end }}" id="widget-{{ .GetID }}-tabpanel-{{ $i }}" role="tabpanel" aria-labelledby="widget-{{ .GetID }}-tab-{{ $i }}" aria-hidden="{{ if eq $i 0 }}false{{ else }}true{{ end }}">
|
||||||
{{ end }}
|
{{- .Render -}}
|
||||||
|
</div>
|
||||||
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
{{ define "navigation-links" }}
|
{{ define "navigation-links" }}
|
||||||
{{ range .App.Config.Pages }}
|
{{ range .App.Config.Pages }}
|
||||||
<a href="{{ $.App.Config.Server.BaseURL }}/{{ .Slug }}" class="nav-item{{ if eq .Slug $.Page.Slug }} nav-item-current{{ end }}">{{ .Title }}</a>
|
<a href="{{ $.App.Config.Server.BaseURL }}/{{ .Slug }}" class="nav-item{{ if eq .Slug $.Page.Slug }} nav-item-current{{ end }}"{{ if eq .Slug $.Page.Slug }} aria-current="page"{{ end }}>{{ .Title }}</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
@@ -35,10 +35,10 @@
|
|||||||
<div class="header-container content-bounds">
|
<div class="header-container content-bounds">
|
||||||
<div class="header flex padding-inline-widget widget-content-frame">
|
<div class="header flex padding-inline-widget widget-content-frame">
|
||||||
<!-- TODO: Replace G with actual logo, first need an actual logo -->
|
<!-- TODO: Replace G with actual logo, first need an actual logo -->
|
||||||
<div class="logo">{{ if ne "" .App.Config.Branding.LogoURL }}<img src="{{ .App.Config.Branding.LogoURL }}" alt="">{{ else if ne "" .App.Config.Branding.LogoText }}{{ .App.Config.Branding.LogoText }}{{ else }}G{{ end }}</div>
|
<div class="logo" aria-hidden="true">{{ if ne "" .App.Config.Branding.LogoURL }}<img src="{{ .App.Config.Branding.LogoURL }}" alt="">{{ else if ne "" .App.Config.Branding.LogoText }}{{ .App.Config.Branding.LogoText }}{{ else }}G{{ end }}</div>
|
||||||
<div class="nav flex grow">
|
<nav class="nav flex grow">
|
||||||
{{ template "navigation-links" . }}
|
{{ template "navigation-links" . }}
|
||||||
</div>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
@@ -57,17 +57,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-bounds grow">
|
<div class="content-bounds grow">
|
||||||
<div class="page" id="page">
|
<main class="page" id="page" aria-live="polite" aria-busy="true">
|
||||||
|
<h1 class="visually-hidden">{{ .Page.Title }}</h1>
|
||||||
<div class="page-content" id="page-content"></div>
|
<div class="page-content" id="page-content"></div>
|
||||||
<div class="page-loading-container">
|
<div class="page-loading-container">
|
||||||
<!-- TODO: add a bigger/better loading indicator -->
|
<!-- TODO: add a bigger/better loading indicator -->
|
||||||
<div class="loading-icon"></div>
|
<div class="visually-hidden">Loading</div>
|
||||||
</div>
|
<div class="loading-icon" aria-hidden="true"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ if not .App.Config.Branding.HideFooter }}
|
{{ if not .App.Config.Branding.HideFooter }}
|
||||||
<div class="footer flex items-center flex-column">
|
<footer class="footer flex items-center flex-column">
|
||||||
{{ if eq "" .App.Config.Branding.CustomFooter }}
|
{{ if eq "" .App.Config.Branding.CustomFooter }}
|
||||||
<div>
|
<div>
|
||||||
<a class="size-h3" href="https://github.com/glanceapp/glance" target="_blank" rel="noreferrer">Glance</a> {{ if ne "dev" .App.Version }}<a class="visited-indicator" title="Release notes" href="https://github.com/glanceapp/glance/releases/tag/{{ .App.Version }}" target="_blank" rel="noreferrer">{{ .App.Version }}</a>{{ else }}({{ .App.Version }}){{ end }}
|
<a class="size-h3" href="https://github.com/glanceapp/glance" target="_blank" rel="noreferrer">Glance</a> {{ if ne "dev" .App.Version }}<a class="visited-indicator" title="Release notes" href="https://github.com/glanceapp/glance/releases/tag/{{ .App.Version }}" target="_blank" rel="noreferrer">{{ .App.Version }}</a>{{ else }}({{ .App.Version }}){{ end }}
|
||||||
@@ -75,7 +77,7 @@
|
|||||||
{{ else }}
|
{{ else }}
|
||||||
{{ .App.Config.Branding.CustomFooter }}
|
{{ .App.Config.Branding.CustomFooter }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</footer>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
<div class="mobile-navigation-offset"></div>
|
<div class="mobile-navigation-offset"></div>
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
{{- if not .HideHeader}}
|
{{- if not .HideHeader}}
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
{{- if ne "" .TitleURL }}
|
{{- if ne "" .TitleURL }}
|
||||||
<a href="{{ .TitleURL | safeURL }}" target="_blank" rel="noreferrer" class="uppercase">{{ .Title }}</a>
|
<h2><a href="{{ .TitleURL | safeURL }}" target="_blank" rel="noreferrer" class="uppercase">{{ .Title }}</a></h2>
|
||||||
{{- else }}
|
{{- else }}
|
||||||
<div class="uppercase">{{ .Title }}</div>
|
<h2 class="uppercase">{{ .Title }}</h2>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .IsWIP }}
|
{{- if .IsWIP }}
|
||||||
<div data-popover-type="html" data-popover-position="above">
|
<div data-popover-type="html" data-popover-position="above">
|
||||||
|
|||||||
@@ -166,8 +166,7 @@ func fetchMarketsDataFromYahoo(marketRequests []marketRequest) (marketList, erro
|
|||||||
|
|
||||||
points := svgPolylineCoordsFromYValues(100, 50, maybeCopySliceWithoutZeroValues(prices))
|
points := svgPolylineCoordsFromYValues(100, 50, maybeCopySliceWithoutZeroValues(prices))
|
||||||
|
|
||||||
currency, exists := currencyToSymbol[response.Chart.Result[0].Meta.Currency]
|
currency, exists := currencyToSymbol[strings.ToUpper(response.Chart.Result[0].Meta.Currency)]
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
currency = response.Chart.Result[0].Meta.Currency
|
currency = response.Chart.Result[0].Meta.Currency
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,13 +121,13 @@ type widget interface {
|
|||||||
// These need to be exported because they get called in templates
|
// These need to be exported because they get called in templates
|
||||||
Render() template.HTML
|
Render() template.HTML
|
||||||
GetType() string
|
GetType() string
|
||||||
|
GetID() uint64
|
||||||
|
|
||||||
initialize() error
|
initialize() error
|
||||||
requiresUpdate(*time.Time) bool
|
requiresUpdate(*time.Time) bool
|
||||||
setProviders(*widgetProviders)
|
setProviders(*widgetProviders)
|
||||||
update(context.Context)
|
update(context.Context)
|
||||||
setID(uint64)
|
setID(uint64)
|
||||||
id() uint64
|
|
||||||
handleRequest(w http.ResponseWriter, r *http.Request)
|
handleRequest(w http.ResponseWriter, r *http.Request)
|
||||||
setHideHeader(bool)
|
setHideHeader(bool)
|
||||||
}
|
}
|
||||||
@@ -184,7 +184,7 @@ func (w *widgetBase) update(ctx context.Context) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *widgetBase) id() uint64 {
|
func (w *widgetBase) GetID() uint64 {
|
||||||
return w.ID
|
return w.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user