Redesign index
This commit is contained in:
parent
7b9019bd9d
commit
87f582590b
|
|
@ -13,17 +13,16 @@ const publish = process.env.PUBLISH_LOCATION;
|
|||
|
||||
const args = process.argv.slice(2);
|
||||
const local = args.includes('--local');
|
||||
const exported = args.includes('--exported');
|
||||
let game = args.find(a => !a.startsWith('-')) ?? '';
|
||||
|
||||
while (!await isGame(game)) {
|
||||
game = await select({
|
||||
message: 'Game to build:',
|
||||
choices: (await getGames()).map(value => ({ value })),
|
||||
choices: (await getGames()).map(game => ({ value: game.name })),
|
||||
});
|
||||
}
|
||||
console.log(`Building ${game}...`);
|
||||
const html = await buildHTML(game, { production: true, local, exported });
|
||||
const html = await buildHTML(game, { production: true, local });
|
||||
|
||||
if (!html) {
|
||||
process.exit(1);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import filePlugin from './plugins/filePlugin';
|
|||
|
||||
import { getGames } from './isGame';
|
||||
|
||||
const b64 = async (file: string | BunFile) => (
|
||||
export const b64 = async (file: string | BunFile) => (
|
||||
typeof file === 'string'
|
||||
? Buffer.from(file)
|
||||
: Buffer.from(await file.arrayBuffer())
|
||||
|
|
@ -33,7 +33,6 @@ interface Args {
|
|||
production?: boolean;
|
||||
mobile?: boolean;
|
||||
local?: boolean;
|
||||
exported?: boolean;
|
||||
}
|
||||
|
||||
const SW_SCRIPT = `<script>
|
||||
|
|
@ -49,8 +48,7 @@ if ('serviceWorker' in navigator) {
|
|||
const CSS_RELOAD_SCRIPT = `<script>
|
||||
function connect() {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const game = params.get('game') || '';
|
||||
if (!game) return;
|
||||
const game = params.get('game') || 'index';
|
||||
const ws = new WebSocket('css-ws?game=' + encodeURIComponent(game));
|
||||
const onMessage = (e) => {
|
||||
try {
|
||||
|
|
@ -61,7 +59,7 @@ function connect() {
|
|||
}
|
||||
} catch { }
|
||||
};
|
||||
const onClose = () => {
|
||||
const onClose = (e) => {
|
||||
ws.removeEventListener('message', onMessage);
|
||||
ws.removeEventListener('close', onClose);
|
||||
setTimeout(connect, 500);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import { b64 } from './html';
|
||||
|
||||
export async function isGame(name: string | null | undefined) {
|
||||
if (!name || name === 'index') return false;
|
||||
|
|
@ -13,7 +14,12 @@ export async function isGame(name: string | null | undefined) {
|
|||
return stat.isDirectory();
|
||||
}
|
||||
|
||||
export async function getGames() {
|
||||
export interface Game {
|
||||
name: string;
|
||||
iconUrl?: string;
|
||||
}
|
||||
|
||||
export async function getGames(): Promise<Game[]> {
|
||||
const dir = path.resolve(import.meta.dir, '..', 'src', 'games');
|
||||
if (!await fs.exists(dir)) return [];
|
||||
|
||||
|
|
@ -21,5 +27,20 @@ export async function getGames() {
|
|||
if (!stat.isDirectory()) return [];
|
||||
|
||||
const list = await fs.readdir(dir);
|
||||
return list.filter(d => d !== 'index').sort();
|
||||
const gameNames = list.filter(d => d !== 'index').sort();
|
||||
|
||||
const games: Game[] = [];
|
||||
for (const name of gameNames) {
|
||||
const gameDir = path.resolve(dir, name);
|
||||
const pwaIconFile = Bun.file(path.resolve(gameDir, 'assets', 'pwa_icon.png'));
|
||||
const iconFile = Bun.file(path.resolve(gameDir, 'assets', 'favicon.ico'));
|
||||
let iconUrl: string | undefined;
|
||||
if (await pwaIconFile.exists()) {
|
||||
iconUrl = `data:image/png;base64,${await b64(pwaIconFile)}`;
|
||||
} else if (await iconFile.exists()) {
|
||||
iconUrl = `data:image/x-icon;base64,${await b64(iconFile)}`;
|
||||
}
|
||||
games.push({ name, iconUrl });
|
||||
}
|
||||
return games;
|
||||
}
|
||||
|
|
@ -70,11 +70,8 @@ Bun.serve<ClientData>({
|
|||
|
||||
switch (pathname) {
|
||||
case 'css-ws': {
|
||||
if (!(await isGame(gameParam))) {
|
||||
return new Response('Unknown game', { status: 400 });
|
||||
}
|
||||
const upgraded = server.upgrade(req, {
|
||||
data: { game: gameParam! } satisfies ClientData,
|
||||
data: { game } satisfies ClientData,
|
||||
});
|
||||
if (upgraded) return undefined;
|
||||
return new Response('Upgrade failed', { status: 500 });
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
isApp: true
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
.games {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
padding: 40px 20%;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 32px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card {
|
||||
composes: card from '@common/assets/ui.module.css';
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--border);
|
||||
transition: all var(--transition);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: var(--radius);
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: var(--radius);
|
||||
background: var(--bg-active);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.cardName {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.games {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
padding: 40px 20%;
|
||||
gap: 20px;
|
||||
}
|
||||
|
|
@ -1,12 +1,27 @@
|
|||
import { render } from "preact";
|
||||
import styles from './game.module.css';
|
||||
import styles from "./assets/game.module.css";
|
||||
import type { Game } from "../../../build/isGame";
|
||||
|
||||
declare const GAMES: string[];
|
||||
declare const GAMES: Game[];
|
||||
|
||||
function GameList({ games }: { games: Game[] }) {
|
||||
return (
|
||||
<div class={styles.games}>
|
||||
<h1 class={styles.title}>Games</h1>
|
||||
<div class={styles.grid}>
|
||||
{games.map((game) => (
|
||||
<a key={game.name} href={`?game=${game.name}`} class={styles.card}>
|
||||
{game.iconUrl
|
||||
? <img src={game.iconUrl} alt={game.name} class={styles.icon} />
|
||||
: <span class={styles.placeholder}>{game.name[0].toUpperCase()}</span>}
|
||||
<span class={styles.cardName}>{game.name}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function run() {
|
||||
const root = <div class={styles.games}>
|
||||
{GAMES.map(g => <a key={g} href={`?game=${g}`}>{g}</a>)}
|
||||
</div>;
|
||||
|
||||
render(root, document.body);
|
||||
render(<GameList games={GAMES} />, document.body);
|
||||
}
|
||||
Loading…
Reference in New Issue