Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,37 @@
</div>
</div>

<h3 class="mt-5">Presets</h3>

<div class="card">
<div class="field grid p-fluid">
<label class="col-12 md:col-2 md:m-0">Name</label>
<div class="col-12 md:col-7">
<input pInputText [formControl]="presetName" placeholder="e.g. Quiet / Turbo" />
</div>
<div class="col-12 md:col-3">
<button pButton type="button"
[label]="editingIndex === null ? 'Save Preset' : 'Update'"
[disabled]="!presetName.value || (presets.length >= maxPresets && editingIndex === null)"
(click)="addOrUpdatePreset()"></button>
</div>
</div>
<small class="block mb-3">Captures the current Frequency and Core Voltage above. Max {{ maxPresets }} presets.</small>

<div *ngFor="let p of presets; let i = index" class="flex align-items-center gap-2 py-1">
<span class="flex-1">{{ p.name }} — {{ p.frequency }} MHz / {{ p.coreVoltage }} mV</span>
<button pButton type="button" label="Apply" (click)="applyPreset(p)"></button>
<button pButton type="button" label="Edit" class="p-button-secondary" (click)="startEditPreset(i)"></button>
<button pButton type="button" icon="pi pi-trash" class="p-button-danger" (click)="deletePreset(i)"></button>
</div>

<div class="flex gap-3 mt-3">
<button pButton type="button" label="Export" class="p-button-secondary" (click)="exportPresets()"></button>
<button pButton type="button" label="Import" class="p-button-secondary" (click)="fileInput.click()"></button>
<input #fileInput type="file" accept="application/json" hidden (change)="importPresets($event)" />
</div>
</div>

<h3 class="mt-5">Display</h3>

<div class="card">
Expand Down
131 changes: 131 additions & 0 deletions main/http_server/axe-os/src/app/components/edit/edit.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,22 @@ import { SliderModule } from 'primeng/slider';
import { TooltipModule } from 'primeng/tooltip';
import { InputTextModule } from 'primeng/inputtext';
import { DateAgoPipe } from 'src/app/pipes/date-ago.pipe';
import { LocalStorageService } from 'src/app/local-storage.service';

type SelectOption = {
name: string;
value: number;
};

type Preset = {
name: string;
frequency: number;
coreVoltage: number;
};

const PRESETS_KEY = 'ASIC_PRESETS';
const MAX_PRESETS = 5;

const DISPLAY_TIMEOUT_STEPS = [0, 1, 2, 5, 15, 30, 60, 60 * 2, 60 * 4, 60* 8, -1];
const STATS_FREQUENCY_STEPS = [0, 1, 2, 5, 10, 30, 60, 60 * 2, 60 * 6, 60 * 14, 60 * 28, 60 * 60];

Expand Down Expand Up @@ -68,13 +78,19 @@ export class EditComponent implements OnInit, OnDestroy, OnChanges {
public statsFrequencyControl: FormControl;
public statsLimit: number = 720;

public presets: Preset[] = [];
public presetName = new FormControl('');
public editingIndex: number | null = null;
public readonly maxPresets = MAX_PRESETS;

constructor(
private fb: FormBuilder,
private systemService: SystemApiService,
private liveDataService: LiveDataService,
private toastr: ToastrService,
private loadingService: LoadingService,
private route: ActivatedRoute,
private localStorageService: LocalStorageService,
) {
// Check URL parameter for settings unlock
this.route.queryParams.subscribe(params => {
Expand Down Expand Up @@ -136,6 +152,7 @@ export class EditComponent implements OnInit, OnDestroy, OnChanges {

ngOnInit(): void {
this.loadDeviceSettings();
this.loadPresets();
}

ngOnChanges(changes: SimpleChanges): void {
Expand Down Expand Up @@ -271,6 +288,120 @@ export class EditComponent implements OnInit, OnDestroy, OnChanges {
});
}

private loadPresets(): void {
this.presets = this.localStorageService.getObject(PRESETS_KEY) ?? [];
}

private savePresets(): void {
this.localStorageService.setObject(PRESETS_KEY, this.presets);
}

public addOrUpdatePreset(): void {
const name = (this.presetName.value || '').trim();
const frequency = this.form.get('frequency')?.value;
const coreVoltage = this.form.get('coreVoltage')?.value;

if (!name) {
this.toastr.warning('Please enter a preset name.');
return;
}
if (typeof frequency !== 'number' || typeof coreVoltage !== 'number') {
this.toastr.warning('Set a valid Frequency and Core Voltage first.');
return;
}

if (this.editingIndex !== null) {
this.presets[this.editingIndex] = { name, frequency, coreVoltage };
} else {
if (this.presets.length >= MAX_PRESETS) {
this.toastr.warning(`You can save at most ${MAX_PRESETS} presets.`);
return;
}
this.presets.push({ name, frequency, coreVoltage });
}

this.savePresets();
this.presetName.reset('');
this.editingIndex = null;
}

public startEditPreset(index: number): void {
this.editingIndex = index;
this.presetName.setValue(this.presets[index].name);
}

public deletePreset(index: number): void {
this.presets.splice(index, 1);
this.savePresets();
if (this.editingIndex === index) {
this.editingIndex = null;
this.presetName.reset('');
}
}

public applyPreset(preset: Preset): void {
this.form.patchValue({ frequency: preset.frequency, coreVoltage: preset.coreVoltage });
this.form.controls['frequency'].markAsDirty();
this.form.controls['coreVoltage'].markAsDirty();

this.systemService.updateSystem(this.uri || '', {
frequency: preset.frequency,
coreVoltage: preset.coreVoltage
})
.pipe(this.loadingService.lockUIUntilComplete())
.subscribe({
next: () => this.toastr.success(`Applied preset "${preset.name}"`),
error: (err: HttpErrorResponse) => this.toastr.error(`Could not apply preset. ${err.message}`)
});
}

public exportPresets(): void {
const blob = new Blob([JSON.stringify(this.presets, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.href = url;
anchor.download = 'axeos-presets.json';
anchor.click();
URL.revokeObjectURL(url);
}

public importPresets(event: Event): void {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (!file) {
return;
}

const reader = new FileReader();
reader.onload = () => {
try {
const parsed = JSON.parse(reader.result as string);
if (!Array.isArray(parsed)) {
throw new Error('Expected an array of presets');
}
const valid = parsed.filter((p: any) =>
p && typeof p.name === 'string' &&
typeof p.frequency === 'number' &&
typeof p.coreVoltage === 'number'
).slice(0, MAX_PRESETS);

this.presets = valid.map((p: any) => ({
name: p.name,
frequency: p.frequency,
coreVoltage: p.coreVoltage
}));
this.savePresets();
this.editingIndex = null;
this.presetName.reset('');
this.toastr.success(`Imported ${this.presets.length} preset(s).`);
} catch (err: any) {
this.toastr.error(`Could not import presets. ${err.message}`);
}
};
reader.readAsText(file);
input.value = '';
}

disableOverheatMode() {
this.form.patchValue({ overheat_mode: 0 });
this.updateSystem();
Expand Down