batocera store

This commit is contained in:
2026-08-10 23:58:27 +02:00
parent 763a092640
commit 0a7e2d14e2
6 changed files with 232 additions and 1 deletions
+16
View File
@@ -197,6 +197,22 @@ export default {
title: 'Build Matrix',
subtitle: 'Which engines build for which platforms.',
},
batocera: {
title: 'Batocera Store',
subtitle: 'Our catalog, straight on your retro box.',
lead: 'ttg-store is a small catalog client for Batocera, the plug-and-play retro gaming distribution. It pulls our games into the box\'s ROM folders together with box art and EmulationStation metadata, and can be re-run any time from the Ports menu to pick up new releases. Python 3 standard library only — nothing to install alongside it.',
repo: 'Git repository',
docs: 'Documentation',
batoceraProject: 'Batocera project',
installTitle: 'Install',
installDesc: 'Run this over SSH on the Batocera box. It installs the client, writes a default config, creates the Ports entry and runs the first sync.',
installNote: 'Then restart EmulationStation so the games show up:',
useTitle: 'Use it',
useDesc: 'On the device: Ports ▸ "Teletype Games Store". Over SSH the client has a small CLI:',
platformsTitle: 'What it installs',
platformsDesc: 'A catalog platform is installable when its release asset boots directly on a Batocera system. Other platforms ship web and desktop builds instead — see the build matrix.',
copy: 'Copy',
},
code: {
title: 'Codebase',
subtitle: 'Explore our collection of open-source projects, study our source code, and contribute to our independent game development tools.',
+16
View File
@@ -197,6 +197,22 @@ export default {
title: 'Build mátrix',
subtitle: 'Melyik engine melyik platformra fordít.',
},
batocera: {
title: 'Batocera Store',
subtitle: 'A katalógusunk közvetlenül a retró gépeden.',
lead: 'A ttg-store egy kicsi katalógus-kliens a Batocerához, a plug-and-play retró gamer disztribúcióhoz. A játékainkat a gép ROM mappáiba tölti le, borítóképpel és EmulationStation metaadatokkal együtt, és a Ports menüből bármikor újrafuttatható az új kiadásokért. Csak a Python 3 alapkönyvtárát használja — nem kell mellé semmit telepíteni.',
repo: 'Git tároló',
docs: 'Dokumentáció',
batoceraProject: 'Batocera projekt',
installTitle: 'Telepítés',
installDesc: 'Futtasd ezt SSH-n a Batocera gépen. Telepíti a klienst, kiírja az alapértelmezett konfigurációt, létrehozza a Ports bejegyzést és lefuttatja az első szinkront.',
installNote: 'Utána indítsd újra az EmulationStationt, hogy megjelenjenek a játékok:',
useTitle: 'Használat',
useDesc: 'A gépen: Ports ▸ „Teletype Games Store”. SSH-n keresztül egy egyszerű CLI áll rendelkezésre:',
platformsTitle: 'Mit telepít',
platformsDesc: 'Egy katalógus-platform akkor telepíthető, ha a kiadás fájlja közvetlenül elindul egy Batocera rendszeren. A többi platform webes és asztali buildeket ad — lásd a build mátrixot.',
copy: 'Másolás',
},
code: {
title: 'Kódbázis',
subtitle: 'Fedezd fel nyílt forráskódú projektek gyűjteményét, tanulmányozd forráskódunkat, és járulj hozzá független játékfejlesztő eszközeinkhez.',
@@ -0,0 +1,185 @@
<template>
<header class="hero-section-gradient from-emerald-700 to-teal-800 py-16">
<div class="hero-container">
<h1 class="hero-title">{{ t('batocera.title') }}</h1>
<p class="hero-subtitle text-emerald-50">{{ t('batocera.subtitle') }}</p>
</div>
</header>
<main class="main-container py-12 px-4 mt-0">
<div class="bat-panel">
<p class="bat-lead">{{ t('batocera.lead') }}</p>
<div class="bat-links">
<a :href="REPO_URL" target="_blank" rel="noopener noreferrer" class="bat-link bat-link-dark">
<i class="fa-solid fa-code-branch"></i> {{ t('batocera.repo') }}
</a>
<a :href="WIKI_URL" target="_blank" rel="noopener noreferrer" class="bat-link bat-link-indigo">
<i class="fa-solid fa-book"></i> {{ t('batocera.docs') }}
</a>
<a :href="BATOCERA_URL" target="_blank" rel="noopener noreferrer" class="bat-link bat-link-emerald">
<i class="fa-solid fa-tv"></i> {{ t('batocera.batoceraProject') }}
</a>
</div>
</div>
<section class="bat-section">
<h2 class="bat-section-title">
<span class="bat-step">1</span> {{ t('batocera.installTitle') }}
</h2>
<p class="bat-section-desc">{{ t('batocera.installDesc') }}</p>
<div class="bat-code">
<pre><code>{{ INSTALL_CMD }}</code></pre>
<button class="bat-copy" :title="t('batocera.copy')" @click="copy(INSTALL_CMD)">
<i :class="copied === INSTALL_CMD ? 'fa-solid fa-check' : 'fa-regular fa-copy'"></i>
</button>
</div>
<p class="bat-note">{{ t('batocera.installNote') }}</p>
<div class="bat-code">
<pre><code>{{ RESTART_CMD }}</code></pre>
<button class="bat-copy" :title="t('batocera.copy')" @click="copy(RESTART_CMD)">
<i :class="copied === RESTART_CMD ? 'fa-solid fa-check' : 'fa-regular fa-copy'"></i>
</button>
</div>
</section>
<section class="bat-section">
<h2 class="bat-section-title">
<span class="bat-step">2</span> {{ t('batocera.useTitle') }}
</h2>
<p class="bat-section-desc">{{ t('batocera.useDesc') }}</p>
<div class="bat-code">
<pre><code>{{ CLI_SNIPPET }}</code></pre>
<button class="bat-copy" :title="t('batocera.copy')" @click="copy(CLI_SNIPPET)">
<i :class="copied === CLI_SNIPPET ? 'fa-solid fa-check' : 'fa-regular fa-copy'"></i>
</button>
</div>
</section>
<section class="bat-section">
<h2 class="bat-section-title">{{ t('batocera.platformsTitle') }}</h2>
<p class="bat-section-desc">{{ t('batocera.platformsDesc') }}</p>
<ul class="bat-platforms">
<li v-for="p in platforms" :key="p.platform" class="bat-platform">
<i :class="p.icon" class="bat-platform-icon"></i>
<span class="bat-platform-name">{{ p.label }}</span>
<code class="bat-platform-ext">{{ p.ext }}</code>
</li>
</ul>
<RouterLink to="/builds" class="bat-inline-link">
<i class="fa-solid fa-table-cells"></i> {{ t('builds.title') }}
</RouterLink>
</section>
<div class="bat-back">
<RouterLink to="/catalog" class="bat-back-link">{{ t('catalogShow.back') }}</RouterLink>
</div>
</main>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { RouterLink } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { CONFIG } from '../../lib/config'
const { t } = useI18n()
const REPO_URL = 'https://git.teletypegames.org/tools/batocera-store'
const WIKI_URL = `${CONFIG.wikiBase}/others/batocera-store`
const BATOCERA_URL = 'https://batocera.org'
// The installer is served straight from the forge, so this one line is the
// whole install on a Batocera box.
const INSTALL_CMD = `curl -fsSL ${REPO_URL}/raw/branch/master/install.sh | bash`
const RESTART_CMD = 'batocera-es-swissknife --restart'
const CLI_SNIPPET = [
'ttg-store list # compatible catalog entries',
'ttg-store sync # download everything new',
'ttg-store remove c64demo',
].join('\n')
const platforms = [
{ platform: 'c64', label: 'Commodore 64 (VICE)', ext: '.prg', icon: 'fa-solid fa-floppy-disk' },
{ platform: 'tic80', label: 'TIC-80', ext: '.tic', icon: 'fa-solid fa-tv' },
]
const copied = ref('')
async function copy(text: string) {
try {
await navigator.clipboard.writeText(text)
copied.value = text
setTimeout(() => { if (copied.value === text) copied.value = '' }, 2000)
} catch {
// Clipboard API needs a secure context — the command stays selectable anyway.
}
}
</script>
<style scoped>
.bat-panel {
@apply bg-white rounded-2xl shadow-xl border border-gray-100 p-6 md:p-8;
}
.bat-lead {
@apply text-gray-600 leading-relaxed mb-6;
}
.bat-links {
@apply flex flex-wrap gap-3;
}
.bat-link {
@apply inline-flex items-center gap-2 font-bold py-2.5 px-5 rounded-xl text-sm text-white transition-all active:scale-95;
}
.bat-link-dark { @apply bg-slate-800 hover:bg-slate-900 shadow-lg shadow-slate-800/20; }
.bat-link-indigo { @apply bg-indigo-600 hover:bg-indigo-700 shadow-lg shadow-indigo-600/20; }
.bat-link-emerald { @apply bg-emerald-600 hover:bg-emerald-700 shadow-lg shadow-emerald-600/20; }
.bat-section {
@apply bg-white rounded-2xl shadow-xl border border-gray-100 p-6 md:p-8 mt-6;
}
.bat-section-title {
@apply flex items-center gap-3 text-xl font-bold text-gray-900 mb-2;
}
.bat-step {
@apply inline-flex items-center justify-center w-7 h-7 rounded-full bg-emerald-100 text-emerald-700 text-sm font-bold;
}
.bat-section-desc {
@apply text-gray-600 mb-4;
}
.bat-note {
@apply text-gray-600 mt-4 mb-3 text-sm;
}
.bat-code {
@apply relative bg-gray-900 text-gray-100 rounded-xl overflow-x-auto;
}
.bat-code pre {
@apply p-4 pr-14 font-mono text-sm leading-relaxed;
}
.bat-copy {
@apply absolute top-2 right-2 w-9 h-9 rounded-lg bg-gray-800 text-gray-300 hover:bg-gray-700 hover:text-white transition-colors;
}
.bat-platforms {
@apply grid grid-cols-1 sm:grid-cols-2 gap-3 mb-5;
}
.bat-platform {
@apply flex items-center gap-3 bg-gray-50 border border-gray-100 rounded-xl px-4 py-3;
}
.bat-platform-icon {
@apply text-gray-400;
}
.bat-platform-name {
@apply font-medium text-gray-800 flex-grow;
}
.bat-platform-ext {
@apply font-mono text-xs text-purple-600 bg-purple-50 px-2 py-0.5 rounded;
}
.bat-inline-link {
@apply inline-flex items-center gap-2 text-sm text-gray-400 hover:text-purple-600 font-medium transition-colors;
}
.bat-back {
@apply mt-8 text-center;
}
.bat-back-link {
@apply text-gray-400 hover:text-gray-600 text-sm font-medium transition-colors;
}
</style>
@@ -66,6 +66,10 @@
<RouterLink to="/builds" class="builds-link">
<i class="fa-solid fa-table-cells"></i> {{ t('builds.title') }}
</RouterLink>
<span class="builds-link-sep">|</span>
<RouterLink to="/batocera" class="builds-link">
<i class="fa-solid fa-gamepad"></i> {{ t('batocera.title') }}
</RouterLink>
</div>
</main>
</template>
@@ -162,9 +166,12 @@ onMounted(() => store.fetch())
@apply text-xs text-gray-400 font-medium ml-1;
}
.builds-link-row {
@apply text-center mt-10;
@apply flex flex-wrap items-center justify-center gap-3 mt-10;
}
.builds-link {
@apply text-sm text-gray-400 hover:text-purple-600 font-medium transition-colors;
}
.builds-link-sep {
@apply text-gray-200 text-sm;
}
</style>
@@ -0,0 +1,5 @@
import type { RouteRecordRaw } from 'vue-router'
export const batoceraRouter: RouteRecordRaw[] = [
{ path: '/batocera', name: 'batoceraIndex', component: () => import('../page/batocera/BatoceraIndexPage.vue') },
]
+2
View File
@@ -8,6 +8,7 @@ import { enginesRouter } from './engines.router'
import { howtosRouter } from './howtos.router'
import { teamRouter } from './team.router'
import { buildsRouter } from './builds.router'
import { batoceraRouter } from './batocera.router'
export const router = createRouter({
history: createWebHistory(),
@@ -21,6 +22,7 @@ export const router = createRouter({
...howtosRouter,
...teamRouter,
...buildsRouter,
...batoceraRouter,
],
scrollBehavior() {
return { top: 0 }