Add theme system for customizable backgrounds

- Move backgrounds to themed folders (backgrounds/desert/, backgrounds/custom/)
- Add theme config option in config.json (default: "desert")
- Add /api/config endpoint to serve theme and location
- Update pixel-view.js to load backgrounds from theme folder
- Add config.json.example for reference
- Update CONFIG.md documentation

Users can now set "theme": "custom" and place their own backgrounds
in backgrounds/custom/ to customize the view for their location.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
root
2026-01-20 12:03:37 -08:00
parent 96ce78accc
commit 1f8b69e846
10 changed files with 140 additions and 26 deletions

View File

@@ -65,6 +65,9 @@ class PixelADSB {
this.viewDirectionNames = { 0: 'N', 90: 'E', 180: 'S', 270: 'W' };
this.fieldOfView = 90; // 90 degree field of view
// Theme for background images (loaded from config)
this.theme = 'desert';
// Hover and selection tracking
this.mouseX = -1;
this.mouseY = -1;
@@ -109,7 +112,7 @@ class PixelADSB {
});
// Load environment images (directional backgrounds, base, sun, clouds)
// Directional background images
// Directional background images (loaded after fetching config)
this.backgroundImages = {
0: new Image(), // North
90: new Image(), // East
@@ -118,24 +121,6 @@ class PixelADSB {
};
this.backgroundImagesLoaded = { 0: false, 90: false, 180: false, 270: false };
// Load directional backgrounds (fallback to desert.png if not available)
const directions = [
{ deg: 0, name: 'north' },
{ deg: 90, name: 'east' },
{ deg: 180, name: 'south' },
{ deg: 270, name: 'west' }
];
directions.forEach(dir => {
this.backgroundImages[dir.deg].onload = () => {
this.backgroundImagesLoaded[dir.deg] = true;
console.log(`${dir.name}.png loaded`);
};
this.backgroundImages[dir.deg].onerror = () => {
console.warn(`Failed to load ${dir.name}.png, using fallback`);
};
this.backgroundImages[dir.deg].src = `${dir.name}.png?v=1`;
});
this.sunImage = new Image();
this.sunImage.onload = () => {
this.sunImageLoaded = true;
@@ -410,8 +395,11 @@ class PixelADSB {
}
async init() {
// Fetch receiver location
await this.fetchReceiverLocation();
// Fetch config (includes theme and location)
await this.fetchConfig();
// Load background images based on theme
this.loadBackgroundImages();
// Fetch weather
await this.fetchWeather();
@@ -547,6 +535,41 @@ class PixelADSB {
return padding + (normalizedAngle * usableWidth);
}
async fetchConfig() {
try {
const response = await fetch('/api/config');
const data = await response.json();
this.theme = data.theme || 'desert';
this.receiverLat = data.location?.lat || 0;
this.receiverLon = data.location?.lon || 0;
this.locationName = data.location?.name || 'My Location';
console.log(`Config loaded - Theme: ${this.theme}, Location: ${this.locationName} (${this.receiverLat}, ${this.receiverLon})`);
} catch (error) {
console.warn('Could not fetch config, using defaults');
// Fallback to receiver-location API for backwards compatibility
await this.fetchReceiverLocation();
}
}
loadBackgroundImages() {
const directions = [
{ deg: 0, name: 'north' },
{ deg: 90, name: 'east' },
{ deg: 180, name: 'south' },
{ deg: 270, name: 'west' }
];
directions.forEach(dir => {
this.backgroundImages[dir.deg].onload = () => {
this.backgroundImagesLoaded[dir.deg] = true;
console.log(`${this.theme}/${dir.name}.png loaded`);
};
this.backgroundImages[dir.deg].onerror = () => {
console.warn(`Failed to load backgrounds/${this.theme}/${dir.name}.png`);
};
this.backgroundImages[dir.deg].src = `backgrounds/${this.theme}/${dir.name}.png?v=1`;
});
}
async fetchReceiverLocation() {
try {
// Fetch from same server that serves pixel-view