Files
meridian-os/js/apps/steam.js

134 lines
7.0 KiB
JavaScript

// Steam spoof — static-but-believable game library UI.
const GAMES = [
{ id: 'elden', title: 'Elden Ring', desc: 'Explore the Lands Between in this open-world action RPG from FromSoftware. Face legendary bosses, forge your build, and uncover the mystery of the shattered Elden Ring. Every step is a descent into the unknown.' },
{ id: 'cod', title: 'Call of Duty: MW2', desc: 'Relive the global conflict in this first-person shooter campaign. From Operation Deadbolt to the final push, the stakes have never been higher. Multiplayer is included, if you can find it in the files.' },
{ id: 'fifa', title: 'FIFA 24', desc: 'The world\'s game, rendered in full hyper-realistic detail. Play Career Mode, Ultimate Team, or just stare at the menu screen for three hours. Your choice, really.' },
{ id: 'hades', title: 'Hades II', desc: 'Return to the Underworld as Melinoch, sister of Zagreus. Wield witchcraft, summon gods, and break out of the cycle of death and rebirth. Again. This time with more style.' },
{ id: 'factorio', title: 'Factorio', desc: 'Build factories, then build bigger factories, then automate your entire existence into a sprawling industrial complex that will consume all your spare time. There is no escape.' },
{ id: 'stardew', title: 'Stardew Valley', desc: 'Leave the city. Take over your grandfather\'s old farm plot. Grow crops, raise animals, befriend the locals. It\'s a quiet life, and you\'ll never leave.' },
{ id: 'bg3', title: "Baldur's Gate 3", desc: 'Gather your party. Roll the dice. Make the worst possible choice. Larian Studios has crafted a deep RPG experience with consequences that actually matter. Mostly.' },
];
export default {
id: 'steam',
title: 'Steam',
render(container, api) {
let selectedGame = null;
let installing = null;
const root = document.createElement('div');
root.style.cssText = 'height:100%;display:flex;flex-direction:column;background:var(--bg-1);';
// Top bar
const topBar = document.createElement('div');
topBar.style.cssText = 'background:var(--bg-2);padding:8px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;';
const topTitle = document.createElement('span');
topTitle.style.cssText = 'font-size:14px;font-weight:600;color:var(--text);';
topTitle.textContent = 'Steam';
topBar.appendChild(topTitle);
root.appendChild(topBar);
// Body: sidebar + main
const body = document.createElement('div');
body.style.cssText = 'flex:1;display:flex;overflow:hidden;';
// Sidebar
const sidebar = document.createElement('div');
sidebar.style.cssText = 'width:220px;background:var(--bg-2);border-right:1px solid var(--border);display:flex;flex-direction:column;overflow-y:auto;flex-shrink:0;';
const sidebarLabel = document.createElement('div');
sidebarLabel.style.cssText = 'padding:12px 14px 6px;font-size:11px;font-weight:600;color:var(--text-dim);text-transform:uppercase;letter-spacing:0.05em;';
sidebarLabel.textContent = 'Library';
sidebar.appendChild(sidebarLabel);
GAMES.forEach((game) => {
const item = document.createElement('div');
item.style.cssText = 'padding:8px 14px;cursor:pointer;font-size:13px;color:var(--text);transition:background 150ms ease-out;border-left:2px solid transparent;';
item.textContent = game.title;
item.addEventListener('click', () => { selectGame(game); });
item.dataset.gameId = game.id;
sidebar.appendChild(item);
});
// Main area
const mainArea = document.createElement('div');
mainArea.style.cssText = 'flex:1;display:flex;flex-direction:column;overflow-y:auto;padding:24px;';
// Initial state: "Select a game"
const placeholder = document.createElement('div');
placeholder.style.cssText = 'flex:1;display:flex;align-items:center;justify-content:center;color:var(--text-dim);font-size:14px;';
placeholder.textContent = 'Select a game from your library';
mainArea.appendChild(placeholder);
body.appendChild(sidebar);
body.appendChild(mainArea);
root.appendChild(body);
// Status bar
const status = document.createElement('div');
status.style.cssText = 'background:var(--bg-2);border-top:1px solid var(--border);padding:6px 14px;font-size:11px;color:var(--text-dim);flex-shrink:0;';
status.textContent = 'Online \u{B7} 142 friends online \u{B7} 4.7 TB free';
root.appendChild(status);
container.appendChild(root);
function selectGame(game) {
selectedGame = game;
installing = null;
// Clear main area and add game details
while (mainArea.firstChild) mainArea.removeChild(mainArea.firstChild);
// Hero image (gradient block with title overlay)
const hero = document.createElement('div');
hero.style.cssText = 'width:100%;height:200px;background:linear-gradient(135deg, rgba(37,42,49,0.9), rgba(212,162,76,0.2));border:1px solid var(--border);display:flex;align-items:flex-end;padding:20px;margin-bottom:20px;position:relative;overflow:hidden;border-radius:4px;';
const heroOverlay = document.createElement('div');
heroOverlay.style.cssText = 'font-size:32px;font-weight:700;color:var(--text);text-shadow:0 2px 8px rgba(0,0,0,0.6);';
heroOverlay.textContent = game.title;
hero.appendChild(heroOverlay);
mainArea.appendChild(hero);
// Description
const desc = document.createElement('p');
desc.style.cssText = 'font-size:13px;color:var(--text);line-height:1.7;margin-bottom:20px;max-width:600px;';
desc.textContent = game.desc;
mainArea.appendChild(desc);
// Install button
const installBtn = document.createElement('button');
installBtn.style.cssText = 'padding:10px 28px;background:var(--accent);color:#0f1114;border:none;border-radius:4px;font-size:14px;font-weight:600;cursor:pointer;transition:opacity 150ms ease-out;width:fit-content;';
installBtn.textContent = 'Install';
installBtn.addEventListener('click', () => startInstall(game, installBtn));
mainArea.appendChild(installBtn);
}
function startInstall(game, btn) {
installing = game.id;
btn.textContent = 'Downloading...';
btn.disabled = true;
const progressWrap = document.createElement('div');
progressWrap.style.cssText = 'width:280px;height:6px;background:var(--bg-3);border-radius:3px;margin-top:12px;overflow:hidden;';
const progressFill = document.createElement('div');
progressFill.style.cssText = 'width:0%;height:100%;background:var(--accent);border-radius:3px;transition:width 2s linear;';
progressWrap.appendChild(progressFill);
mainArea.appendChild(progressWrap);
// Animate progress over 2s
requestAnimationFrame(() => {
progressFill.style.width = '100%';
});
setTimeout(() => {
btn.textContent = 'Installed (in your imagination)';
btn.style.background = 'var(--bg-3)';
btn.style.color = 'var(--text-dim)';
btn.style.border = '1px solid var(--border)';
btn.disabled = false;
installing = null;
progressWrap.remove();
}, 2100);
}
},
};