@@ -4,6 +4,7 @@ import { createCmsModuleDefinition } from "../src/host-adapter";
44
55type Cleanup = ( ( ) => void ) | undefined ;
66type AuthStatusState = "idle" | "ok" | "error" ;
7+ type ThemeMode = "auto" | "light" | "dark" ;
78
89type OidcDiscovery = {
910 authorizationEndpoint : string ;
@@ -26,6 +27,7 @@ type PkceSessionState = AuthFormState & {
2627const AUTH_TOKEN_STORAGE_KEY = "mfe.preview.authToken" ;
2728const AUTH_FORM_STORAGE_KEY = "mfe.preview.authFormState" ;
2829const PKCE_SESSION_STORAGE_KEY = "mfe.preview.pkceSessionState" ;
30+ const THEME_STORAGE_KEY = "suncoast:cms:theme-mode" ;
2931const PKCE_MAX_AGE_MS = 10 * 60 * 1000 ;
3032const DEFAULT_PREVIEW_GRAPHQL_HTTP_URLS = {
3133 dev : "https://cf-suncoast-graphql-proxy.dev.suncoast.systems/graphql" ,
@@ -115,6 +117,7 @@ const conversationIdInput = getInput("conversationId");
115117const applyButton = getButton ( "applyButton" ) ;
116118const loginButton = getButton ( "loginButton" ) ;
117119const clearTokenButton = getButton ( "clearTokenButton" ) ;
120+ const themeToggleButton = getButton ( "themeToggle" ) ;
118121const authStatus = getAuthStatusElement ( ) ;
119122const host = getHost ( ) ;
120123
@@ -158,6 +161,61 @@ function getSavedToken(): string {
158161 }
159162}
160163
164+ function getSavedTheme ( ) : ThemeMode | null {
165+ try {
166+ const raw = localStorage . getItem ( THEME_STORAGE_KEY ) ?. trim ( ) ;
167+ if ( raw === "auto" || raw === "light" || raw === "dark" ) {
168+ return raw ;
169+ }
170+ return null ;
171+ } catch {
172+ return null ;
173+ }
174+ }
175+
176+ function saveTheme ( theme : ThemeMode ) : void {
177+ try {
178+ localStorage . setItem ( THEME_STORAGE_KEY , theme ) ;
179+ } catch {
180+ // Ignore storage write errors in preview harness.
181+ }
182+ }
183+
184+ function inferSystemTheme ( ) : ThemeMode {
185+ try {
186+ return window . matchMedia ( "(prefers-color-scheme: dark)" ) . matches ? "dark" : "light" ;
187+ } catch {
188+ return "light" ;
189+ }
190+ }
191+
192+ function resolveThemeForRender ( theme : ThemeMode ) : "light" | "dark" {
193+ if ( theme === "auto" ) {
194+ const system = inferSystemTheme ( ) ;
195+ return system === "dark" ? "dark" : "light" ;
196+ }
197+ return theme ;
198+ }
199+
200+ function applyTheme ( theme : ThemeMode ) : void {
201+ const resolvedTheme = resolveThemeForRender ( theme ) ;
202+ document . documentElement . setAttribute ( "data-theme-mode" , theme ) ;
203+ document . documentElement . setAttribute ( "data-theme-mode-resolved" , resolvedTheme ) ;
204+ const label =
205+ theme === "auto"
206+ ? "Theme: Auto"
207+ : theme === "light"
208+ ? "Theme: Light"
209+ : "Theme: Dark" ;
210+ themeToggleButton . textContent = label ;
211+ }
212+
213+ function initializeTheme ( ) : ThemeMode {
214+ const initial = getSavedTheme ( ) ?? "auto" ;
215+ applyTheme ( initial ) ;
216+ return initial ;
217+ }
218+
161219function saveToken ( value : string ) : void {
162220 const token = value . trim ( ) ;
163221 try {
@@ -565,6 +623,30 @@ applyButton.addEventListener("click", () => {
565623 void mountFromForm ( ) ;
566624} ) ;
567625
626+ let activeTheme : ThemeMode = initializeTheme ( ) ;
627+
628+ themeToggleButton . addEventListener ( "click" , ( ) => {
629+ activeTheme = activeTheme === "auto" ? "light" : activeTheme === "light" ? "dark" : "auto" ;
630+ saveTheme ( activeTheme ) ;
631+ applyTheme ( activeTheme ) ;
632+ } ) ;
633+
634+ try {
635+ const media = window . matchMedia ( "(prefers-color-scheme: dark)" ) ;
636+ const handleSystemThemeChange = ( ) => {
637+ if ( activeTheme === "auto" ) {
638+ applyTheme ( "auto" ) ;
639+ }
640+ } ;
641+ if ( typeof media . addEventListener === "function" ) {
642+ media . addEventListener ( "change" , handleSystemThemeChange ) ;
643+ } else if ( typeof media . addListener === "function" ) {
644+ media . addListener ( handleSystemThemeChange ) ;
645+ }
646+ } catch {
647+ // Ignore unavailable matchMedia support in preview harness.
648+ }
649+
568650loginButton . addEventListener ( "click" , ( ) => {
569651 void startLoginRedirect ( ) ;
570652} ) ;
0 commit comments