batocera store
This commit is contained in:
@@ -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.',
|
||||
|
||||
@@ -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') },
|
||||
]
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user