Allow disabling theme picker

This commit is contained in:
Svilen Markov 2025-05-16 18:08:36 +01:00
parent b294839b79
commit b4094b28bd
5 changed files with 60 additions and 37 deletions

View File

@ -389,10 +389,12 @@ Example:
```yaml ```yaml
theme: theme:
# This will be the default theme
background-color: 100 20 10 background-color: 100 20 10
primary-color: 40 90 40 primary-color: 40 90 40
contrast-multiplier: 1.1 contrast-multiplier: 1.1
disable-picker: false
presets: presets:
gruvbox-dark: gruvbox-dark:
background-color: 0 0 16 background-color: 0 0 16
@ -421,6 +423,7 @@ If you don't want to spend time configuring your own theme, there are [several a
| contrast-multiplier | number | no | 1 | | contrast-multiplier | number | no | 1 |
| text-saturation-multiplier | number | no | 1 | | text-saturation-multiplier | number | no | 1 |
| custom-css-file | string | no | | | custom-css-file | string | no | |
| disable-picker | bool | false | |
| presets | object | no | | | presets | object | no | |
#### `light` #### `light`
@ -466,8 +469,11 @@ theme:
> >
> In addition, you can also use the `css-class` property which is available on every widget to set custom class names for individual widgets. > In addition, you can also use the `css-class` property which is available on every widget to set custom class names for individual widgets.
#### `disable-picker`
When set to `true` hides the theme picker and disables the abiltity to switch between themes. All users who previously picked a non-default theme will be switched over to the default theme.
#### `presets` #### `presets`
Define additional theme presets that can be selected from the theme switcher on the page. For each preset, you can specify the same properties as for the default theme, such as `background-color`, `primary-color`, `positive-color`, `negative-color`, `contrast-multiplier`, etc., except for the `custom-css-file` property. Define additional theme presets that can be selected from the theme picker on the page. For each preset, you can specify the same properties as for the default theme, such as `background-color`, `primary-color`, `positive-color`, `negative-color`, `contrast-multiplier`, etc., except for the `custom-css-file` property.
Example: Example:

View File

@ -48,6 +48,8 @@ type config struct {
Theme struct { Theme struct {
themeProperties `yaml:",inline"` themeProperties `yaml:",inline"`
CustomCSSFile string `yaml:"custom-css-file"` CustomCSSFile string `yaml:"custom-css-file"`
DisablePicker bool `yaml:"disable-picker"`
Presets orderedYAMLMap[string, *themeProperties] `yaml:"presets"` Presets orderedYAMLMap[string, *themeProperties] `yaml:"presets"`
} `yaml:"theme"` } `yaml:"theme"`

View File

@ -101,6 +101,7 @@ func newApplication(c *config) (*application, error) {
// Init themes // Init themes
// //
if !config.Theme.DisablePicker {
themeKeys := make([]string, 0, 2) themeKeys := make([]string, 0, 2)
themeProps := make([]*themeProperties, 0, 2) themeProps := make([]*themeProperties, 0, 2)
@ -132,6 +133,7 @@ func newApplication(c *config) (*application, error) {
return nil, fmt.Errorf("initializing preset theme %s: %v", key, err) return nil, fmt.Errorf("initializing preset theme %s: %v", key, err)
} }
} }
}
config.Theme.Key = "default" config.Theme.Key = "default"
if err := config.Theme.init(); err != nil { if err := config.Theme.init(); err != nil {
@ -288,6 +290,7 @@ type templateData struct {
func (a *application) populateTemplateRequestData(data *templateRequestData, r *http.Request) { func (a *application) populateTemplateRequestData(data *templateRequestData, r *http.Request) {
theme := &a.Config.Theme.themeProperties theme := &a.Config.Theme.themeProperties
if !a.Config.Theme.DisablePicker {
selectedTheme, err := r.Cookie("theme") selectedTheme, err := r.Cookie("theme")
if err == nil { if err == nil {
preset, exists := a.Config.Theme.Presets.Get(selectedTheme.Value) preset, exists := a.Config.Theme.Presets.Get(selectedTheme.Value)
@ -295,6 +298,7 @@ func (a *application) populateTemplateRequestData(data *templateRequestData, r *
theme = preset theme = preset
} }
} }
}
data.Theme = theme data.Theme = theme
} }
@ -436,7 +440,11 @@ func (a *application) server() (func() error, func() error) {
mux.HandleFunc("GET /{page}", a.handlePageRequest) mux.HandleFunc("GET /{page}", a.handlePageRequest)
mux.HandleFunc("GET /api/pages/{page}/content/{$}", a.handlePageContentRequest) mux.HandleFunc("GET /api/pages/{page}/content/{$}", a.handlePageContentRequest)
if !a.Config.Theme.DisablePicker {
mux.HandleFunc("POST /api/set-theme/{key}", a.handleThemeChangeRequest) mux.HandleFunc("POST /api/set-theme/{key}", a.handleThemeChangeRequest)
}
mux.HandleFunc("/api/widgets/{widget}/{path...}", a.handleWidgetRequest) mux.HandleFunc("/api/widgets/{widget}/{path...}", a.handleWidgetRequest)
mux.HandleFunc("GET /api/healthz", func(w http.ResponseWriter, _ *http.Request) { mux.HandleFunc("GET /api/healthz", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)

View File

@ -689,12 +689,15 @@ async function changeTheme(key, onChanged) {
setTimeout(() => { tempStyle.remove(); }, 10); setTimeout(() => { tempStyle.remove(); }, 10);
} }
function initThemeSwitcher() { function initThemePicker() {
const themeChoicesInMobileNav = find(".mobile-navigation .theme-choices");
if (!themeChoicesInMobileNav) return;
const themeChoicesInHeader = find(".header-container .theme-choices"); const themeChoicesInHeader = find(".header-container .theme-choices");
if (themeChoicesInHeader) { if (themeChoicesInHeader) {
themeChoicesInHeader.replaceWith( themeChoicesInHeader.replaceWith(
find(".mobile-navigation .theme-choices").cloneNode(true) themeChoicesInMobileNav.cloneNode(true)
); );
} }
@ -739,7 +742,7 @@ function initThemeSwitcher() {
} }
async function setupPage() { async function setupPage() {
initThemeSwitcher(); initThemePicker();
const pageElement = document.getElementById("page"); const pageElement = document.getElementById("page");
const pageContentElement = document.getElementById("page-content"); const pageContentElement = document.getElementById("page-content");

View File

@ -33,6 +33,7 @@
<nav class="nav flex grow hide-scrollbars"> <nav class="nav flex grow hide-scrollbars">
{{ template "navigation-links" . }} {{ template "navigation-links" . }}
</nav> </nav>
{{ if not .App.Config.Theme.DisablePicker }}
<div class="theme-picker self-center" data-popover-type="html" data-popover-position="below" data-popover-show-delay="0"> <div class="theme-picker self-center" data-popover-type="html" data-popover-position="below" data-popover-show-delay="0">
<div class="current-theme-preview"> <div class="current-theme-preview">
{{ .Request.Theme.PreviewHTML }} {{ .Request.Theme.PreviewHTML }}
@ -41,6 +42,7 @@
<div class="theme-choices"></div> <div class="theme-choices"></div>
</div> </div>
</div> </div>
{{ end }}
{{- if .App.RequiresAuth }} {{- if .App.RequiresAuth }}
<a class="block self-center" href="{{ .App.Config.Server.BaseURL }}/logout" title="Logout"> <a class="block self-center" href="{{ .App.Config.Server.BaseURL }}/logout" title="Logout">
<svg class="logout-button" stroke="var(--color-text-subdue)" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"> <svg class="logout-button" stroke="var(--color-text-subdue)" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
@ -66,6 +68,7 @@
</div> </div>
<div class="mobile-navigation-actions flex flex-column margin-block-10"> <div class="mobile-navigation-actions flex flex-column margin-block-10">
{{ if not .App.Config.Theme.DisablePicker }}
<div class="theme-picker flex justify-between items-center" data-popover-type="html" data-popover-position="above" data-popover-show-delay="0" data-popover-hide-delay="100" data-popover-anchor=".current-theme-preview" data-popover-trigger="click"> <div class="theme-picker flex justify-between items-center" data-popover-type="html" data-popover-position="above" data-popover-show-delay="0" data-popover-hide-delay="100" data-popover-anchor=".current-theme-preview" data-popover-trigger="click">
<div data-popover-html> <div data-popover-html>
<div class="theme-choices"> <div class="theme-choices">
@ -87,6 +90,7 @@
</svg> </svg>
</div> </div>
</div> </div>
{{ end }}
{{ if .App.RequiresAuth }} {{ if .App.RequiresAuth }}
<a href="{{ .App.Config.Server.BaseURL }}/logout" class="flex justify-between items-center"> <a href="{{ .App.Config.Server.BaseURL }}/logout" class="flex justify-between items-center">