feat(toolbar): theme-mode tester + sync toolbar.templ from cms

Sync the vendored templates/bn/toolbar.templ with the cms copy (entrance
animation, position picker chips, HidePreviewToggle — and the new per-tab
theme tester button). Port themeInitScript to the override-aware
window.bnApplyTheme so jutsu-rendered pages honour the admin's transient
bn-theme-override (sessionStorage) ahead of the visitor bn-theme preference.

Regenerated *_templ.go with templ v0.3.1020 (button/engagement diffs are
generator-version churn only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Alex Dunmow 2026-07-07 02:20:04 +08:00
parent 1a75edeae9
commit 1ee9c280e2
6 changed files with 482 additions and 281 deletions

View File

@ -1,6 +1,6 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.1001
// templ: version: v0.3.1020
package shared
//lint:file-ignore SA4006 This context is only used if a nested component is present.
@ -44,7 +44,7 @@ func RenderButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var3 templ.SafeURL
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(btn.URL))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 8, Col: 33}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 8, Col: 33}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
@ -55,11 +55,11 @@ func RenderButton(btn ButtonConfig) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var2).String())
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 1, Col: 0}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -80,7 +80,7 @@ func RenderButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(btn.InlineStyle())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 14, Col: 29}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 14, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
@ -93,7 +93,7 @@ func RenderButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(btn.Text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 16, Col: 14}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 16, Col: 14}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
@ -116,7 +116,7 @@ func RenderButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var8 templ.SafeURL
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(btn.URL))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 20, Col: 33}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 20, Col: 33}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
@ -127,11 +127,11 @@ func RenderButton(btn ButtonConfig) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var7).String())
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var7).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 1, Col: 0}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -152,7 +152,7 @@ func RenderButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(btn.Text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 27, Col: 14}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 27, Col: 14}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
@ -175,11 +175,11 @@ func RenderButton(btn ButtonConfig) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var11).String())
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var11).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 1, Col: 0}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -190,7 +190,7 @@ func RenderButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(btn.InlineStyle())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 32, Col: 74}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 32, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
@ -203,7 +203,7 @@ func RenderButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(btn.Text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 33, Col: 14}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 33, Col: 14}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
@ -224,11 +224,11 @@ func RenderButton(btn ButtonConfig) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var15).String())
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var15).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 1, Col: 0}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -239,7 +239,7 @@ func RenderButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(btn.Text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 37, Col: 14}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 37, Col: 14}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
@ -288,11 +288,11 @@ func RenderSubmitButton(btn ButtonConfig) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var20 string
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var19).String())
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var19).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 1, Col: 0}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -303,7 +303,7 @@ func RenderSubmitButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(btn.InlineStyle())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 46, Col: 73}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 46, Col: 73}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
@ -316,7 +316,7 @@ func RenderSubmitButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(btn.Text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 47, Col: 13}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 47, Col: 13}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil {
@ -337,11 +337,11 @@ func RenderSubmitButton(btn ButtonConfig) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var24 string
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var23).String())
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var23).String())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 1, Col: 0}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 1, Col: 0}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -352,7 +352,7 @@ func RenderSubmitButton(btn ButtonConfig) templ.Component {
var templ_7745c5c3_Var25 string
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(btn.Text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `button.templ`, Line: 51, Col: 13}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `blocks/shared/button.templ`, Line: 51, Col: 13}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
if templ_7745c5c3_Err != nil {

View File

@ -1,6 +1,6 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.1001
// templ: version: v0.3.1020
package bn
//lint:file-ignore SA4006 This context is only used if a nested component is present.
@ -60,11 +60,11 @@ func EngagementScript(config EngagementConfig) templ.Component {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(engagementConfigJSON(config))
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(engagementConfigJSON(config))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `engagement.templ`, Line: 30, Col: 63}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/engagement.templ`, Line: 30, Col: 63}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@ -775,15 +775,23 @@ func themeInitScript(themeMode string) templ.Component {
default:
themeMode = "light"
}
// Precedence: admin toolbar override (per-tab, sessionStorage) → visitor
// preference (bn-theme cookie/localStorage) → site default → system.
// Exposed as window.bnApplyTheme so the toolbar theme tester can re-apply
// after changing the override without duplicating this resolution.
// KEEP IN SYNC with cms backend/templates/bn/head.templ.
return templ.Raw(`<script>
(function(){
var c=document.cookie.match(/(?:^|; )bn-theme=([^;]*)/);
var t=c?c[1]:localStorage.getItem('bn-theme');
window.bnApplyTheme=function(){
var t=null;try{t=sessionStorage.getItem('bn-theme-override')}catch(e){}
if(!t){
var c=document.cookie.match(/(?:^|; )bn-theme=([^;]*)/);
t=c?c[1]:localStorage.getItem('bn-theme');
}
if(!t)t='` + themeMode + `';
var h=document.documentElement;
if(t==='system')t=window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light';
if(t==='dark')h.classList.add('dark');
else h.classList.remove('dark');
})();
document.documentElement.classList.toggle('dark',t==='dark');
return t;
};
window.bnApplyTheme();
</script>`)
}

View File

@ -416,7 +416,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(data.EffectiveTitle())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 384, Col: 32}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 384, Col: 32}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
@ -434,7 +434,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var3 templ.SafeURL
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs("/data/styles/styles.css?v=" + data.CSSHash)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 386, Col: 76}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 386, Col: 76}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
@ -466,7 +466,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var4 templ.SafeURL
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Branding.FaviconICO)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 394, Col: 80}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 394, Col: 80}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
@ -479,7 +479,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var5 templ.SafeURL
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Branding.Favicon16)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 395, Col: 90}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 395, Col: 90}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
@ -492,7 +492,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var6 templ.SafeURL
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Branding.Favicon32)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 396, Col: 90}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 396, Col: 90}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
@ -505,7 +505,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var7 templ.SafeURL
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Branding.AppleTouchIcon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 397, Col: 92}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 397, Col: 92}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
@ -518,7 +518,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var8 templ.SafeURL
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Branding.ManifestURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 398, Col: 65}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 398, Col: 65}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
@ -536,7 +536,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Settings.Branding.ThemeColor)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 400, Col: 72}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 400, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
if templ_7745c5c3_Err != nil {
@ -560,7 +560,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var10 templ.SafeURL
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.Favicon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 405, Col: 49}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 405, Col: 49}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
@ -583,7 +583,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var11 templ.SafeURL
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.AppleTouchIcon)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 408, Col: 68}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 408, Col: 68}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
@ -603,7 +603,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 416, Col: 67}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 416, Col: 67}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12)
if templ_7745c5c3_Err != nil {
@ -621,7 +621,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Settings.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 418, Col: 67}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 418, Col: 67}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
if templ_7745c5c3_Err != nil {
@ -640,7 +640,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var14 templ.SafeURL
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinURLErrs(data.PageMeta.CanonicalURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 423, Col: 58}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 423, Col: 58}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
@ -659,7 +659,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.RobotsDirective)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 428, Col: 62}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 428, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
if templ_7745c5c3_Err != nil {
@ -678,7 +678,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Settings.OGSiteName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 434, Col: 67}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 434, Col: 67}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
if templ_7745c5c3_Err != nil {
@ -697,7 +697,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.OGTitle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 439, Col: 60}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 439, Col: 60}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
if templ_7745c5c3_Err != nil {
@ -715,7 +715,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.MetaTitle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 441, Col: 62}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 441, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18)
if templ_7745c5c3_Err != nil {
@ -733,7 +733,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 443, Col: 49}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 443, Col: 49}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)
if templ_7745c5c3_Err != nil {
@ -752,7 +752,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var20 string
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.OGDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 448, Col: 72}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 448, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20)
if templ_7745c5c3_Err != nil {
@ -770,7 +770,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 450, Col: 74}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 450, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21)
if templ_7745c5c3_Err != nil {
@ -788,7 +788,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Settings.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 452, Col: 74}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 452, Col: 74}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var22)
if templ_7745c5c3_Err != nil {
@ -807,7 +807,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var23 string
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue(resolveMediaURL(data.PageMeta.OGImage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 457, Col: 77}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 457, Col: 77}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
if templ_7745c5c3_Err != nil {
@ -825,7 +825,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var24 string
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.ResolveAttributeValue(resolveMediaURL(data.Settings.DefaultOGImage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 459, Col: 84}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 459, Col: 84}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24)
if templ_7745c5c3_Err != nil {
@ -848,7 +848,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var25 string
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Settings.TwitterHandle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 467, Col: 66}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 467, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)
if templ_7745c5c3_Err != nil {
@ -871,7 +871,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var26 string
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.TwitterTitle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 473, Col: 66}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 473, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26)
if templ_7745c5c3_Err != nil {
@ -889,7 +889,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var27 string
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.OGTitle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 475, Col: 61}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 475, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var27)
if templ_7745c5c3_Err != nil {
@ -907,7 +907,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var28 string
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.MetaTitle)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 477, Col: 63}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 477, Col: 63}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28)
if templ_7745c5c3_Err != nil {
@ -925,7 +925,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var29 string
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 479, Col: 50}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 479, Col: 50}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)
if templ_7745c5c3_Err != nil {
@ -944,7 +944,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var30 string
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.TwitterDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 484, Col: 78}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 484, Col: 78}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var30)
if templ_7745c5c3_Err != nil {
@ -962,7 +962,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var31 string
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.OGDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 486, Col: 73}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 486, Col: 73}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var31)
if templ_7745c5c3_Err != nil {
@ -980,7 +980,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var32 string
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.PageMeta.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 488, Col: 75}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 488, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var32)
if templ_7745c5c3_Err != nil {
@ -998,7 +998,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var33 string
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Settings.MetaDescription)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 490, Col: 75}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 490, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33)
if templ_7745c5c3_Err != nil {
@ -1017,7 +1017,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var34 string
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.ResolveAttributeValue(resolveMediaURL(data.PageMeta.TwitterImage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 495, Col: 83}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 495, Col: 83}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34)
if templ_7745c5c3_Err != nil {
@ -1035,7 +1035,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var35 string
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(resolveMediaURL(data.PageMeta.OGImage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 497, Col: 78}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 497, Col: 78}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
if templ_7745c5c3_Err != nil {
@ -1053,7 +1053,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var36 string
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.ResolveAttributeValue(resolveMediaURL(data.Settings.DefaultOGImage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 499, Col: 85}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 499, Col: 85}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var36)
if templ_7745c5c3_Err != nil {
@ -1078,7 +1078,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var37 string
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Settings.RSSFeedTitle + " (RSS)")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 511, Col: 97}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 511, Col: 97}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37)
if templ_7745c5c3_Err != nil {
@ -1091,7 +1091,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var38 templ.SafeURL
templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.RSSFeedURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 511, Col: 131}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 511, Col: 131}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
if templ_7745c5c3_Err != nil {
@ -1104,7 +1104,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var39 string
templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.ResolveAttributeValue(data.Settings.RSSFeedTitle + " (Atom)")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 512, Col: 99}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 512, Col: 99}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var39)
if templ_7745c5c3_Err != nil {
@ -1117,7 +1117,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var40 templ.SafeURL
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinURLErrs(data.Settings.RSSFeedURL + "/atom")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 512, Col: 143}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 512, Col: 143}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
if templ_7745c5c3_Err != nil {
@ -1136,7 +1136,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var41 string
templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.ResolveAttributeValue("https://www.googletagmanager.com/gtag/js?id=" + data.Settings.GoogleAnalyticsID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 516, Col: 103}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 516, Col: 103}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var41)
if templ_7745c5c3_Err != nil {
@ -1167,7 +1167,7 @@ func Head(data HeadData) templ.Component {
var templ_7745c5c3_Var42 templ.SafeURL
templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinURLErrs(style)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 524, Col: 38}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 524, Col: 38}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
if templ_7745c5c3_Err != nil {
@ -1374,7 +1374,7 @@ func analyticsScript(nonce string) templ.Component {
var templ_7745c5c3_Var47 string
templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.ResolveAttributeValue(nonce)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `bn/head.templ`, Line: 598, Col: 27}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bn/head.templ`, Line: 598, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var47)
if templ_7745c5c3_Err != nil {
@ -1394,16 +1394,24 @@ func themeInitScript(themeMode string) templ.Component {
default:
themeMode = "light"
}
// Precedence: admin toolbar override (per-tab, sessionStorage) → visitor
// preference (bn-theme cookie/localStorage) → site default → system.
// Exposed as window.bnApplyTheme so the toolbar theme tester can re-apply
// after changing the override without duplicating this resolution.
// KEEP IN SYNC with cms backend/templates/bn/head.templ.
return templ.Raw(`<script>
(function(){
var c=document.cookie.match(/(?:^|; )bn-theme=([^;]*)/);
var t=c?c[1]:localStorage.getItem('bn-theme');
window.bnApplyTheme=function(){
var t=null;try{t=sessionStorage.getItem('bn-theme-override')}catch(e){}
if(!t){
var c=document.cookie.match(/(?:^|; )bn-theme=([^;]*)/);
t=c?c[1]:localStorage.getItem('bn-theme');
}
if(!t)t='` + themeMode + `';
var h=document.documentElement;
if(t==='system')t=window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light';
if(t==='dark')h.classList.add('dark');
else h.classList.remove('dark');
})();
document.documentElement.classList.toggle('dark',t==='dark');
return t;
};
window.bnApplyTheme();
</script>`)
}

View File

@ -17,6 +17,8 @@ type ToolbarData struct {
Status string // "published", "draft", "scheduled"
HasUnpublishedChanges bool
PreviewMode string // "published" or "draft"
HidePreviewToggle bool
Animate bool // true only on initial full-page render (entrance animation); false on HTMX re-renders
Position string // "tl", "tc", "tr", "bl", "bc", "br" (default: "tr")
ScheduledAt *time.Time
TemplateName string
@ -135,6 +137,20 @@ func (t ToolbarData) DropdownAlign() string {
return "right"
}
// EnterAnimationClass returns the entrance-animation classes for the initial
// full-page render. It slides the pill in from the edge it rests against (up for
// bottom positions, down for top) then gives a brief pulse. Empty on HTMX
// re-renders (Animate=false) so publish/discard/reposition swaps appear instantly.
func (t ToolbarData) EnterAnimationClass() string {
if !t.Animate {
return ""
}
if t.IsBottomPosition() {
return "bn-toolbar-enter bn-toolbar-enter-up"
}
return "bn-toolbar-enter bn-toolbar-enter-down"
}
// PositionClasses returns the positioning classes for the floating pill
func (t ToolbarData) PositionClasses() string {
switch t.Position {
@ -188,7 +204,7 @@ templ AdminEditorToolbar(data ToolbarData) {
if data.Enabled {
<div
id="bn-admin-toolbar"
class={ "bn-toolbar fixed z-[9999] flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-full backdrop-blur-md transition-all duration-300", data.PositionClasses() }
class={ "bn-toolbar fixed z-[9999] flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-full backdrop-blur-md transition-all duration-300", data.PositionClasses(), data.EnterAnimationClass() }
>
<style>
/* Toolbar: dark by default (for light pages), light when page has .dark class */
@ -205,6 +221,33 @@ templ AdminEditorToolbar(data ToolbarData) {
border: 1px solid var(--bn-toolbar-border);
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.25), 0 8px 10px -6px rgba(0, 0, 0, 0.2);
}
/* Entrance: slide in from the resting edge, then a brief pulse.
Animates `transform` only; Tailwind v4 centres via the separate
`translate` property, so `-translate-x-1/2` is preserved. Runs
~1s after render, 500ms total, initial page load only. */
@keyframes bn-toolbar-slide-up {
0% { opacity: 0; transform: translateY(1.25rem); }
60% { opacity: 1; transform: translateY(0) scale(1); }
80% { transform: translateY(0) scale(1.05); }
100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes bn-toolbar-slide-down {
0% { opacity: 0; transform: translateY(-1.25rem); }
60% { opacity: 1; transform: translateY(0) scale(1); }
80% { transform: translateY(0) scale(1.05); }
100% { opacity: 1; transform: translateY(0) scale(1); }
}
.bn-toolbar-enter {
animation-duration: 500ms;
animation-delay: 900ms;
animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
animation-fill-mode: both;
}
.bn-toolbar-enter-up { animation-name: bn-toolbar-slide-up; }
.bn-toolbar-enter-down { animation-name: bn-toolbar-slide-down; }
@media (prefers-reduced-motion: reduce) {
.bn-toolbar-enter { animation: none; }
}
/* Light toolbar when page theme is dark (.dark class on html or body) */
.dark .bn-toolbar, html.dark .bn-toolbar, body.dark .bn-toolbar {
--bn-toolbar-bg: rgba(250, 250, 250, 0.95);
@ -225,6 +268,13 @@ templ AdminEditorToolbar(data ToolbarData) {
.bn-toolbar .bn-toolbar-primary:hover {
background: color-mix(in srgb, var(--bn-toolbar-primary-bg) 90%, black);
}
/* Theme tester: highlighted while a light/dark override is forced.
Uses toolbar vars (not site theme tokens) so it renders on any site. */
.bn-toolbar .bn-theme-tester-active,
.bn-toolbar .bn-theme-tester-active:hover {
background: color-mix(in srgb, var(--bn-toolbar-primary-bg) 20%, transparent);
color: var(--bn-toolbar-primary-bg);
}
/* Dropdown styling - inherits toolbar vars */
.bn-toolbar-dropdown {
background: var(--bn-toolbar-bg);
@ -262,28 +312,120 @@ templ AdminEditorToolbar(data ToolbarData) {
<span class={ "w-2 h-2 rounded-full", templ.KV("bg-success", data.Status == "published"), templ.KV("bg-warning", data.Status == "draft"), templ.KV("bg-info", data.Status == "scheduled"), templ.KV("animate-pulse ring-2 ring-warning/50", data.HasUnpublishedChanges) }></span>
<span class="bn-toolbar-muted text-xs hidden sm:inline">{ data.StatusLabel() }</span>
</div>
if !data.HidePreviewToggle {
<!-- Divider -->
<div class="bn-toolbar-divider w-px h-5"></div>
<!-- Preview toggle - compact -->
<button
type="button"
class={ "bn-toolbar-hover inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs transition-colors", templ.KV("!bg-info/20 text-info", data.IsPreviewMode()), templ.KV("bn-toolbar-muted", !data.IsPreviewMode()) }
onclick={ togglePreviewMode(data.IsPreviewMode()) }
title={ func() string { if data.IsPreviewMode() { return "Viewing draft - click to view published" } else { return "Viewing published - click to preview draft" } }() }
>
if data.IsPreviewMode() {
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="currentColor">
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z"></path>
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"></path>
</svg>
<span class="hidden sm:inline">Draft</span>
} else {
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
<span class="hidden sm:inline">Live</span>
}
</button>
}
<!-- Divider -->
<div class="bn-toolbar-divider w-px h-5"></div>
<!-- Preview toggle - compact -->
<!-- Theme tester: cycles site default → forced light → forced dark.
Per-tab override (sessionStorage), applied by bnApplyTheme in head.templ.
Never touches the visitor bn-theme preference. -->
<button
type="button"
class={ "bn-toolbar-hover inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs transition-colors", templ.KV("!bg-info/20 text-info", data.IsPreviewMode()), templ.KV("bn-toolbar-muted", !data.IsPreviewMode()) }
onclick={ togglePreviewMode(data.IsPreviewMode()) }
title={ func() string { if data.IsPreviewMode() { return "Viewing draft - click to view published" } else { return "Viewing published - click to preview draft" } }() }
id="bn-theme-tester"
class="bn-toolbar-hover bn-toolbar-muted inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs transition-colors"
onclick="bnCycleThemeTester()"
title="Theme: site default — click to force light"
>
if data.IsPreviewMode() {
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="currentColor">
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z"></path>
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"></path>
<span data-tt="auto">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
<line x1="8" y1="21" x2="16" y2="21"></line>
<line x1="12" y1="17" x2="12" y2="21"></line>
</svg>
<span class="hidden sm:inline">Draft</span>
} else {
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</span>
<span data-tt="light" style="display:none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
<span class="hidden sm:inline">Live</span>
}
</span>
<span data-tt="dark" style="display:none">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
</span>
<span data-tt-label class="hidden sm:inline" style="display:none"></span>
</button>
<script>
// Theme tester: per-tab light/dark override for admins. Lives inside
// #bn-admin-toolbar so HTMX outerHTML swaps re-run the sync call.
window.bnCycleThemeTester = function() {
var cur = null;
try { cur = sessionStorage.getItem('bn-theme-override'); } catch (e) {}
var next = cur === 'light' ? 'dark' : (cur === 'dark' ? null : 'light');
try {
if (next) sessionStorage.setItem('bn-theme-override', next);
else sessionStorage.removeItem('bn-theme-override');
} catch (e) {}
if (window.bnApplyTheme) {
window.bnApplyTheme();
} else if (next) {
// Fallback for templates without the bn head script
document.documentElement.classList.toggle('dark', next === 'dark');
}
window.bnSyncThemeTester();
// Let darkmode-switcher blocks refresh their icons
var mode = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
document.querySelectorAll('[data-bn-theme-toggle]').forEach(function(el) {
el.dispatchEvent(new CustomEvent('bn:theme-changed', { detail: { mode: mode } }));
});
};
window.bnSyncThemeTester = function() {
var btn = document.getElementById('bn-theme-tester');
if (!btn) return;
var cur = null;
try { cur = sessionStorage.getItem('bn-theme-override'); } catch (e) {}
var state = (cur === 'light' || cur === 'dark') ? cur : 'auto';
btn.querySelectorAll('[data-tt]').forEach(function(s) {
s.style.display = s.getAttribute('data-tt') === state ? '' : 'none';
});
var lbl = btn.querySelector('[data-tt-label]');
if (state === 'auto') {
lbl.style.display = 'none';
lbl.textContent = '';
} else {
lbl.style.display = '';
lbl.textContent = state === 'light' ? 'Light' : 'Dark';
}
btn.classList.toggle('bn-theme-tester-active', state !== 'auto');
btn.classList.toggle('bn-toolbar-muted', state === 'auto');
btn.title = state === 'auto'
? 'Theme: site default — click to force light'
: (state === 'light'
? 'Theme: forced light — click to force dark'
: 'Theme: forced dark — click to reset to site default');
};
window.bnSyncThemeTester();
</script>
<!-- Blog-specific: Reading time -->
if data.IsBlogPost() && data.ReadingTime > 0 {
<div class="bn-toolbar-muted hidden md:flex items-center gap-1 px-2 text-xs" title="Reading time">
@ -347,6 +489,21 @@ templ AdminEditorToolbar(data ToolbarData) {
// PageInfoDropdown renders the page info and quick actions dropdown
templ PageInfoDropdown(data ToolbarData) {
<div class="bn-toolbar-dropdown rounded-lg py-2 min-w-[260px] max-w-[320px]">
<style>
/* Position-picker chips - shipped with the fragment so styling never
desyncs from the separately-rendered toolbar shell. Uses toolbar
vars (resolve via the enclosing .bn-toolbar) to stay visible in
both inverted themes. */
.bn-toolbar-dropdown .bn-dd-poschip { border-color: var(--bn-toolbar-border); }
.bn-toolbar-dropdown .bn-dd-poschip:hover { background: var(--bn-toolbar-hover); }
.bn-toolbar-dropdown .bn-dd-poschip-active,
.bn-toolbar-dropdown .bn-dd-poschip-active:hover {
background: var(--bn-toolbar-primary-bg);
border-color: var(--bn-toolbar-primary-bg);
}
.bn-toolbar-dropdown .bn-dd-dot { background: var(--bn-toolbar-muted); }
.bn-toolbar-dropdown .bn-dd-dot-active { background: var(--bn-toolbar-primary-fg); }
</style>
<!-- Analytics snapshot -->
if data.TodayPageviews > 0 || data.PageviewsTrend != "" {
<div class="bn-dd-border px-3 py-2 border-b">
@ -482,15 +639,16 @@ templ PageInfoDropdown(data ToolbarData) {
// positionButton renders a position selector button
templ positionButton(pageID uuid.UUID, pos string, currentPos string, label string) {
{{ active := pos == currentPos || (currentPos == "" && pos == "tr") }}
<button
type="button"
class={ "w-8 h-6 rounded border transition-colors flex items-center justify-center", templ.KV("bg-info border-info", pos == currentPos || (currentPos == "" && pos == "tr")), templ.KV("bn-dd-border bn-dd-hover", pos != currentPos && !(currentPos == "" && pos == "tr")) }
class={ "bn-dd-poschip w-8 h-6 rounded border transition-colors flex items-center justify-center", templ.KV("bn-dd-poschip-active", active) }
hx-post={ fmt.Sprintf("/toolbar/set-position/%s?pos=%s", pageID, pos) }
hx-target="#bn-admin-toolbar"
hx-swap="outerHTML"
title={ label }
>
<span class={ "w-1.5 h-1.5 rounded-full", templ.KV("bg-info-foreground", pos == currentPos || (currentPos == "" && pos == "tr")), templ.KV("bn-dd-muted", pos != currentPos && !(currentPos == "" && pos == "tr")) }></span>
<span class={ "w-1.5 h-1.5 rounded-full", templ.KV("bn-dd-dot-active", active), templ.KV("bn-dd-dot", !active) }></span>
</button>
}

File diff suppressed because one or more lines are too long