From e792ede99c03fa865eec17525faa86b76a743869 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 20:11:35 +0000 Subject: [PATCH 1/6] Initial plan From d0baaed20c736dcb36df0678f595e76c56c804a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 20:18:01 +0000 Subject: [PATCH 2/6] fix: tolerate blocked localStorage during theme startup Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/cb041476-1c5d-4a67-a06e-46d15d131caa Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> --- src/Core.Assets/src/Design/ThemeStorage.ts | 105 +++++++++++++-------- src/Core/wwwroot/js/loading-theme.js | 57 +++++++++-- 2 files changed, 113 insertions(+), 49 deletions(-) diff --git a/src/Core.Assets/src/Design/ThemeStorage.ts b/src/Core.Assets/src/Design/ThemeStorage.ts index f0e67a30fc..aabccb03bc 100644 --- a/src/Core.Assets/src/Design/ThemeStorage.ts +++ b/src/Core.Assets/src/Design/ThemeStorage.ts @@ -1,5 +1,25 @@ import { DesignTheme } from "../DesignTheme"; +type ThemeStorageValue = { mode: string | null, primaryColor: string | null, neutralColor: string | null }; + +type StorageLike = Pick; + +const memoryStorage = (() => { + const values: Record = {}; + + return { + getItem(key: string): string | null { + return Object.prototype.hasOwnProperty.call(values, key) ? values[key] : null; + }, + setItem(key: string, value: string): void { + values[key] = value; + }, + removeItem(key: string): void { + delete values[key]; + }, + } satisfies StorageLike; +})(); + class ThemeStorage { private _designTheme: DesignTheme @@ -19,13 +39,16 @@ class ThemeStorage { return this._designTheme.storageName; } + private static getStorage(): StorageLike { + try { + return window.localStorage ?? memoryStorage; + } catch { + return memoryStorage; + } + } public updateLocalStorage(mode: string | null, primaryColor: string | null, neutralColor: string | null): void { - - // If LocalStorage is not available, do nothing. - if (localStorage == null) { - return; - } + const storage = ThemeStorage.getStorage(); // Wait the component to be initialized if (!this._designTheme._isInitialized) { @@ -37,56 +60,58 @@ class ThemeStorage { return; } - // Save to the localstorage - localStorage.setItem(this.storageName, JSON.stringify({ - mode: ThemeStorage.getValueOrNull(mode), - primaryColor: ThemeStorage.getValueOrNull(primaryColor), - neutralColor: ThemeStorage.getValueOrNull(neutralColor), - })); + try { + storage.setItem(this.storageName, JSON.stringify({ + mode: ThemeStorage.getValueOrNull(mode), + primaryColor: ThemeStorage.getValueOrNull(primaryColor), + neutralColor: ThemeStorage.getValueOrNull(neutralColor), + })); + } catch { + // Ignore storage write failures and continue with in-memory theme state. + } } - public readLocalStorage(): { mode: string | null, primaryColor: string | null, neutralColor: string | null } | null { - - // If LocalStorage is not available, do nothing. - if (localStorage == null) { - return null; - } + public readLocalStorage(): ThemeStorageValue | null { + const storage = ThemeStorage.getStorage(); // Check if storageName attribute is defined if (this.storageName == null) { return null; } - // Check if localstorage exists - const storageJson = localStorage.getItem(this.storageName); + try { + const storageJson = storage.getItem(this.storageName); + if (storageJson == null) { + return null; + } + + // Read the localstorage + const storageItems = JSON.parse(storageJson); - if (storageJson == null) { + return { + mode: ThemeStorage.getValueOrNull(storageItems?.mode), + primaryColor: ThemeStorage.getValueOrNull(storageItems?.primaryColor), + neutralColor: ThemeStorage.getValueOrNull(storageItems?.neutralColor), + } + } catch { + this.clearLocalStorage(); return null; } + } - // Read the localstorage - const storageItems = JSON.parse(storageJson); + public clearLocalStorage(): void { + const storage = ThemeStorage.getStorage(); - return { - mode: ThemeStorage.getValueOrNull(storageItems?.mode), - primaryColor: ThemeStorage.getValueOrNull(storageItems?.primaryColor), - neutralColor: ThemeStorage.getValueOrNull(storageItems?.neutralColor), + // Check if storageName attribute is defined + if (this.storageName == null) { + return; } - } - public clearLocalStorage(): void { - // If LocalStorage is not available, do nothing. - if (localStorage == null) { - return; - } - - // Check if storageName attribute is defined - if (this.storageName == null) { - return; - } - - // Clear the localstorage - localStorage.removeItem(this.storageName); + try { + storage.removeItem(this.storageName); + } catch { + // Ignore storage clear failures and continue with in-memory theme state. + } } /** diff --git a/src/Core/wwwroot/js/loading-theme.js b/src/Core/wwwroot/js/loading-theme.js index 9529e56e9d..a00a12e403 100644 --- a/src/Core/wwwroot/js/loading-theme.js +++ b/src/Core/wwwroot/js/loading-theme.js @@ -2,6 +2,50 @@ // Here we'll find the first web component and wait for it to be upgraded. // When it is, we'll remove this invisibility from the body. +const memoryStorage = (() => { + const values = {}; + + return { + getItem(key) { + return Object.prototype.hasOwnProperty.call(values, key) ? values[key] : null; + }, + setItem(key, value) { + values[key] = String(value); + }, + removeItem(key) { + delete values[key]; + } + }; +})(); + +function getThemeStorage() { + try { + return window.localStorage ?? memoryStorage; + } catch { + return memoryStorage; + } +} + +function readStoredTheme(storageName) { + if (!storageName) { + return null; + } + + const storage = getThemeStorage(); + + try { + const theme = storage.getItem(storageName); + return theme ? JSON.parse(theme) : null; + } catch { + try { + storage.removeItem(storageName); + } catch { + } + + return null; + } +} + class LoadingTheme extends HTMLElement { className = "hidden-body"; @@ -17,17 +61,12 @@ class LoadingTheme extends HTMLElement { // Custom element added to page. connectedCallback() { - - // If LocalStorage is not available, do nothing. - if (localStorage == null) { - return; - } - // Attributes const storageName = this.getAttribute("storage-name"); const mode = this.getAttribute("mode"); const primaryColor = this.getAttribute("primary-color"); const neutralColor = this.getAttribute("neutral-color"); + const storedTheme = readStoredTheme(storageName); const isDark = (modeSaved, isSystemDark) => { switch (modeSaved) { @@ -41,9 +80,9 @@ class LoadingTheme extends HTMLElement { }; // Compute the saved or the system theme (dark/light). - const modeSaved = mode ?? JSON.parse(localStorage.getItem(storageName))?.mode; - const primaryColorSaved = primaryColor ?? JSON.parse(localStorage.getItem(storageName))?.primaryColor; - const neutralColorSaved = neutralColor ?? JSON.parse(localStorage.getItem(storageName))?.neutralColor; + const modeSaved = mode ?? storedTheme?.mode; + const primaryColorSaved = primaryColor ?? storedTheme?.primaryColor; + const neutralColorSaved = neutralColor ?? storedTheme?.neutralColor; const isSystemDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; const bgColor = isDark(modeSaved, isSystemDark) ? this.defaultDarkColor : this.defaultLightColor; From 7988792ae7f537e622b6d01605d62a9887462ca0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 20:19:32 +0000 Subject: [PATCH 3/6] refactor: simplify localStorage fallback access Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/cb041476-1c5d-4a67-a06e-46d15d131caa Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> --- src/Core.Assets/src/Design/ThemeStorage.ts | 3 ++- src/Core/wwwroot/js/loading-theme.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Core.Assets/src/Design/ThemeStorage.ts b/src/Core.Assets/src/Design/ThemeStorage.ts index aabccb03bc..c2447f3b83 100644 --- a/src/Core.Assets/src/Design/ThemeStorage.ts +++ b/src/Core.Assets/src/Design/ThemeStorage.ts @@ -41,7 +41,8 @@ class ThemeStorage { private static getStorage(): StorageLike { try { - return window.localStorage ?? memoryStorage; + const storage = window.localStorage; + return storage ?? memoryStorage; } catch { return memoryStorage; } diff --git a/src/Core/wwwroot/js/loading-theme.js b/src/Core/wwwroot/js/loading-theme.js index a00a12e403..1a16c3c4ae 100644 --- a/src/Core/wwwroot/js/loading-theme.js +++ b/src/Core/wwwroot/js/loading-theme.js @@ -20,7 +20,8 @@ const memoryStorage = (() => { function getThemeStorage() { try { - return window.localStorage ?? memoryStorage; + const storage = window.localStorage; + return storage ?? memoryStorage; } catch { return memoryStorage; } From 8a5049f3aaf71e4146643fce9ce036d48e0f5152 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 20:21:34 +0000 Subject: [PATCH 4/6] refactor: make storage fallback branch explicit Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/cb041476-1c5d-4a67-a06e-46d15d131caa Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> --- src/Core.Assets/src/Design/ThemeStorage.ts | 6 +++++- src/Core/wwwroot/js/loading-theme.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Core.Assets/src/Design/ThemeStorage.ts b/src/Core.Assets/src/Design/ThemeStorage.ts index c2447f3b83..c4269a4c07 100644 --- a/src/Core.Assets/src/Design/ThemeStorage.ts +++ b/src/Core.Assets/src/Design/ThemeStorage.ts @@ -42,7 +42,11 @@ class ThemeStorage { private static getStorage(): StorageLike { try { const storage = window.localStorage; - return storage ?? memoryStorage; + if (storage == null) { + return memoryStorage; + } + + return storage; } catch { return memoryStorage; } diff --git a/src/Core/wwwroot/js/loading-theme.js b/src/Core/wwwroot/js/loading-theme.js index 1a16c3c4ae..48075c0a27 100644 --- a/src/Core/wwwroot/js/loading-theme.js +++ b/src/Core/wwwroot/js/loading-theme.js @@ -21,7 +21,11 @@ const memoryStorage = (() => { function getThemeStorage() { try { const storage = window.localStorage; - return storage ?? memoryStorage; + if (storage == null) { + return memoryStorage; + } + + return storage; } catch { return memoryStorage; } From fe467c918412fc4939d0665025bb3bf49d3f9f48 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 20:22:56 +0000 Subject: [PATCH 5/6] style: tidy storage fallback helpers Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/cb041476-1c5d-4a67-a06e-46d15d131caa Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> --- src/Core.Assets/src/Design/ThemeStorage.ts | 2 +- src/Core/wwwroot/js/loading-theme.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Core.Assets/src/Design/ThemeStorage.ts b/src/Core.Assets/src/Design/ThemeStorage.ts index c4269a4c07..a01d6bc243 100644 --- a/src/Core.Assets/src/Design/ThemeStorage.ts +++ b/src/Core.Assets/src/Design/ThemeStorage.ts @@ -97,7 +97,7 @@ class ThemeStorage { mode: ThemeStorage.getValueOrNull(storageItems?.mode), primaryColor: ThemeStorage.getValueOrNull(storageItems?.primaryColor), neutralColor: ThemeStorage.getValueOrNull(storageItems?.neutralColor), - } + }; } catch { this.clearLocalStorage(); return null; diff --git a/src/Core/wwwroot/js/loading-theme.js b/src/Core/wwwroot/js/loading-theme.js index 48075c0a27..04704f28a1 100644 --- a/src/Core/wwwroot/js/loading-theme.js +++ b/src/Core/wwwroot/js/loading-theme.js @@ -67,11 +67,11 @@ class LoadingTheme extends HTMLElement { // Custom element added to page. connectedCallback() { // Attributes - const storageName = this.getAttribute("storage-name"); + const storageNameAttribute = this.getAttribute("storage-name"); const mode = this.getAttribute("mode"); const primaryColor = this.getAttribute("primary-color"); const neutralColor = this.getAttribute("neutral-color"); - const storedTheme = readStoredTheme(storageName); + const storedTheme = readStoredTheme(storageNameAttribute); const isDark = (modeSaved, isSystemDark) => { switch (modeSaved) { From 22a32209959b8b5a283c3c19111b9175404abaa8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 12 May 2026 20:24:27 +0000 Subject: [PATCH 6/6] refactor: reuse theme storage payload type Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/cb041476-1c5d-4a67-a06e-46d15d131caa Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> --- src/Core.Assets/src/Design/ThemeStorage.ts | 12 +++++++----- src/Core/wwwroot/js/loading-theme.js | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Core.Assets/src/Design/ThemeStorage.ts b/src/Core.Assets/src/Design/ThemeStorage.ts index a01d6bc243..023a48103f 100644 --- a/src/Core.Assets/src/Design/ThemeStorage.ts +++ b/src/Core.Assets/src/Design/ThemeStorage.ts @@ -65,12 +65,14 @@ class ThemeStorage { return; } + const theme: ThemeStorageValue = { + mode: ThemeStorage.getValueOrNull(mode), + primaryColor: ThemeStorage.getValueOrNull(primaryColor), + neutralColor: ThemeStorage.getValueOrNull(neutralColor), + }; + try { - storage.setItem(this.storageName, JSON.stringify({ - mode: ThemeStorage.getValueOrNull(mode), - primaryColor: ThemeStorage.getValueOrNull(primaryColor), - neutralColor: ThemeStorage.getValueOrNull(neutralColor), - })); + storage.setItem(this.storageName, JSON.stringify(theme)); } catch { // Ignore storage write failures and continue with in-memory theme state. } diff --git a/src/Core/wwwroot/js/loading-theme.js b/src/Core/wwwroot/js/loading-theme.js index 04704f28a1..48075c0a27 100644 --- a/src/Core/wwwroot/js/loading-theme.js +++ b/src/Core/wwwroot/js/loading-theme.js @@ -67,11 +67,11 @@ class LoadingTheme extends HTMLElement { // Custom element added to page. connectedCallback() { // Attributes - const storageNameAttribute = this.getAttribute("storage-name"); + const storageName = this.getAttribute("storage-name"); const mode = this.getAttribute("mode"); const primaryColor = this.getAttribute("primary-color"); const neutralColor = this.getAttribute("neutral-color"); - const storedTheme = readStoredTheme(storageNameAttribute); + const storedTheme = readStoredTheme(storageName); const isDark = (modeSaved, isSystemDark) => { switch (modeSaved) {