Initial commit — Meridian OS browser desktop demo
This commit is contained in:
133
js/apps/steam.js
Normal file
133
js/apps/steam.js
Normal file
@@ -0,0 +1,133 @@
|
||||
// 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);
|
||||
}
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user