Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI themes #600

Merged
merged 6 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading