Skip to content

Commit

Permalink
UI themes (#600)
Browse files Browse the repository at this point in the history
* Revert "Enable PSRAM (#468)"

This reverts commit e67aa1d.

* add color themes

* enable persistant themes

* change back to origional red

* add default theme 'dark-red'. subscribe on home component to the ThemeService
  • Loading branch information
WantClue authored Dec 28, 2024
1 parent d9ee113 commit 188336c
Show file tree
Hide file tree
Showing 14 changed files with 737 additions and 60 deletions.
1 change: 1 addition & 0 deletions main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ SRCS
"lv_font_portfolio-6x8.c"
"logo.c"
"./http_server/http_server.c"
"./http_server/theme_api.c"
"./self_test/self_test.c"
"./tasks/stratum_task.c"
"./tasks/create_jobs_task.c"
Expand Down
4 changes: 3 additions & 1 deletion main/http_server/axe-os/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { LogsComponent } from './components/logs/logs.component';
import { NetworkComponent } from './components/network/network.component';
import { SettingsComponent } from './components/settings/settings.component';
import { SwarmComponent } from './components/swarm/swarm.component';
import { ThemeConfigComponent } from './components/settings/theme-config.component';
import { AppLayoutModule } from './layout/app.layout.module';
import { ANSIPipe } from './pipes/ansi.pipe';
import { DateAgoPipe } from './pipes/date-ago.pipe';
Expand Down Expand Up @@ -45,7 +46,8 @@ const components = [
DateAgoPipe,
SwarmComponent,
SettingsComponent,
HashSuffixPipe
HashSuffixPipe,
ThemeConfigComponent
],
imports: [
BrowserModule,
Expand Down
54 changes: 47 additions & 7 deletions main/http_server/axe-os/src/app/components/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component } from '@angular/core';
import { interval, map, Observable, shareReplay, startWith, switchMap, tap } from 'rxjs';
import { HashSuffixPipe } from 'src/app/pipes/hash-suffix.pipe';
import { SystemService } from 'src/app/services/system.service';
import { ThemeService } from 'src/app/services/theme.service';
import { eASICModel } from 'src/models/enum/eASICModel';
import { ISystemInfo } from 'src/models/ISystemInfo';

Expand All @@ -12,11 +13,10 @@ import { ISystemInfo } from 'src/models/ISystemInfo';
})
export class HomeComponent {

public info$: Observable<ISystemInfo>;

public quickLink$: Observable<string | undefined>;
public fallbackQuickLink$: Observable<string | undefined>;
public expectedHashRate$: Observable<number | undefined>;
public info$!: Observable<ISystemInfo>;
public quickLink$!: Observable<string | undefined>;
public fallbackQuickLink$!: Observable<string | undefined>;
public expectedHashRate$!: Observable<number | undefined>;


public chartOptions: any;
Expand All @@ -31,9 +31,50 @@ export class HomeComponent {
public maxFrequency: number = 800;

constructor(
private systemService: SystemService
private systemService: SystemService,
private themeService: ThemeService
) {
this.initializeChart();

// Subscribe to theme changes
this.themeService.getThemeSettings().subscribe(() => {
this.updateChartColors();
});
}

private updateChartColors() {
const documentStyle = getComputedStyle(document.documentElement);
const textColor = documentStyle.getPropertyValue('--text-color');
const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary');
const surfaceBorder = documentStyle.getPropertyValue('--surface-border');
const primaryColor = documentStyle.getPropertyValue('--primary-color');

// Update chart colors
if (this.chartData && this.chartData.datasets) {
this.chartData.datasets[0].backgroundColor = primaryColor + '30';
this.chartData.datasets[0].borderColor = primaryColor;
this.chartData.datasets[1].backgroundColor = primaryColor + '30';
this.chartData.datasets[1].borderColor = primaryColor + '60';
this.chartData.datasets[2].backgroundColor = textColorSecondary;
this.chartData.datasets[2].borderColor = textColorSecondary;
}

// Update chart options
if (this.chartOptions) {
this.chartOptions.plugins.legend.labels.color = textColor;
this.chartOptions.scales.x.ticks.color = textColorSecondary;
this.chartOptions.scales.x.grid.color = surfaceBorder;
this.chartOptions.scales.y.ticks.color = textColorSecondary;
this.chartOptions.scales.y.grid.color = surfaceBorder;
this.chartOptions.scales.y2.ticks.color = textColorSecondary;
this.chartOptions.scales.y2.grid.color = surfaceBorder;
}

// Force chart update
this.chartData = { ...this.chartData };
}

private initializeChart() {
const documentStyle = getComputedStyle(document.documentElement);
const textColor = documentStyle.getPropertyValue('--text-color');
const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary');
Expand Down Expand Up @@ -243,4 +284,3 @@ export class HomeComponent {
return stratumURL.startsWith('http') ? stratumURL : `http://${stratumURL}`;
}
}

Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
<div class="card">
<h2>Settings</h2>

<app-edit></app-edit>
</div>


<div class="grid">
<div class="col-12">
<app-theme-config></app-theme-config>
</div>
</div>

<div class="grid">
<div class="col-12 lg:col-6 xl:col-4">
<div class="card" *ngIf="checkLatestRelease == false">
<h5>Current Version: {{(info$ | async)?.version}}</h5>
<h5>Current Version: {{(info$ | async)?.version}}</h5>
<h2>Latest Release: <p-button (onClick)="checkLatestRelease = true">Check</p-button></h2>
<small>Clicking this button will connect to GitHub to check for recent updates</small>
</div>
<div class="card" *ngIf="checkLatestRelease == true">
<ng-container *ngIf="latestRelease$ | async as latestRelease">
<h5>Current Version: {{(info$ | async)?.version}}</h5>
<h5>Current Version: {{(info$ | async)?.version}}</h5>
<h2>Latest Release: {{latestRelease.name}}</h2>

<div *ngFor="let asset of latestRelease.assets">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import { Component, OnInit } from '@angular/core';
import { LayoutService } from '../../layout/service/app.layout.service';
import { ThemeService } from '../../services/theme.service';

interface ThemeOption {
name: string;
primaryColor: string;
accentColors: {
[key: string]: string;
};
}

@Component({
selector: 'app-theme-config',
template: `
<div class="card">
<h5>Theme Configuration</h5>
<div class="grid">
<div class="col-12">
<h6>Color Scheme</h6>
<div class="flex gap-3">
<div class="flex align-items-center">
<p-radioButton name="colorScheme" [value]="'dark'" [(ngModel)]="selectedScheme"
(onClick)="changeColorScheme('dark')" inputId="dark"></p-radioButton>
<label for="dark" class="ml-2">Dark</label>
</div>
<div class="flex align-items-center">
<p-radioButton name="colorScheme" [value]="'light'" [(ngModel)]="selectedScheme"
(onClick)="changeColorScheme('light')" inputId="light"></p-radioButton>
<label for="light" class="ml-2">Light</label>
</div>
</div>
</div>
<div class="col-12">
<h6>Theme Colors</h6>
<div class="grid">
<div *ngFor="let theme of themes" class="col-3">
<button pButton [class]="'p-button-rounded p-button-text color-dot'"
[style.backgroundColor]="theme.primaryColor"
style="width: 2rem; height: 2rem; border: none;"
(click)="changeTheme(theme)">
</button>
<div class="text-sm mt-1">{{theme.name}}</div>
</div>
</div>
</div>
</div>
</div>
`
})
export class ThemeConfigComponent implements OnInit {
selectedScheme: string;
themes: ThemeOption[] = [
{
name: 'Red',
primaryColor: '#F80421',
accentColors: {
'--primary-color': '#F80421',
'--primary-color-text': '#ffffff',
'--highlight-bg': '#F80421',
'--highlight-text-color': '#ffffff',
'--focus-ring': '0 0 0 0.2rem rgba(255,64,50,0.2)',
// PrimeNG Slider
'--slider-bg': '#dee2e6',
'--slider-range-bg': '#F80421',
'--slider-handle-bg': '#F80421',
// Progress Bar
'--progressbar-bg': '#dee2e6',
'--progressbar-value-bg': '#F80421',
// PrimeNG Checkbox
'--checkbox-border': '#F80421',
'--checkbox-bg': '#F80421',
'--checkbox-hover-bg': '#e63c2e',
// PrimeNG Button
'--button-bg': '#F80421',
'--button-hover-bg': '#e63c2e',
'--button-focus-shadow': '0 0 0 2px #ffffff, 0 0 0 4px #F80421',
// Toggle button
'--togglebutton-bg': '#F80421',
'--togglebutton-border': '1px solid #F80421',
'--togglebutton-hover-bg': '#e63c2e',
'--togglebutton-hover-border': '1px solid #e63c2e',
'--togglebutton-text-color': '#ffffff'
}
},
{
name: 'Blue',
primaryColor: '#2196f3',
accentColors: {
'--primary-color': '#2196f3',
'--primary-color-text': '#ffffff',
'--highlight-bg': '#2196f3',
'--highlight-text-color': '#ffffff',
'--focus-ring': '0 0 0 0.2rem rgba(33,150,243,0.2)',
// PrimeNG Slider
'--slider-bg': '#dee2e6',
'--slider-range-bg': '#2196f3',
'--slider-handle-bg': '#2196f3',
// Progress Bar
'--progressbar-bg': '#dee2e6',
'--progressbar-value-bg': '#2196f3',
// PrimeNG Checkbox
'--checkbox-border': '#2196f3',
'--checkbox-bg': '#2196f3',
'--checkbox-hover-bg': '#1e88e5',
// PrimeNG Button
'--button-bg': '#2196f3',
'--button-hover-bg': '#1e88e5',
'--button-focus-shadow': '0 0 0 2px #ffffff, 0 0 0 4px #2196f3',
// Toggle button
'--togglebutton-bg': '#2196f3',
'--togglebutton-border': '1px solid #2196f3',
'--togglebutton-hover-bg': '#1e88e5',
'--togglebutton-hover-border': '1px solid #1e88e5',
'--togglebutton-text-color': '#ffffff'
}
},
{
name: 'Green',
primaryColor: '#4caf50',
accentColors: {
'--primary-color': '#4caf50',
'--primary-color-text': '#ffffff',
'--highlight-bg': '#4caf50',
'--highlight-text-color': '#ffffff',
'--focus-ring': '0 0 0 0.2rem rgba(76,175,80,0.2)',
// PrimeNG Slider
'--slider-bg': '#dee2e6',
'--slider-range-bg': '#4caf50',
'--slider-handle-bg': '#4caf50',
// Progress Bar
'--progressbar-bg': '#dee2e6',
'--progressbar-value-bg': '#4caf50',
// PrimeNG Checkbox
'--checkbox-border': '#4caf50',
'--checkbox-bg': '#4caf50',
'--checkbox-hover-bg': '#43a047',
// PrimeNG Button
'--button-bg': '#4caf50',
'--button-hover-bg': '#43a047',
'--button-focus-shadow': '0 0 0 2px #ffffff, 0 0 0 4px #4caf50',
// Toggle button
'--togglebutton-bg': '#4caf50',
'--togglebutton-border': '1px solid #4caf50',
'--togglebutton-hover-bg': '#43a047',
'--togglebutton-hover-border': '1px solid #43a047',
'--togglebutton-text-color': '#ffffff'
}
},
{
name: 'Purple',
primaryColor: '#9c27b0',
accentColors: {
'--primary-color': '#9c27b0',
'--primary-color-text': '#ffffff',
'--highlight-bg': '#9c27b0',
'--highlight-text-color': '#ffffff',
'--focus-ring': '0 0 0 0.2rem rgba(156,39,176,0.2)',
// PrimeNG Slider
'--slider-bg': '#dee2e6',
'--slider-range-bg': '#9c27b0',
'--slider-handle-bg': '#9c27b0',
// Progress Bar
'--progressbar-bg': '#dee2e6',
'--progressbar-value-bg': '#9c27b0',
// PrimeNG Checkbox
'--checkbox-border': '#9c27b0',
'--checkbox-bg': '#9c27b0',
'--checkbox-hover-bg': '#8e24aa',
// PrimeNG Button
'--button-bg': '#9c27b0',
'--button-hover-bg': '#8e24aa',
'--button-focus-shadow': '0 0 0 2px #ffffff, 0 0 0 4px #9c27b0',
// Toggle button
'--togglebutton-bg': '#9c27b0',
'--togglebutton-border': '1px solid #9c27b0',
'--togglebutton-hover-bg': '#8e24aa',
'--togglebutton-hover-border': '1px solid #8e24aa',
'--togglebutton-text-color': '#ffffff'
}
}
];

constructor(
public layoutService: LayoutService,
private themeService: ThemeService
) {
this.selectedScheme = this.layoutService.config().colorScheme;
}

ngOnInit() {
// Load saved theme settings from NVS
this.themeService.getThemeSettings().subscribe(
settings => {
if (settings && settings.accentColors) {
this.applyThemeColors(settings.accentColors);
}
},
error => console.error('Error loading theme settings:', error)
);
}

private applyThemeColors(colors: { [key: string]: string }) {
Object.entries(colors).forEach(([key, value]) => {
document.documentElement.style.setProperty(key, value);
});
}

changeColorScheme(scheme: string) {
this.selectedScheme = scheme;
const config = { ...this.layoutService.config() };
config.colorScheme = scheme;
this.layoutService.config.set(config);
}

changeTheme(theme: ThemeOption) {
// Update CSS variables
this.applyThemeColors(theme.accentColors);
// Save theme settings to NVS
this.themeService.saveThemeSettings({
colorScheme: this.selectedScheme,
theme: this.layoutService.config().theme,
accentColors: theme.accentColors
}).subscribe(
() => {},
error => console.error('Error saving theme settings:', error)
);
}
}
Loading

0 comments on commit 188336c

Please sign in to comment.