Files
meridian-os/js/widgets/wallpaper.js

139 lines
3.7 KiB
JavaScript

import State from '../core/state.js';
import { Bus } from '../core/bus.js';
const WALLPAPERS = {
graphite: {
name: 'Graphite',
gradient: 'radial-gradient(ellipse at center, #1e2126 0%, #0f1114 100%)'
},
ember: {
name: 'Ember',
gradient: 'linear-gradient(135deg, #1a1d21 0%, #2a1f15 40%, #d4a24c33 100%)'
},
tide: {
name: 'Tide',
gradient: 'linear-gradient(160deg, #0a1628 0%, #0f2847 40%, #14425a 70%, #1a5c6e 100%)'
},
vellum: {
name: 'Vellum',
gradient: 'linear-gradient(145deg, #1e1c1a 0%, #2a2520 50%, #332e28 100%)'
},
eclipse: {
name: 'Eclipse',
gradient: 'linear-gradient(160deg, #0a0a0f 0%, #150f20 50%, #1a1030 100%)'
},
terminal: {
name: 'Terminal',
gradient: 'radial-gradient(ellipse at 20% 50%, #0a1a0a 0%, #0f1410 40%, #0a0f0a 100%)'
}
};
let currentWp = null;
let applying = false;
function applyWallpaper(id) {
if (applying) return;
const wp = WALLPAPERS[id];
if (!wp) return;
const layer = document.getElementById('wallpaper-layer');
if (!layer) return;
applying = true;
// Crossfade
const overlay = document.createElement('div');
overlay.style.position = 'absolute';
overlay.style.inset = '0';
overlay.style.background = wp.gradient;
overlay.style.opacity = '0';
overlay.style.transition = 'opacity 250ms ease';
overlay.style.zIndex = '1';
layer.appendChild(overlay);
requestAnimationFrame(() => {
overlay.style.opacity = '1';
if (currentWp && currentWp !== overlay) {
const old = currentWp;
old.style.opacity = '0';
setTimeout(() => old.remove(), 300);
}
currentWp = overlay;
});
if (State.get('wallpaper') !== id) {
State.set('wallpaper', id);
}
applying = false;
// Update picker swatches
document.querySelectorAll('.wallpaper-swatch').forEach(s => {
s.classList.toggle('active', s.dataset.id === id);
});
}
function initPicker() {
const picker = document.getElementById('wallpaper-picker');
const grid = document.getElementById('wallpaper-picker-grid');
if (!picker || !grid) return;
// Populate swatches
for (const [id, wp] of Object.entries(WALLPAPERS)) {
const swatch = document.createElement('div');
swatch.className = 'wallpaper-swatch';
swatch.dataset.id = id;
swatch.setAttribute('role', 'button');
swatch.setAttribute('aria-label', `Select ${wp.name} wallpaper`);
swatch.style.background = wp.gradient;
swatch.innerHTML = `<span class="wallpaper-swatch-label">${wp.name}</span>`;
swatch.addEventListener('click', () => applyWallpaper(id));
grid.appendChild(swatch);
}
// Apply saved wallpaper
const saved = State.get('wallpaper');
if (saved && WALLPAPERS[saved]) {
applyWallpaper(saved);
}
// Toggle button in status bar
const toggleBtn = document.getElementById('wallpaper-toggle');
if (toggleBtn) {
toggleBtn.addEventListener('click', () => {
picker.classList.toggle('hidden');
});
}
// Close picker on backdrop click
picker.addEventListener('click', (e) => {
if (e.target === picker) {
picker.classList.add('hidden');
}
});
// Close on Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && !picker.classList.contains('hidden')) {
picker.classList.add('hidden');
}
});
}
function init() {
// Listen for state changes to wallpaper (external triggers only; applying flag prevents recursion)
State.subscribe('wallpaper', (id) => {
if (id && WALLPAPERS[id]) {
applyWallpaper(id);
}
});
// Apply saved or default
const saved = State.get('wallpaper');
const wpId = (saved && WALLPAPERS[saved]) ? saved : 'graphite';
applyWallpaper(wpId);
initPicker();
}
init();