From f1a6de7cd79a4c8d487d75562a94a96113a2a1d5 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sat, 24 Oct 2026 19:44:34 -0400 Subject: [PATCH] ui: fix admin/docs theme flash + persist sidebar open/closed state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Theme flash (admin pages, docs, stats, progress — anywhere SSR'd): the JWT middlewares built the context user WITHOUT the Theme column, so every page rendering theme-{user.Theme} from c.Get("user") painted an empty class (:root tokyo-night default) and only settled on the real theme after initializeTheme -> loadUserTheme round-tripped the server. Both middlewares now carry liveUser.Theme into the context user; verified live: /admin /stats /progress /docs /docs-deep /dashboard /collections all SSR theme-hackerman. Defense in depth for the same bug: loadTheme() no longer re-applies localStorage over a server-rendered body theme (applyTheme records even the default, so any signed-out visit baked tokyo-night into localStorage and every signed-in page flipped to it post-paint). Public auth/landing bodies dropped their hardcoded theme classes so ThemeFlashGuard's pre-paint html class actually governs them; docs theme Bookhoard - Home
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -49,7 +49,7 @@ func Index(loggedIn bool, registrationEnabled bool) templ.Component { var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(opt.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 26, Col: 31} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 26, Col: 31} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2) if templ_7745c5c3_Err != nil { @@ -62,7 +62,7 @@ func Index(loggedIn bool, registrationEnabled bool) templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(opt.Label) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 26, Col: 45} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 26, Col: 45} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -85,7 +85,7 @@ func Index(loggedIn bool, registrationEnabled bool) templ.Component { var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(opt.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 31, Col: 31} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 31, Col: 31} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4) if templ_7745c5c3_Err != nil { @@ -98,7 +98,7 @@ func Index(loggedIn bool, registrationEnabled bool) templ.Component { var templ_7745c5c3_Var5 string templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(opt.Label) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/index.templ`, Line: 31, Col: 45} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 31, Col: 45} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { diff --git a/templates/library_switcher_templ.go b/templates/library_switcher_templ.go index a9b8ff18..0979e575 100644 --- a/templates/library_switcher_templ.go +++ b/templates/library_switcher_templ.go @@ -41,7 +41,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 11, Col: 105} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 11, Col: 105} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { @@ -64,7 +64,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(TotalMediaCount(libData)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 20, Col: 62} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 20, Col: 62} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -84,7 +84,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 24, Col: 29} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 24, Col: 29} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4) if templ_7745c5c3_Err != nil { @@ -97,7 +97,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var5 string templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 24, Col: 51} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 24, Col: 51} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { @@ -110,7 +110,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 24, Col: 71} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 24, Col: 71} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { @@ -128,7 +128,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 26, Col: 29} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 26, Col: 29} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7) if templ_7745c5c3_Err != nil { @@ -141,7 +141,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var8 string templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 26, Col: 42} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 26, Col: 42} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { @@ -154,7 +154,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var9 string templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 26, Col: 62} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 26, Col: 62} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { @@ -173,7 +173,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var10 string templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 29, Col: 28} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 29, Col: 28} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10) if templ_7745c5c3_Err != nil { @@ -186,7 +186,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var11 string templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 29, Col: 50} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 29, Col: 50} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) if templ_7745c5c3_Err != nil { @@ -199,7 +199,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var12 string templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 29, Col: 70} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 29, Col: 70} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { @@ -217,7 +217,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var13 string templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 31, Col: 28} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 31, Col: 28} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13) if templ_7745c5c3_Err != nil { @@ -230,7 +230,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 31, Col: 41} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 31, Col: 41} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { @@ -243,7 +243,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin var templ_7745c5c3_Var15 string templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 31, Col: 61} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `library_switcher.templ`, Line: 31, Col: 61} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) if templ_7745c5c3_Err != nil { diff --git a/templates/login.templ b/templates/login.templ index 5dccec5b..557bfd9b 100644 --- a/templates/login.templ +++ b/templates/login.templ @@ -13,7 +13,7 @@ templ Login(sessionExpired bool, deleted bool, registrationEnabled bool) { - +
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "Login - Bookhoard

Password Requirements:

  • ○ At least 8 characters
  • ○ One uppercase letter
  • ○ One lowercase letter
  • ○ One number
  • ○ One special character
  • ○ Passwords match

Already have an account? Login

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "Register - Bookhoard
📚 Bookhoard

Create your account

Password Requirements:

  • ○ At least 8 characters
  • ○ One uppercase letter
  • ○ One lowercase letter
  • ○ One number
  • ○ One special character
  • ○ Passwords match

Already have an account? Login

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/templates/series_templ.go b/templates/series_templ.go index 487ec075..8d9edf74 100644 --- a/templates/series_templ.go +++ b/templates/series_templ.go @@ -50,7 +50,7 @@ func Series(user User, seriesList []SeriesCardData, libData []LibraryData, curre var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 1, Col: 0} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 1, Col: 0} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3) if templ_7745c5c3_Err != nil { @@ -121,7 +121,7 @@ func Series(user User, seriesList []SeriesCardData, libData []LibraryData, curre var templ_7745c5c3_Var4 templ.SafeURL templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(fmt.Sprintf("/series?library_id=%s&page=%d", currentLibraryID, currentPage-1)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 50, Col: 93} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 50, Col: 93} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { @@ -147,7 +147,7 @@ func Series(user User, seriesList []SeriesCardData, libData []LibraryData, curre var templ_7745c5c3_Var5 string templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", currentPage)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 58, Col: 45} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 58, Col: 45} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { @@ -160,7 +160,7 @@ func Series(user User, seriesList []SeriesCardData, libData []LibraryData, curre var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", totalPages)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 58, Col: 82} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 58, Col: 82} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { @@ -178,7 +178,7 @@ func Series(user User, seriesList []SeriesCardData, libData []LibraryData, curre var templ_7745c5c3_Var7 templ.SafeURL templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs(fmt.Sprintf("/series?library_id=%s&page=%d", currentLibraryID, currentPage+1)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 62, Col: 93} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 62, Col: 93} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -246,7 +246,7 @@ func SeriesCard(series SeriesCardData) templ.Component { var templ_7745c5c3_Var9 templ.SafeURL templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinURLErrs("/series/detail?name=" + url.QueryEscape(series.Name) + "&library_id=" + url.QueryEscape(series.LibraryID)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 79, Col: 117} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 79, Col: 117} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { @@ -268,7 +268,7 @@ func SeriesCard(series SeriesCardData) templ.Component { var templ_7745c5c3_Var11 string templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var10).String()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 1, Col: 0} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 1, Col: 0} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11) if templ_7745c5c3_Err != nil { @@ -291,7 +291,7 @@ func SeriesCard(series SeriesCardData) templ.Component { var templ_7745c5c3_Var13 string templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(cover) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 84, Col: 17} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 84, Col: 17} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13) if templ_7745c5c3_Err != nil { @@ -304,7 +304,7 @@ func SeriesCard(series SeriesCardData) templ.Component { var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var12).String()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 1, Col: 0} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 1, Col: 0} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14) if templ_7745c5c3_Err != nil { @@ -317,7 +317,7 @@ func SeriesCard(series SeriesCardData) templ.Component { var templ_7745c5c3_Var15 string templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(series.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 86, Col: 23} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 86, Col: 23} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15) if templ_7745c5c3_Err != nil { @@ -349,7 +349,7 @@ func SeriesCard(series SeriesCardData) templ.Component { var templ_7745c5c3_Var16 string templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(series.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 98, Col: 99} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 98, Col: 99} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) if templ_7745c5c3_Err != nil { @@ -362,7 +362,7 @@ func SeriesCard(series SeriesCardData) templ.Component { var templ_7745c5c3_Var17 string templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", series.BookCount)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 100, Col: 42} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 100, Col: 42} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) if templ_7745c5c3_Err != nil { @@ -375,7 +375,7 @@ func SeriesCard(series SeriesCardData) templ.Component { var templ_7745c5c3_Var18 string templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", series.TotalInSeries)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 100, Col: 89} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `series.templ`, Line: 100, Col: 89} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) if templ_7745c5c3_Err != nil { diff --git a/templates/setup.templ b/templates/setup.templ index ee999e56..27ddd759 100644 --- a/templates/setup.templ +++ b/templates/setup.templ @@ -15,7 +15,7 @@ templ Setup(libraryTypes []database.LibraryTypes) { - +
diff --git a/templates/setup_templ.go b/templates/setup_templ.go index 30d1f86b..5b384471 100644 --- a/templates/setup_templ.go +++ b/templates/setup_templ.go @@ -39,7 +39,7 @@ func Setup(libraryTypes []database.LibraryTypes) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "Setup - Bookhoard
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "Setup - Bookhoard
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/templates/stats_templ.go b/templates/stats_templ.go index cd977958..f32142b5 100644 --- a/templates/stats_templ.go +++ b/templates/stats_templ.go @@ -45,7 +45,7 @@ func Stats(user User) templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/stats.templ`, Line: 1, Col: 0} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `stats.templ`, Line: 1, Col: 0} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3) if templ_7745c5c3_Err != nil { @@ -74,7 +74,7 @@ func Stats(user User) templ.Component { var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Timezone) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/stats.templ`, Line: 24, Col: 112} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `stats.templ`, Line: 24, Col: 112} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { diff --git a/templates/toast_templ.go b/templates/toast_templ.go index 86114b1a..84b0d230 100644 --- a/templates/toast_templ.go +++ b/templates/toast_templ.go @@ -37,7 +37,7 @@ func ErrorToast(message string) templ.Component { var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue("showErrorToast('" + message + "')") if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/toast.templ`, Line: 5, Col: 71} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `toast.templ`, Line: 5, Col: 71} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2) if templ_7745c5c3_Err != nil { diff --git a/templates/unlinked_books_templ.go b/templates/unlinked_books_templ.go index 245fb0e4..c76e229b 100644 --- a/templates/unlinked_books_templ.go +++ b/templates/unlinked_books_templ.go @@ -53,7 +53,7 @@ func UnlinkedMatchCards(progressId string, matches []services.BookMatch) templ.C var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(progressId) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 22, Col: 33} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 22, Col: 33} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2) if templ_7745c5c3_Err != nil { @@ -66,7 +66,7 @@ func UnlinkedMatchCards(progressId string, matches []services.BookMatch) templ.C var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(match.MediaItemID.String()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 23, Col: 51} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 23, Col: 51} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3) if templ_7745c5c3_Err != nil { @@ -79,7 +79,7 @@ func UnlinkedMatchCards(progressId string, matches []services.BookMatch) templ.C var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%v", match.Confidence)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 24, Col: 57} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 24, Col: 57} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4) if templ_7745c5c3_Err != nil { @@ -92,7 +92,7 @@ func UnlinkedMatchCards(progressId string, matches []services.BookMatch) templ.C var templ_7745c5c3_Var5 string templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(match.CoverImagePath) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 28, Col: 32} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 28, Col: 32} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5) if templ_7745c5c3_Err != nil { @@ -105,7 +105,7 @@ func UnlinkedMatchCards(progressId string, matches []services.BookMatch) templ.C var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(match.Title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 34, Col: 80} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 34, Col: 80} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { @@ -123,7 +123,7 @@ func UnlinkedMatchCards(progressId string, matches []services.BookMatch) templ.C var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(match.Author) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 36, Col: 80} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 36, Col: 80} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -141,7 +141,7 @@ func UnlinkedMatchCards(progressId string, matches []services.BookMatch) templ.C var templ_7745c5c3_Var8 string templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.0f%% confidence", match.Confidence*100)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 40, Col: 64} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 40, Col: 64} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { @@ -154,7 +154,7 @@ func UnlinkedMatchCards(progressId string, matches []services.BookMatch) templ.C var templ_7745c5c3_Var9 string templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(match.MatchMethod) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 42, Col: 85} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 42, Col: 85} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { @@ -207,7 +207,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var12 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: `templates/unlinked_books.templ`, Line: 1, Col: 0} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 1, Col: 0} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12) if templ_7745c5c3_Err != nil { @@ -289,7 +289,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var13 string templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.ProgressID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 111, Col: 100} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 111, Col: 100} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13) if templ_7745c5c3_Err != nil { @@ -302,7 +302,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.TitleFromDevice) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 111, Col: 136} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 111, Col: 136} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14) if templ_7745c5c3_Err != nil { @@ -335,7 +335,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var15 string templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(book.TitleFromDevice) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 125, Col: 111} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 125, Col: 111} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) if templ_7745c5c3_Err != nil { @@ -348,7 +348,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var16 string templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(book.DeviceName) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 127, Col: 37} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 127, Col: 37} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) if templ_7745c5c3_Err != nil { @@ -361,7 +361,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var17 string templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(book.DeviceType) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 127, Col: 58} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 127, Col: 58} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) if templ_7745c5c3_Err != nil { @@ -378,7 +378,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var18 string templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(book.FilePath) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 136, Col: 27} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 136, Col: 27} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) if templ_7745c5c3_Err != nil { @@ -396,7 +396,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var19 string templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.SHA256) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 142, Col: 68} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 142, Col: 68} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19) if templ_7745c5c3_Err != nil { @@ -409,7 +409,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var20 string templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(book.SHA256) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 142, Col: 84} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 142, Col: 84} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) if templ_7745c5c3_Err != nil { @@ -427,7 +427,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var21 string templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(book.LastSyncTime) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 147, Col: 71} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 147, Col: 71} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) if templ_7745c5c3_Err != nil { @@ -445,7 +445,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var22 string templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(book.ConfidenceScore) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 153, Col: 35} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 153, Col: 35} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) if templ_7745c5c3_Err != nil { @@ -463,7 +463,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var23 string templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue("searchMatches(" + JSONArg(book.ProgressID) + ", " + JSONArg(book.SHA256) + ", " + JSONArg(book.TitleFromDevice) + ")") if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 160, Col: 138} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 160, Col: 138} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23) if templ_7745c5c3_Err != nil { @@ -484,7 +484,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var24 string templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.ResolveAttributeValue("showManualLinkModal(" + JSONArg(book.ProgressID) + ", " + JSONArg(book.TitleFromDevice) + ")") if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 167, Col: 114} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 167, Col: 114} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24) if templ_7745c5c3_Err != nil { @@ -505,7 +505,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var25 string templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.ResolveAttributeValue("matches-" + book.ProgressID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 174, Col: 47} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 174, Col: 47} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25) if templ_7745c5c3_Err != nil { @@ -518,7 +518,7 @@ func UnlinkedBooks(user User, unlinkedBooks []UnlinkedBookData) templ.Component var templ_7745c5c3_Var26 string templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue("matches-list-" + book.ProgressID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/unlinked_books.templ`, Line: 177, Col: 54} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `unlinked_books.templ`, Line: 177, Col: 54} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26) if templ_7745c5c3_Err != nil { diff --git a/web/src/docs.ts b/web/src/docs.ts index fa2a17d5..d28c20f3 100644 --- a/web/src/docs.ts +++ b/web/src/docs.ts @@ -15,17 +15,70 @@ function toggleSidebar(): void { overlay.classList.toggle("hidden"); } +// Docs sidebar collapse persistence. Rendering ownership: the server +// renders what is expanded on FIRST paint (navigation.go derives it from +// the current path); after that, open/closed is client-local state and +// survives navigation via localStorage. Keys are the data-key attributes +// rendered by DocsSidebar ("Section" / "Section / Group"). + +const DOCS_NAV_STORAGE_KEY = "docs-nav-state"; + +type DocsNavState = Record; + +function readDocsNavState(): DocsNavState { + try { + const raw = localStorage.getItem(DOCS_NAV_STORAGE_KEY); + if (!raw) return {}; + const parsed = JSON.parse(raw); + if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { + return parsed as DocsNavState; + } + } catch { + // Corrupt state falls back to the server-rendered defaults. + } + return {}; +} + +function writeDocsNavState(state: DocsNavState): void { + try { + localStorage.setItem(DOCS_NAV_STORAGE_KEY, JSON.stringify(state)); + } catch { + // Storage full/blocked: persistence is best-effort. + } +} + +function applyCollapsed(items: HTMLElement, collapsed: boolean): void { + items.classList.toggle("hidden", collapsed); + items.setAttribute("data-collapsed", collapsed ? "true" : "false"); + const arrow = items.previousElementSibling?.querySelector(".section-arrow"); + if (arrow) { + arrow.style.transform = collapsed ? "rotate(-90deg)" : "rotate(0deg)"; + } +} + +function restoreDocsNav(): void { + const state = readDocsNavState(); + document.querySelectorAll("[data-key][data-collapsed]").forEach((items) => { + const key = items.getAttribute("data-key"); + if (key && key in state) { + applyCollapsed(items, state[key]); + } + }); +} + function toggleCollapsible(header: HTMLElement): void { const items = header.nextElementSibling as HTMLElement | null; - const arrow = header.querySelector(".section-arrow"); if (!items) return; const isCollapsed = items.getAttribute("data-collapsed") === "true"; - items.classList.toggle("hidden", !isCollapsed); - items.setAttribute("data-collapsed", isCollapsed ? "false" : "true"); - if (arrow) { - arrow.style.transform = isCollapsed ? "rotate(0deg)" : "rotate(-90deg)"; + applyCollapsed(items, !isCollapsed); + + const key = items.getAttribute("data-key"); + if (key) { + const state = readDocsNavState(); + state[key] = !isCollapsed; + writeDocsNavState(state); } } @@ -116,6 +169,7 @@ function initializeCodeCopyButtons(): void { Alpine.data("docs", () => ({ toggleSidebar, toggleCollapsible, + restoreDocsNav, initializeSearch: initializeDocsSearch, initializeCodeCopyButtons, })); diff --git a/web/src/theme.ts b/web/src/theme.ts index b2012b8a..0bfc1705 100644 --- a/web/src/theme.ts +++ b/web/src/theme.ts @@ -66,11 +66,24 @@ const applyTheme = (theme: string): void => { localStorage.setItem(THEME_STORAGE_KEY, theme); }; -// Load theme from localStorage or use default +// Load theme from localStorage or use default. +// +// Do NOT blindly applyTheme() here: signed-in pages server-render the +// user's theme into the body class, and re-applying the localStorage +// value caused the admin/docs "flash to tokyo-night then settle" bug — +// applyTheme records even the default, so any signed-out visit bakes +// "tokyo-night" into localStorage and every signed-in page painted the +// SSR theme, flipped to the stale stored one, then loadUserTheme() +// flipped back. The server-rendered class is authoritative; the async +// loadUserTheme() re-syncs localStorage to it. Signed-out pages carry +// no meaningful body theme ("theme-" with an empty user) and fall back +// to localStorage — which ThemeFlashGuard has already applied to +// pre-paint, so this is visually a no-op for them. const loadTheme = (): void => { - const storedTheme = localStorage.getItem(THEME_STORAGE_KEY) as ThemeType | null; - const theme = storedTheme || DEFAULT_THEME; - applyTheme(theme); + if (!/(?:^|\s)theme-[\w-]+/.test(document.body.className)) { + const storedTheme = localStorage.getItem(THEME_STORAGE_KEY); + applyTheme(storedTheme || DEFAULT_THEME); + } }; // Change: Remove DOM element lookup, accept theme as parameter @@ -115,6 +128,7 @@ const loadUserTheme = async (): Promise => { if (data.theme) { applyTheme(data.theme as string); updateThemeIndicators(); + syncThemeSelect(data.theme as string); } } } catch { @@ -122,6 +136,15 @@ const loadUserTheme = async (): Promise => { } }; +// Keep the docs-topbar theme