From df8baa146a6b9727dca35312d05f76951b67f2c8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 31 Oct 2026 06:40:03 +0000 Subject: [PATCH] Add Light, Dark, and System theme choices to the console. The workspace keeps one saved preference and still paints with the existing light or dark body class, including when System follows the OS scheme. Co-authored-by: harsh --- apps/36-blocks/src/app/app.component.ts | 3 +- .../create-feature.component.ts | 2 +- .../widget-preview.component.html | 13 +- .../widget-preview.component.ts | 12 +- .../app/panel/layout/layout.component.html | 9 +- .../src/app/panel/layout/layout.component.ts | 22 +--- .../sidebar-user-menu.component.html | 8 +- .../sidebar-user-menu.component.scss | 9 ++ .../sidebar-user-menu.component.ts | 8 +- .../src/app/panel/layout/theme-preference.ts | 81 ++++++++++++ .../theme-switcher.component.html | 26 ++++ .../theme-switcher.component.scss | 78 ++++++++++++ .../theme-switcher.component.ts | 24 ++++ .../app/panel/layout/ui-settings.service.ts | 120 +++++++++++------- .../src/assets/scss/base/_dark-theme.scss | 1 + .../src/assets/scss/base/_light-theme.scss | 1 + .../assets/scss/component/_experience.scss | 8 ++ apps/36-blocks/src/index.html | 29 +++++ docs/modules/panel-ops.md | 11 +- docs/ui/design-system.md | 4 +- docs/ui/routes.md | 2 +- docs/ui/state.md | 6 +- tools/tests/theme-preference.test.cjs | 67 ++++++++++ 23 files changed, 439 insertions(+), 105 deletions(-) create mode 100644 apps/36-blocks/src/app/panel/layout/sidebar-user-menu/sidebar-user-menu.component.scss create mode 100644 apps/36-blocks/src/app/panel/layout/theme-preference.ts create mode 100644 apps/36-blocks/src/app/panel/layout/theme-switcher/theme-switcher.component.html create mode 100644 apps/36-blocks/src/app/panel/layout/theme-switcher/theme-switcher.component.scss create mode 100644 apps/36-blocks/src/app/panel/layout/theme-switcher/theme-switcher.component.ts create mode 100644 tools/tests/theme-preference.test.cjs diff --git a/apps/36-blocks/src/app/app.component.ts b/apps/36-blocks/src/app/app.component.ts index fcd9bdd2..89694e28 100644 --- a/apps/36-blocks/src/app/app.component.ts +++ b/apps/36-blocks/src/app/app.component.ts @@ -107,7 +107,8 @@ export class AppComponent extends BaseComponent implements OnInit, OnDestroy { } public ngOnInit(): void { - this.uiSettings.setTheme(this.uiSettings.theme); + // Keep the root theme service alive for the whole session, including system-scheme changes. + this.uiSettings.themePreference(); this.logoutActionComplete$.subscribe((res) => { if (res) { diff --git a/apps/36-blocks/src/app/panel/features/create-feature/create-feature.component.ts b/apps/36-blocks/src/app/panel/features/create-feature/create-feature.component.ts index 5447bf4c..9730cb75 100644 --- a/apps/36-blocks/src/app/panel/features/create-feature/create-feature.component.ts +++ b/apps/36-blocks/src/app/panel/features/create-feature/create-feature.component.ts @@ -771,7 +771,7 @@ export class CreateFeatureComponent extends BaseComponent implements OnDestroy, /** Default primary color: black in light theme, white in dark theme */ public getDefaultPrimaryColor(): string { - return this.uiSettings.theme === 'dark-theme' ? '#ffffff' : '#000000'; + return this.uiSettings.isDark() ? '#ffffff' : '#000000'; } /** Returns the effective primary color based on the selected branding theme */ diff --git a/apps/36-blocks/src/app/panel/features/create-feature/feature-preview/widget-preview/widget-preview.component.html b/apps/36-blocks/src/app/panel/features/create-feature/feature-preview/widget-preview/widget-preview.component.html index 86391b44..c5153be7 100644 --- a/apps/36-blocks/src/app/panel/features/create-feature/feature-preview/widget-preview/widget-preview.component.html +++ b/apps/36-blocks/src/app/panel/features/create-feature/feature-preview/widget-preview/widget-preview.component.html @@ -41,14 +41,13 @@

Widget Preview

} +
+

Console theme

+ +
@if (logInData$ | async; as user) { - + } @@ -59,7 +58,7 @@

Widget Preview

hideSingleSelectionIndicator [value]="theme()" (change)="onThemeChange($event.value)" - aria-label="Select theme" + aria-label="Widget preview theme" > diff --git a/apps/36-blocks/src/app/panel/features/create-feature/feature-preview/widget-preview/widget-preview.component.ts b/apps/36-blocks/src/app/panel/features/create-feature/feature-preview/widget-preview/widget-preview.component.ts index 337b8146..2902c182 100644 --- a/apps/36-blocks/src/app/panel/features/create-feature/feature-preview/widget-preview/widget-preview.component.ts +++ b/apps/36-blocks/src/app/panel/features/create-feature/feature-preview/widget-preview/widget-preview.component.ts @@ -30,7 +30,7 @@ import { selectLogInData } from '../../../../../website/home/ngrx/selector/login import { ILogInFeatureStateWithRootState } from '../../../../../website/home/ngrx/store/login.state'; import * as logInActions from '../../../../../website/home/ngrx/actions/login.action'; import { SidebarUserMenuComponent } from '../../../../layout/sidebar-user-menu/sidebar-user-menu.component'; -import { UiSettingsService } from '../../../../layout/ui-settings.service'; +import { ThemeSwitcherComponent } from '../../../../layout/theme-switcher/theme-switcher.component'; import { SideNavService } from '../../../../layout/side-nav.service'; import { Location } from '@angular/common'; import { environment } from '../../../../../../environments/environment'; @@ -61,6 +61,7 @@ export type PreviewTab = CopyButtonComponent, A11yModule, SidebarUserMenuComponent, + ThemeSwitcherComponent, ], templateUrl: './widget-preview.component.html', changeDetection: ChangeDetectionStrategy.OnPush, @@ -74,13 +75,11 @@ export class WidgetPreviewComponent { private breakpointObserver = inject(BreakpointObserver); private readonly _injector = inject(Injector); private readonly store = inject>(Store); - private readonly uiSettings = inject(UiSettingsService); private readonly sideNavService = inject(SideNavService); @ViewChild('drawer') drawer!: MatDrawer; public readonly isMobile = signal(false); - public readonly isDarkMode = signal(false); public readonly logInData$: Observable; private readonly originUrl: string | null; @@ -129,7 +128,6 @@ export class WidgetPreviewComponent { constructor() { this.originUrl = this.resolveOriginUrl(); this.logInData$ = this.store.pipe(select(selectLogInData), distinctUntilChanged(isEqual)); - this.isDarkMode.set(this.uiSettings.theme === 'dark-theme'); this.breakpointObserver .observe([Breakpoints.XSmall, Breakpoints.Small]) .pipe(takeUntilDestroyed()) @@ -260,12 +258,6 @@ export class WidgetPreviewComponent { this.store.dispatch(logInActions.logoutAction()); } - public switchedDarkMode(isDark: boolean): void { - const hostClass = isDark ? 'dark-theme' : 'light-theme'; - this.uiSettings.setTheme(hostClass); - this.isDarkMode.set(isDark); - } - public launchWidget(): void { const tab = this.activeTab(); const isAuthTab = tab === PublicScriptType.Authorization; diff --git a/apps/36-blocks/src/app/panel/layout/layout.component.html b/apps/36-blocks/src/app/panel/layout/layout.component.html index 1e32c1d9..1630d085 100644 --- a/apps/36-blocks/src/app/panel/layout/layout.component.html +++ b/apps/36-blocks/src/app/panel/layout/layout.component.html @@ -94,13 +94,7 @@ > @if (logInData$ | async; as user) { - + }
+ Console: {{ environmentLabel }}Developer docs ↗ diff --git a/apps/36-blocks/src/app/panel/layout/layout.component.ts b/apps/36-blocks/src/app/panel/layout/layout.component.ts index cabd033b..cd7984b9 100644 --- a/apps/36-blocks/src/app/panel/layout/layout.component.ts +++ b/apps/36-blocks/src/app/panel/layout/layout.component.ts @@ -4,6 +4,7 @@ import { Component, OnDestroy, OnInit, + effect, inject, signal, } from '@angular/core'; @@ -22,6 +23,7 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { ScrollingModule } from '@angular/cdk/scrolling'; import { MainLeftSideNavComponent } from './main-left-side-nav/main-left-side-nav.component'; import { SidebarUserMenuComponent } from './sidebar-user-menu/sidebar-user-menu.component'; +import { ThemeSwitcherComponent } from './theme-switcher/theme-switcher.component'; import { IClient, IClientSettings, IFirebaseUserModel, IPaginatedResponse } from '@proxy/models/root-models'; import { BaseComponent } from '@proxy/ui/base-component'; import { Store, select } from '@ngrx/store'; @@ -59,6 +61,7 @@ import { take } from 'rxjs/operators'; ScrollingModule, MainLeftSideNavComponent, SidebarUserMenuComponent, + ThemeSwitcherComponent, ], host: { class: 'flex w-full h-screen' }, templateUrl: './layout.component.html', @@ -70,8 +73,6 @@ export class LayoutComponent extends BaseComponent implements OnInit, OnDestroy public clients$: Observable>; public swtichClientSuccess$: Observable; - public isDarkMode = signal(false); - public toggleMenuSideBar: boolean; public showContainer = false; readonly currentArea = signal('Overview'); @@ -114,6 +115,9 @@ export class LayoutComponent extends BaseComponent implements OnInit, OnDestroy constructor() { super(); + effect(() => { + this.sendThemeToChatbot(this.uiSettings.isDark() ? 'dark' : 'light'); + }); this.logInData$ = this.store.pipe( select(selectLogInData), distinctUntilChanged(isEqual), @@ -169,7 +173,6 @@ export class LayoutComponent extends BaseComponent implements OnInit, OnDestroy ngOnInit(): void { this.getClients(); this.getClientSettings(); - this.getCurrentTheme(); document.body.classList.remove('website-active'); if (this.isMobileDevice()) { @@ -272,19 +275,6 @@ export class LayoutComponent extends BaseComponent implements OnInit, OnDestroy this.store.dispatch(rootActions.switchClient({ request: { client_id: clientId } })); } - public getCurrentTheme() { - this.switchedDarkMode(this.uiSettings.theme === 'dark-theme'); - } - - public switchedDarkMode(isDarkMode: boolean) { - const hostClass = isDarkMode ? 'dark-theme' : 'light-theme'; - this.uiSettings.setTheme(hostClass); - this.isDarkMode.set(isDarkMode); - - // This GTWY chatbot config for forced theme - this.sendThemeToChatbot(this.isDarkMode() ? 'dark' : 'light'); - } - private sendThemeToChatbot(theme: 'light' | 'dark'): void { (window as any).Chatbot?.sendData({ theme }); } diff --git a/apps/36-blocks/src/app/panel/layout/sidebar-user-menu/sidebar-user-menu.component.html b/apps/36-blocks/src/app/panel/layout/sidebar-user-menu/sidebar-user-menu.component.html index 9d18af47..000f59b6 100644 --- a/apps/36-blocks/src/app/panel/layout/sidebar-user-menu/sidebar-user-menu.component.html +++ b/apps/36-blocks/src/app/panel/layout/sidebar-user-menu/sidebar-user-menu.component.html @@ -33,10 +33,10 @@ }

{{ user.displayName }}

- +
+

Theme

+ +