This commit is contained in:
2026-05-10 15:55:08 +02:00
parent ccababf226
commit e8465fb270
15 changed files with 559 additions and 138 deletions
+66
View File
@@ -10,6 +10,7 @@
"dependencies": {
"pinia": "^2.2.0",
"vue": "^3.5.0",
"vue-i18n": "^10.0.8",
"vue-router": "^4.4.0"
},
"devDependencies": {
@@ -81,6 +82,50 @@
"node": ">=6.9.0"
}
},
"node_modules/@intlify/core-base": {
"version": "10.0.8",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.8.tgz",
"integrity": "sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==",
"license": "MIT",
"dependencies": {
"@intlify/message-compiler": "10.0.8",
"@intlify/shared": "10.0.8"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/message-compiler": {
"version": "10.0.8",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.8.tgz",
"integrity": "sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==",
"license": "MIT",
"dependencies": {
"@intlify/shared": "10.0.8",
"source-map-js": "^1.0.2"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/shared": {
"version": "10.0.8",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.8.tgz",
"integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -2535,6 +2580,27 @@
}
}
},
"node_modules/vue-i18n": {
"version": "10.0.8",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.8.tgz",
"integrity": "sha512-mIjy4utxMz9lMMo6G9vYePv7gUFt4ztOMhY9/4czDJxZ26xPeJ49MAGa9wBAE3XuXbYCrtVPmPxNjej7JJJkZQ==",
"deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html",
"license": "MIT",
"dependencies": {
"@intlify/core-base": "10.0.8",
"@intlify/shared": "10.0.8",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-router": {
"version": "4.6.4",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
+1
View File
@@ -10,6 +10,7 @@
"dependencies": {
"pinia": "^2.2.0",
"vue": "^3.5.0",
"vue-i18n": "^10.0.8",
"vue-router": "^4.4.0"
},
"devDependencies": {
+12
View File
@@ -0,0 +1,12 @@
import { createI18n } from 'vue-i18n'
import en from './locales/en'
import hu from './locales/hu'
const savedLocale = localStorage.getItem('locale') ?? 'en'
export const i18n = createI18n({
legacy: false,
locale: savedLocale,
fallbackLocale: 'en',
messages: { en, hu },
})
+157
View File
@@ -0,0 +1,157 @@
export default {
nav: {
home: 'Home',
catalog: 'Catalog',
blog: 'Blog',
howtos: 'How-tos',
code: 'Code',
team: 'Team',
contact: 'Contact us',
retro: 'Retro Mode',
exitRetro: 'Exit Retro Mode',
},
common: {
status: {
released: 'released',
demo: 'demo',
development: 'development',
archived: 'archived',
},
error: 'Error!',
loading: 'Loading...',
missingApiConfig: 'Missing API Configuration',
failedToConnectWiki: 'Failed to connect to Wiki',
},
home: {
welcomeTo: 'Welcome to',
subtitle: 'Teletype Games is an independent game development community built on creative freedom, equality, and an open-source mindset. Our goal is to create experimental and full-fledged games with short development cycles.',
startsOn: 'Starts on:',
upcomingEvents: 'Upcoming Events',
featuredGame: 'Featured Game',
playInBrowser: '▶ Play in Browser',
viewProjectDetails: 'View Project Details',
latestFromYoutube: 'Latest from YouTube',
visitChannel: 'Visit Channel ↗',
newRelease: 'New Release',
watchOnYoutube: 'Watch on YouTube',
note: 'Our HowTo-s and blog posts are written by AI with human supervision.',
catalog: {
title: 'Catalog',
desc: 'Here you can find our games. Discover our creations for TIC-80 and other platforms, many of which you can try directly in your browser.',
btn: 'Browse Games',
},
wiki: {
title: 'Wiki',
desc: 'We gather our public interest development knowledge here. Read through technical descriptions and development logs.',
btn: 'Knowledge Base',
},
git: {
title: 'Git Repos',
desc: 'All our projects are open source. Browse our code, study our solutions, or even participate in the development.',
btn: 'Gitea',
},
youtube: {
title: 'YouTube',
desc: 'Watch our development vlogs, gameplay videos, and tutorials on our official YouTube channel.',
btn: 'Watch on YouTube',
},
},
contact: {
title: 'Contact Us',
subtitle: "Have questions, feedback, or just want to say hi? We'd love to hear from you!",
joinDiscord: 'Join our Discord',
emailUs: 'Email Us',
emailDesc: 'For business inquiries, support, or detailed feedback, feel free to drop us an email.',
community: 'Community',
communityDesc: 'Join our primarily Hungarian-speaking community (English speakers are also welcome!)',
discord: {
desc: 'We have a vibrant Discord server for real-time chat and support.',
link: 'Join Discord Server',
},
youtube: {
desc: 'Watch our latest vlogs and development videos on our channel.',
link: 'Visit Channel',
},
irc: {
desc: "If Discord is too modern, find us on IRC!",
},
bbs: {
desc: 'Join us for some retro vibes on our BBS!',
},
},
blog: {
badge: 'Community Stories',
title: 'Developer Blog',
subtitle: 'News, updates, and behind-the-scenes stories from the Teletype Games community.',
errorTitle: 'Failed to connect to Wiki',
noPostsTitle: 'No blog posts found',
noPostsDesc: "We haven't published any blog posts yet. Check back soon!",
newPost: 'New Post',
readMore: 'Read more',
},
catalog: {
title: 'Our Games',
subtitle: 'Discover games made for TIC-80 and other platforms, with versions playable directly in your browser!',
filter: {
all: 'All',
released: 'Released',
demo: 'Demo',
development: 'Development',
archived: 'Archived',
},
author: 'Author:',
platform: 'Platform:',
play: '▶ Play',
moreInfo: ' More Info',
},
catalogShow: {
back: '← Back to Catalog',
projectInfo: 'Project Info',
author: 'Author',
platform: 'Platform',
license: 'License',
about: 'About',
latestStable: 'Latest Stable',
released: 'Released:',
playNow: '▶ Play Now',
download: '💾 Download',
source: '📄 Source',
docs: '📖 Docs',
allReleases: 'All Releases',
version: 'Version',
releaseDate: 'Release Date',
devVersions: 'Development Versions',
notFound: 'Game not found',
failedToLoad: 'Failed to load:',
loading: 'Loading...',
play: '▶ Play',
},
team: {
title: 'Our Team',
subtitle: 'Meet the brilliant minds behind Teletype Games.',
},
howtos: {
badge: 'Knowledge Base',
title: 'Tech HowTo Center',
subtitle: 'The latest documentation, technical guides, and development logs from our knowledge base.',
knowledgeBase: 'Knowledge Base',
errorTitle: 'Failed to connect to Wiki',
noPagesTitle: 'No pages found',
noPagesDesc: "It seems like there aren't any public pages available on the wiki at the moment.",
noDesc: 'No description provided.',
browseWiki: 'Browse Wiki Home',
new: 'New',
read: 'Read',
},
code: {
title: 'Codebase',
subtitle: 'Explore our collection of open-source projects, study our source code, and contribute to our independent game development tools.',
openGitea: 'Open Gitea',
noCommits: 'No recent commits found or accessible from public repositories.',
recentCommits: 'Recent Commits',
language: 'Language:',
updated: 'Updated:',
authoredBy: 'Authored by',
on: 'on',
},
}
+157
View File
@@ -0,0 +1,157 @@
export default {
nav: {
home: 'Főoldal',
catalog: 'Katalógus',
blog: 'Blog',
howtos: 'Hogyan csináld',
code: 'Kód',
team: 'Csapat',
contact: 'Kapcsolat',
retro: 'Retró mód',
exitRetro: 'Retró mód kikapcsolása',
},
common: {
status: {
released: 'kiadott',
demo: 'demó',
development: 'fejlesztés',
archived: 'archivált',
},
error: 'Hiba!',
loading: 'Betöltés...',
missingApiConfig: 'Hiányzó API konfiguráció',
failedToConnectWiki: 'Nem sikerült csatlakozni a Wikihez',
},
home: {
welcomeTo: 'Üdvözlünk a',
subtitle: 'A Teletype Games egy független játékfejlesztő közösség, amelyet a kreatív szabadság, az egyenlőség és a nyílt forráskódú szemlélet vezérel. Célunk rövid fejlesztési ciklusokkal kísérleti és teljes értékű játékokat létrehozni.',
startsOn: 'Kezdete:',
upcomingEvents: 'Közelgő események',
featuredGame: 'Kiemelt játék',
playInBrowser: '▶ Játék böngészőben',
viewProjectDetails: 'Projekt részletei',
latestFromYoutube: 'Legújabb YouTube-ról',
visitChannel: 'Csatorna megtekintése ↗',
newRelease: 'Új megjelenés',
watchOnYoutube: 'Nézd meg YouTube-on',
note: 'A HowTo-k és blogbejegyzések mesterséges intelligenciával íródtak, emberi felügyelet mellett.',
catalog: {
title: 'Katalógus',
desc: 'Itt megtalálod a játékainkat. Fedezd fel alkotásainkat TIC-80-ra és más platformokra, amelyek közül sokat közvetlenül a böngésződben is kipróbálhatsz.',
btn: 'Játékok böngészése',
},
wiki: {
title: 'Wiki',
desc: 'Itt gyűjtjük össze a közérdekű fejlesztési tudásunkat. Olvasd át a technikai leírásokat és fejlesztési naplókat.',
btn: 'Tudásbázis',
},
git: {
title: 'Git tárolók',
desc: 'Minden projektünk nyílt forráskódú. Böngészd a kódunkat, tanulmányozd a megoldásainkat, vagy akár részt is vehetsz a fejlesztésben.',
btn: 'Gitea',
},
youtube: {
title: 'YouTube',
desc: 'Nézd meg fejlesztési vlogjainkat, gameplay videóinkat és tutorialainkat a hivatalos YouTube-csatornánkon.',
btn: 'Nézd meg YouTube-on',
},
},
contact: {
title: 'Kapcsolat',
subtitle: 'Kérdéseid vannak, visszajelzésed van, vagy csak szeretnél üdvözölni? Szívesen hallunk felőled!',
joinDiscord: 'Csatlakozz a Discordunkhoz',
emailUs: 'Írj emailt',
emailDesc: 'Üzleti megkeresésekhez, támogatáshoz vagy részletes visszajelzéshez nyugodtan írj nekünk emailt.',
community: 'Közösség',
communityDesc: 'Csatlakozz elsősorban magyar nyelvű közösségünkhöz (az angolul beszélők is szívesen látottak!)',
discord: {
desc: 'Élénk Discord szerverünk van valós idejű csevegéshez és támogatáshoz.',
link: 'Csatlakozz a Discord szerverhez',
},
youtube: {
desc: 'Nézd meg legújabb vlogjainkat és fejlesztési videóinkat a csatornánkon.',
link: 'Csatorna megtekintése',
},
irc: {
desc: 'Ha a Discord túl modern, találj minket az IRC-n!',
},
bbs: {
desc: 'Csatlakozz hozzánk retró élményekért a BBS-ünkön!',
},
},
blog: {
badge: 'Közösségi történetek',
title: 'Fejlesztői Blog',
subtitle: 'Hírek, frissítések és kulisszák mögötti történetek a Teletype Games közösségből.',
errorTitle: 'Nem sikerült csatlakozni a Wikihez',
noPostsTitle: 'Nem találhatók blogbejegyzések',
noPostsDesc: 'Még nem tettünk közzé blogbejegyzéseket. Nézz vissza hamarosan!',
newPost: 'Új bejegyzés',
readMore: 'Tovább olvasom',
},
catalog: {
title: 'Játékaink',
subtitle: 'Fedezd fel a TIC-80-ra és más platformokra készített játékainkat, amelyeknek közvetlenül a böngésződben is kipróbálhatsz verzióit!',
filter: {
all: 'Összes',
released: 'Kiadott',
demo: 'Demó',
development: 'Fejlesztés alatt',
archived: 'Archivált',
},
author: 'Szerző:',
platform: 'Platform:',
play: '▶ Játék',
moreInfo: ' Részletek',
},
catalogShow: {
back: '← Vissza a katalógushoz',
projectInfo: 'Projekt infó',
author: 'Szerző',
platform: 'Platform',
license: 'Licensz',
about: 'Leírás',
latestStable: 'Legújabb stabil',
released: 'Kiadva:',
playNow: '▶ Játék most',
download: '💾 Letöltés',
source: '📄 Forráskód',
docs: '📖 Dokumentáció',
allReleases: 'Összes kiadás',
version: 'Verzió',
releaseDate: 'Kiadás dátuma',
devVersions: 'Fejlesztői verziók',
notFound: 'A játék nem található',
failedToLoad: 'Nem sikerült betölteni:',
loading: 'Betöltés...',
play: '▶ Játék',
},
team: {
title: 'Csapatunk',
subtitle: 'Ismerd meg a Teletype Games mögött álló zseniális elméket.',
},
howtos: {
badge: 'Tudásbázis',
title: 'Tech HowTo Központ',
subtitle: 'A legfrissebb dokumentáció, technikai útmutatók és fejlesztési naplók a tudásbázisunkból.',
knowledgeBase: 'Tudásbázis',
errorTitle: 'Nem sikerült csatlakozni a Wikihez',
noPagesTitle: 'Nem találhatók oldalak',
noPagesDesc: 'Úgy tűnik, jelenleg nincsenek nyilvánosan elérhető oldalak a wikin.',
noDesc: 'Nincs megadva leírás.',
browseWiki: 'Wiki főoldal böngészése',
new: 'Új',
read: 'Olvasá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.',
openGitea: 'Gitea megnyitása',
noCommits: 'Nem találhatók közelmúltbeli commitok a nyilvános tárolókban.',
recentCommits: 'Legutóbbi commitok',
language: 'Nyelv:',
updated: 'Frissítve:',
authoredBy: 'Közreműködő:',
on: '·',
},
}
+35 -16
View File
@@ -4,14 +4,20 @@
<RouterLink to="/" class="text-xl font-bold">Teletype Games</RouterLink>
<!-- Desktop Navigation -->
<nav class="hidden md:flex space-x-4">
<RouterLink to="/" class="p-2 hover:text-purple-300 transition-colors duration-200">Home</RouterLink>
<RouterLink to="/catalog" class="p-2 hover:text-purple-300 transition-colors duration-200">Catalog</RouterLink>
<RouterLink to="/blog" class="p-2 hover:text-purple-300 transition-colors duration-200">Blog</RouterLink>
<RouterLink to="/howtos" class="p-2 hover:text-purple-300 transition-colors duration-200">How-tos</RouterLink>
<RouterLink to="/code" class="p-2 hover:text-purple-300 transition-colors duration-200">Code</RouterLink>
<RouterLink to="/team" class="p-2 hover:text-purple-300 transition-colors duration-200">Team</RouterLink>
<RouterLink to="/contact" class="p-2 hover:text-purple-300 transition-colors duration-200">Contact us</RouterLink>
<nav class="hidden md:flex items-center space-x-4">
<RouterLink to="/" class="p-2 hover:text-purple-300 transition-colors duration-200">{{ t('nav.home') }}</RouterLink>
<RouterLink to="/catalog" class="p-2 hover:text-purple-300 transition-colors duration-200">{{ t('nav.catalog') }}</RouterLink>
<RouterLink to="/blog" class="p-2 hover:text-purple-300 transition-colors duration-200">{{ t('nav.blog') }}</RouterLink>
<RouterLink to="/howtos" class="p-2 hover:text-purple-300 transition-colors duration-200">{{ t('nav.howtos') }}</RouterLink>
<RouterLink to="/code" class="p-2 hover:text-purple-300 transition-colors duration-200">{{ t('nav.code') }}</RouterLink>
<RouterLink to="/team" class="p-2 hover:text-purple-300 transition-colors duration-200">{{ t('nav.team') }}</RouterLink>
<RouterLink to="/contact" class="p-2 hover:text-purple-300 transition-colors duration-200">{{ t('nav.contact') }}</RouterLink>
<div class="flex items-center gap-1 text-sm font-bold ml-2 border-l border-gray-600 pl-4">
<button @click="switchLocale('en')" :class="locale === 'en' ? 'text-white' : 'text-gray-400 hover:text-white'" class="transition-colors px-1">EN</button>
<span class="text-gray-600">|</span>
<button @click="switchLocale('hu')" :class="locale === 'hu' ? 'text-white' : 'text-gray-400 hover:text-white'" class="transition-colors px-1">HU</button>
</div>
</nav>
<!-- Hamburger Button (Mobile only) -->
@@ -23,13 +29,18 @@
<!-- Mobile Navigation -->
<nav v-show="menuOpen" class="md:hidden absolute top-full left-0 w-full bg-gray-800 flex flex-col items-center py-4 space-y-2 z-50">
<RouterLink to="/" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">Home</RouterLink>
<RouterLink to="/catalog" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">Catalog</RouterLink>
<RouterLink to="/blog" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">Blog</RouterLink>
<RouterLink to="/howtos" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">How-tos</RouterLink>
<RouterLink to="/code" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">Code</RouterLink>
<RouterLink to="/team" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">Team</RouterLink>
<RouterLink to="/contact" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">Contact us</RouterLink>
<RouterLink to="/" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">{{ t('nav.home') }}</RouterLink>
<RouterLink to="/catalog" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">{{ t('nav.catalog') }}</RouterLink>
<RouterLink to="/blog" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">{{ t('nav.blog') }}</RouterLink>
<RouterLink to="/howtos" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">{{ t('nav.howtos') }}</RouterLink>
<RouterLink to="/code" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">{{ t('nav.code') }}</RouterLink>
<RouterLink to="/team" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">{{ t('nav.team') }}</RouterLink>
<RouterLink to="/contact" class="block p-2 w-full text-center hover:bg-gray-700 transition-colors duration-200">{{ t('nav.contact') }}</RouterLink>
<div class="flex items-center gap-2 pt-2 border-t border-gray-700 w-full justify-center text-sm font-bold">
<button @click="switchLocale('en')" :class="locale === 'en' ? 'text-white' : 'text-gray-400'" class="transition-colors px-2 py-1">EN</button>
<span class="text-gray-600">|</span>
<button @click="switchLocale('hu')" :class="locale === 'hu' ? 'text-white' : 'text-gray-400'" class="transition-colors px-2 py-1">HU</button>
</div>
</nav>
</header>
@@ -39,7 +50,7 @@
<div class="retro-toggle-bar">
<button class="retro-toggle-btn" @click="toggleRetroMode">
🖥 {{ isRetroMode ? 'Exit Retro Mode' : 'Retro Mode' }}
🖥 {{ isRetroMode ? t('nav.exitRetro') : t('nav.retro') }}
</button>
</div>
</div>
@@ -49,8 +60,11 @@
import { watch } from 'vue'
import { storeToRefs } from 'pinia'
import { RouterLink, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useUiStore } from '../stores/ui.store'
const { t, locale } = useI18n()
const ui = useUiStore()
const { isRetroMode, menuOpen } = storeToRefs(ui)
const { toggleMenu, toggleRetroMode } = ui
@@ -59,6 +73,11 @@ const route = useRoute()
watch(() => route.path, () => {
ui.closeMenu()
})
function switchLocale(lang: string) {
locale.value = lang
localStorage.setItem('locale', lang)
}
</script>
<style scoped>
+2 -1
View File
@@ -1,8 +1,9 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { router } from './router/index.router'
import { i18n } from './i18n'
import App from './App.vue'
import './styles/global.css'
import './styles/retro.css'
createApp(App).use(createPinia()).use(router).mount('#app')
createApp(App).use(createPinia()).use(router).use(i18n).mount('#app')
+10 -9
View File
@@ -7,13 +7,11 @@
</div>
<div class="hero-container">
<div class="hero-badge">Community Stories</div>
<div class="hero-badge">{{ t('blog.badge') }}</div>
<h1 class="hero-title">
Developer <span class="text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-400">Blog</span>
</h1>
<p class="hero-subtitle mb-10">
News, updates, and behind-the-scenes stories from the Teletype Games community.
</p>
<p class="hero-subtitle mb-10">{{ t('blog.subtitle') }}</p>
</div>
</header>
@@ -21,15 +19,15 @@
<div v-if="error" class="error-banner" role="alert">
<span class="text-2xl"></span>
<div>
<h3 class="error-banner-title">Failed to connect to Wiki</h3>
<h3 class="error-banner-title">{{ t('blog.errorTitle') }}</h3>
<p class="error-banner-desc">{{ error }}</p>
</div>
</div>
<div v-else-if="!loading && blogPages.length === 0" class="empty-state">
<div class="empty-state-icon">📭</div>
<h2 class="empty-state-title">No blog posts found</h2>
<p class="empty-state-desc">We haven't published any blog posts yet. Check back soon!</p>
<h2 class="empty-state-title">{{ t('blog.noPostsTitle') }}</h2>
<p class="empty-state-desc">{{ t('blog.noPostsDesc') }}</p>
</div>
<div v-else class="blog-posts-list">
@@ -40,7 +38,7 @@
<div class="blog-post-timestamp">
<span v-if="isNew(page.createdAt)" class="new-post-badge">
<span class="new-post-dot"></span>
New Post
{{ t('blog.newPost') }}
</span>
<span>{{ formatDateTime(page.createdAt) }}</span>
</div>
@@ -60,7 +58,7 @@
<div class="mt-6">
<RouterLink :to="getPermalink(page.path)" class="read-more-btn">
Read more
{{ t('blog.readMore') }}
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
@@ -77,9 +75,12 @@
import { onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { RouterLink } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { formatDateTime } from '../../lib/dateFormat'
import { useBlogStore } from '../../stores/blog.store'
const { t } = useI18n()
const store = useBlogStore()
const { pages: blogPages, loading, error } = storeToRefs(store)
const { isNew, getPermalink, getCleanPreview } = store
@@ -1,8 +1,8 @@
<template>
<header class="hero-section-gradient from-purple-600 to-indigo-600 py-16">
<div class="hero-container">
<h1 class="hero-title">Our Games</h1>
<p class="hero-subtitle text-purple-50">Discover games made for TIC-80 and other platforms, with versions playable directly in your browser!</p>
<h1 class="hero-title">{{ t('catalog.title') }}</h1>
<p class="hero-subtitle text-purple-50">{{ t('catalog.subtitle') }}</p>
</div>
</header>
@@ -14,7 +14,7 @@
:class="['filter-btn', { active: activeFilter === f }]"
@click="activeFilter = f"
>
{{ f.charAt(0).toUpperCase() + f.slice(1) }}
{{ t(`catalog.filter.${f}`) }}
</button>
</div>
@@ -31,13 +31,13 @@
<RouterLink :to="`/catalog/${software.name}`" class="hover:text-purple-600 transition-colors">
<h2 class="software-title mb-0">{{ software.title }}</h2>
</RouterLink>
<span :class="`status-badge status-${software.status}`">{{ software.status }}</span>
<span :class="`status-badge status-${software.status}`">{{ t(`common.status.${software.status}`) }}</span>
</div>
<p class="software-desc max-w-2xl">{{ software.desc }}</p>
<div class="software-meta">
<span>Author: <span class="text-gray-900 font-medium">{{ software.author }}</span></span>
<span>{{ t('catalog.author') }} <span class="text-gray-900 font-medium">{{ software.author }}</span></span>
<span class="mx-2 text-gray-300"></span>
<span>Platform: <span class="text-gray-900 font-medium">{{ software.platform }}</span></span>
<span>{{ t('catalog.platform') }} <span class="text-gray-900 font-medium">{{ software.platform }}</span></span>
</div>
</div>
@@ -47,9 +47,9 @@
:href="getLatestStable(releases).htmlFolderPath"
target="_blank"
class="btn-play-sm"
> Play</a>
>{{ t('catalog.play') }}</a>
<RouterLink :to="`/catalog/${software.name}`" class="btn-info-sm">
More Info
{{ t('catalog.moreInfo') }}
</RouterLink>
</div>
</div>
@@ -64,8 +64,11 @@
import { ref, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { RouterLink } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useSoftwareStore } from '../../stores/software.store'
const { t } = useI18n()
const filters = ['all', 'released', 'demo', 'development', 'archived']
const activeFilter = ref('released')
@@ -4,14 +4,14 @@
<div class="hero-container">
<nav class="mb-8">
<RouterLink to="/catalog" class="text-purple-200 hover:text-white transition-colors flex items-center gap-2 font-medium">
Back to Catalog
{{ t('catalogShow.back') }}
</RouterLink>
</nav>
<div v-if="software" class="flex flex-col md:flex-row md:items-end justify-between gap-6">
<div class="flex-grow">
<div class="flex items-center gap-3 mb-4">
<span :class="`status-badge status-${software.status} px-3 py-1`">{{ software.status }}</span>
<span :class="`status-badge status-${software.status} px-3 py-1`">{{ t(`common.status.${software.status}`) }}</span>
<span class="text-purple-300 font-mono text-sm tracking-widest uppercase">{{ software.platform }}</span>
</div>
<h1 class="text-4xl md:text-6xl font-black text-white mb-4 leading-tight">{{ software.title }}</h1>
@@ -31,19 +31,19 @@
<img :src="software.imageUrl" :alt="software.title" class="max-h-48 w-auto object-contain border-4 border-gray-100 shadow-sm" />
</div>
<h2 class="text-xl font-bold text-gray-900 mb-6 flex items-center gap-2">
<span class="text-purple-600"></span> Project Info
<span class="text-purple-600"></span> {{ t('catalogShow.projectInfo') }}
</h2>
<dl class="space-y-4">
<div>
<dt class="text-xs font-bold text-gray-400 uppercase tracking-widest mb-1">Author</dt>
<dt class="text-xs font-bold text-gray-400 uppercase tracking-widest mb-1">{{ t('catalogShow.author') }}</dt>
<dd class="text-gray-900 font-medium">{{ software.author }}</dd>
</div>
<div>
<dt class="text-xs font-bold text-gray-400 uppercase tracking-widest mb-1">Platform</dt>
<dt class="text-xs font-bold text-gray-400 uppercase tracking-widest mb-1">{{ t('catalogShow.platform') }}</dt>
<dd class="text-gray-900 font-medium">{{ software.platform }}</dd>
</div>
<div v-if="software.license">
<dt class="text-xs font-bold text-gray-400 uppercase tracking-widest mb-1">License</dt>
<dt class="text-xs font-bold text-gray-400 uppercase tracking-widest mb-1">{{ t('catalogShow.license') }}</dt>
<dd class="text-gray-900 font-medium">{{ software.license }}</dd>
</div>
<div v-for="link in software.externalLinks" :key="link.url">
@@ -61,7 +61,7 @@
<!-- Main Content Area -->
<div class="lg:col-span-2 space-y-8">
<div v-if="software.story" class="bg-white rounded-3xl shadow-xl p-8 border border-gray-100">
<h2 class="text-xl font-bold text-gray-900 mb-4">About</h2>
<h2 class="text-xl font-bold text-gray-900 mb-4">{{ t('catalogShow.about') }}</h2>
<p class="text-gray-700 leading-relaxed whitespace-pre-line">{{ software.story }}</p>
</div>
@@ -69,24 +69,24 @@
<div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-6">
<div>
<div class="flex items-center gap-2 mb-2">
<span class="px-2 py-0.5 bg-white/20 text-white text-[10px] font-black uppercase rounded tracking-widest">Latest Stable</span>
<span class="px-2 py-0.5 bg-white/20 text-white text-[10px] font-black uppercase rounded tracking-widest">{{ t('catalogShow.latestStable') }}</span>
</div>
<h2 class="text-4xl font-black mb-1">{{ latestStable.version }}</h2>
<p class="text-indigo-100 text-sm">Released: {{ formatDateTime(latestStable.UpdatedAt) }}</p>
<p class="text-indigo-100 text-sm">{{ t('catalogShow.released') }} {{ formatDateTime(latestStable.UpdatedAt) }}</p>
</div>
<div class="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-2 xl:grid-cols-4 gap-2 w-full md:w-auto">
<a v-if="latestStable.htmlFolderPath" :href="latestStable.htmlFolderPath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-white text-indigo-900 font-bold rounded-xl transition-all hover:bg-indigo-50 shadow-lg shadow-black/10 text-sm whitespace-nowrap"> Play Now</a>
<a v-if="latestStable.cartridgePath" :href="latestStable.cartridgePath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">💾 Download</a>
<a v-if="latestStable.sourcePath" :href="latestStable.sourcePath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">📄 Source</a>
<a v-if="latestStable.docsFolderPath" :href="latestStable.docsFolderPath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">📖 Docs</a>
<a v-if="latestStable.htmlFolderPath" :href="latestStable.htmlFolderPath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-white text-indigo-900 font-bold rounded-xl transition-all hover:bg-indigo-50 shadow-lg shadow-black/10 text-sm whitespace-nowrap">{{ t('catalogShow.playNow') }}</a>
<a v-if="latestStable.cartridgePath" :href="latestStable.cartridgePath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">{{ t('catalogShow.download') }}</a>
<a v-if="latestStable.sourcePath" :href="latestStable.sourcePath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">{{ t('catalogShow.source') }}</a>
<a v-if="latestStable.docsFolderPath" :href="latestStable.docsFolderPath" target="_blank" class="flex items-center justify-center py-3 px-4 bg-indigo-500/30 border border-indigo-400/30 text-white font-bold rounded-xl transition-all hover:bg-indigo-500/40 text-sm whitespace-nowrap">{{ t('catalogShow.docs') }}</a>
</div>
</div>
</div>
<div class="bg-white rounded-3xl shadow-xl overflow-hidden border border-gray-100">
<div class="p-8 border-b border-gray-100">
<h2 class="text-2xl font-bold text-gray-900">All Releases</h2>
<h2 class="text-2xl font-bold text-gray-900">{{ t('catalogShow.allReleases') }}</h2>
</div>
<div class="p-0">
<!-- Desktop Table -->
@@ -94,8 +94,8 @@
<table class="w-full text-left">
<thead class="bg-gray-50 text-gray-500 text-xs font-bold uppercase tracking-widest">
<tr>
<th class="px-8 py-4">Version</th>
<th class="px-8 py-4">Release Date</th>
<th class="px-8 py-4">{{ t('catalogShow.version') }}</th>
<th class="px-8 py-4">{{ t('catalogShow.releaseDate') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@@ -103,25 +103,25 @@
<td class="px-8 py-4">
<div class="font-bold text-gray-900 text-lg mb-2">{{ release.version }}</div>
<div class="flex flex-wrap gap-4">
<a v-if="release.htmlFolderPath" :href="release.htmlFolderPath" target="_blank" class="text-purple-600 hover:text-purple-900 font-bold text-sm flex items-center gap-1"> Play</a>
<a v-if="release.cartridgePath" :href="release.cartridgePath" target="_blank" class="text-blue-600 hover:text-blue-900 font-bold text-sm flex items-center gap-1">💾 Download</a>
<a v-if="release.sourcePath" :href="release.sourcePath" target="_blank" class="text-green-600 hover:text-green-900 font-bold text-sm flex items-center gap-1">📄 Source</a>
<a v-if="release.docsFolderPath" :href="release.docsFolderPath" target="_blank" class="text-yellow-600 hover:text-yellow-900 font-bold text-sm flex items-center gap-1">📖 Docs</a>
<a v-if="release.htmlFolderPath" :href="release.htmlFolderPath" target="_blank" class="text-purple-600 hover:text-purple-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.play') }}</a>
<a v-if="release.cartridgePath" :href="release.cartridgePath" target="_blank" class="text-blue-600 hover:text-blue-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.download') }}</a>
<a v-if="release.sourcePath" :href="release.sourcePath" target="_blank" class="text-green-600 hover:text-green-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.source') }}</a>
<a v-if="release.docsFolderPath" :href="release.docsFolderPath" target="_blank" class="text-yellow-600 hover:text-yellow-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.docs') }}</a>
</div>
</td>
<td class="px-8 py-4 text-gray-500 align-top pt-5">{{ formatDateTime(release.UpdatedAt) }}</td>
</tr>
<tr v-if="devReleases.length > 0" class="bg-yellow-50/30">
<td colspan="2" class="px-8 py-3 text-[10px] font-black text-yellow-700 uppercase tracking-tighter">Development Versions</td>
<td colspan="2" class="px-8 py-3 text-[10px] font-black text-yellow-700 uppercase tracking-tighter">{{ t('catalogShow.devVersions') }}</td>
</tr>
<tr v-for="release in devReleases" :key="release.version" class="bg-yellow-50/10 hover:bg-yellow-50/30 transition-colors">
<td class="px-8 py-4">
<div class="font-bold text-yellow-800 text-lg mb-2">{{ release.version }}</div>
<div class="flex flex-wrap gap-4">
<a v-if="release.htmlFolderPath" :href="release.htmlFolderPath" target="_blank" class="text-purple-600 hover:text-purple-900 font-bold text-sm flex items-center gap-1"> Play</a>
<a v-if="release.cartridgePath" :href="release.cartridgePath" target="_blank" class="text-blue-600 hover:text-blue-900 font-bold text-sm flex items-center gap-1">💾 Download</a>
<a v-if="release.htmlFolderPath" :href="release.htmlFolderPath" target="_blank" class="text-purple-600 hover:text-purple-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.play') }}</a>
<a v-if="release.cartridgePath" :href="release.cartridgePath" target="_blank" class="text-blue-600 hover:text-blue-900 font-bold text-sm flex items-center gap-1">{{ t('catalogShow.download') }}</a>
</div>
</td>
<td class="px-8 py-4 text-gray-500 align-top pt-5">{{ formatDateTime(release.UpdatedAt) }}</td>
@@ -145,10 +145,10 @@
<span v-if="release.version.startsWith('dev-')" class="px-2 py-0.5 bg-yellow-100 text-yellow-800 text-[10px] font-black uppercase rounded">Dev</span>
</div>
<div class="grid grid-cols-2 gap-2">
<a v-if="release.htmlFolderPath" :href="release.htmlFolderPath" target="_blank" class="flex items-center justify-center py-2 bg-purple-100 text-purple-700 font-bold rounded-lg text-sm">Play</a>
<a v-if="release.cartridgePath" :href="release.cartridgePath" target="_blank" class="flex items-center justify-center py-2 bg-blue-100 text-blue-700 font-bold rounded-lg text-sm">Download</a>
<a v-if="release.sourcePath" :href="release.sourcePath" target="_blank" class="flex items-center justify-center py-2 bg-green-100 text-green-700 font-bold rounded-lg text-sm">Source</a>
<a v-if="release.docsFolderPath" :href="release.docsFolderPath" target="_blank" class="flex items-center justify-center py-2 bg-yellow-100 text-yellow-700 font-bold rounded-lg text-sm">Docs</a>
<a v-if="release.htmlFolderPath" :href="release.htmlFolderPath" target="_blank" class="flex items-center justify-center py-2 bg-purple-100 text-purple-700 font-bold rounded-lg text-sm">{{ t('catalogShow.play') }}</a>
<a v-if="release.cartridgePath" :href="release.cartridgePath" target="_blank" class="flex items-center justify-center py-2 bg-blue-100 text-blue-700 font-bold rounded-lg text-sm">{{ t('catalogShow.download') }}</a>
<a v-if="release.sourcePath" :href="release.sourcePath" target="_blank" class="flex items-center justify-center py-2 bg-green-100 text-green-700 font-bold rounded-lg text-sm">{{ t('catalogShow.source') }}</a>
<a v-if="release.docsFolderPath" :href="release.docsFolderPath" target="_blank" class="flex items-center justify-center py-2 bg-yellow-100 text-yellow-700 font-bold rounded-lg text-sm">{{ t('catalogShow.docs') }}</a>
</div>
</div>
</div>
@@ -160,7 +160,7 @@
<main v-else class="main-container -mt-10">
<div class="bg-white rounded-3xl shadow-xl p-12 text-center text-gray-400">
{{ error || 'Loading...' }}
{{ error || t('catalogShow.loading') }}
</div>
</main>
</div>
@@ -169,10 +169,12 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { formatDateTime } from '../../lib/dateFormat'
import { useSoftwareStore } from '../../stores/software.store'
import type { Release, Software } from '../../lib/interfaces/software.interface'
const { t } = useI18n()
const route = useRoute()
const store = useSoftwareStore()
@@ -203,10 +205,10 @@ onMounted(async () => {
software.value = item.software
releases.value = item.releases
} else {
error.value = 'Game not found'
error.value = t('catalogShow.notFound')
}
} catch (e: any) {
error.value = `Failed to load: ${e.message}`
error.value = `${t('catalogShow.failedToLoad')} ${e.message}`
}
})
</script>
+12 -11
View File
@@ -1,19 +1,17 @@
<template>
<header class="hero-section-gradient from-teal-600 to-green-600 py-20">
<div class="hero-container max-w-4xl">
<h1 class="hero-title">Codebase</h1>
<p class="hero-subtitle text-teal-50 mb-10">
Explore our collection of open-source projects, study our source code, and contribute to our independent game development tools.
</p>
<h1 class="hero-title">{{ t('code.title') }}</h1>
<p class="hero-subtitle text-teal-50 mb-10">{{ t('code.subtitle') }}</p>
<a href="https://git.teletype.hu" target="_blank" class="btn-hero-teal">
Open Gitea
{{ t('code.openGitea') }}
</a>
</div>
</header>
<main class="main-container">
<div v-if="error" class="error-alert" role="alert">
<strong class="font-bold">Error!</strong>
<strong class="font-bold">{{ t('common.error') }}</strong>
<span class="block sm:inline"> {{ error }}</span>
</div>
@@ -25,15 +23,15 @@
</a>
</h3>
<p v-if="repo.description" class="repo-card-desc">{{ repo.description }}</p>
<p v-if="repo.language" class="repo-card-meta">Language: {{ repo.language }}</p>
<p class="repo-card-meta">Updated: {{ formatDateTime(repo.updated_at) }}</p>
<p v-if="repo.language" class="repo-card-meta">{{ t('code.language') }} {{ repo.language }}</p>
<p class="repo-card-meta">{{ t('code.updated') }} {{ formatDateTime(repo.updated_at) }}</p>
</div>
</div>
<h2 class="commits-section-title">Recent Commits</h2>
<h2 class="commits-section-title">{{ t('code.recentCommits') }}</h2>
<p v-if="!error && recentCommits.length === 0" class="empty-commits-msg">
No recent commits found or accessible from public repositories.
{{ t('code.noCommits') }}
</p>
<div class="commits-list">
@@ -47,7 +45,7 @@
: <span class="text-gray-700">{{ commit.message.split('\n')[0] }}</span>
</p>
<p class="commit-meta">
Authored by <span class="font-bold">{{ commit.author.name }}</span> on {{ formatDateTime(commit.date) }}
{{ t('code.authoredBy') }} <span class="font-bold">{{ commit.author.name }}</span> {{ t('code.on') }} {{ formatDateTime(commit.date) }}
<a :href="commit.url" target="_blank" class="commit-sha-link">
<span class="font-mono text-xs">{{ commit.sha.substring(0, 7) }}</span>
</a>
@@ -61,9 +59,12 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useI18n } from 'vue-i18n'
import { formatDateTime } from '../../lib/dateFormat'
import { useGitStore } from '../../stores/git.store'
const { t } = useI18n()
const store = useGitStore()
const { repos: publicRepos, commits: recentCommits, error } = storeToRefs(store)
+17 -19
View File
@@ -1,15 +1,13 @@
<template>
<header class="hero-section-gradient from-blue-600 to-indigo-700 py-20 text-white">
<div class="hero-container text-center">
<h1 class="hero-title mb-6">Contact Us</h1>
<p class="hero-subtitle mb-10 text-blue-100">
Have questions, feedback, or just want to say hi? We'd love to hear from you!
</p>
<h1 class="hero-title mb-6">{{ t('contact.title') }}</h1>
<p class="hero-subtitle mb-10 text-blue-100">{{ t('contact.subtitle') }}</p>
<a :href="DISCORD_INVITE_LINK" target="_blank" class="discord-hero-btn">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037 19.736 19.736 0 0 0-4.885 1.515.069.069 0 0 0-.032.027C.533 9.048-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.862-1.297 1.197-1.99.051-.105.001-.23-.106-.272a13.223 13.223 0 0 1-1.878-.894.077.077 0 0 1-.007-.128c.126-.094.252-.192.372-.29a.074.074 0 0 1 .077-.01c3.927 1.793 8.18 1.793 12.061 0a.074.074 0 0 1 .077.01c.12.098.246.196.373.29a.077.077 0 0 1-.006.128 12.299 12.299 0 0 1-1.878.894.077.077 0 0 0-.106.272c.335.693.735 1.36 1.197 1.99a.078.078 0 0 0 .084.028 19.83 19.83 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.182 0-2.156-1.085-2.156-2.419 0-1.333.956-2.419 2.156-2.419 1.21 0 2.176 1.096 2.156 2.419 0 1.334-.956 2.419-2.156 2.419zm7.974 0c-1.182 0-2.156-1.085-2.156-2.419 0-1.333.955-2.419 2.156-2.419 1.21 0 2.176 1.096 2.156 2.419 0 1.334-.946 2.419-2.156 2.419z"/>
</svg>
Join our Discord
{{ t('contact.joinDiscord') }}
</a>
</div>
</header>
@@ -18,11 +16,9 @@
<div class="contact-grid">
<div class="contact-card">
<h2 class="contact-card-title">
<span class="text-3xl">📧</span> Email Us
<span class="text-3xl">📧</span> {{ t('contact.emailUs') }}
</h2>
<p class="contact-card-desc">
For business inquiries, support, or detailed feedback, feel free to drop us an email.
</p>
<p class="contact-card-desc">{{ t('contact.emailDesc') }}</p>
<div @click="openEmail" class="email-link cursor-pointer">
contact@teletype.hu
</div>
@@ -30,32 +26,30 @@
<div class="contact-card">
<h2 class="contact-card-title">
<span class="text-3xl">💬</span> Community
<span class="text-3xl">💬</span> {{ t('contact.community') }}
</h2>
<p class="contact-card-desc mb-8">
Join our primarily Hungarian-speaking community (English speakers are also welcome!).
</p>
<p class="contact-card-desc mb-8">{{ t('contact.communityDesc') }}</p>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 justify-center">
<div class="community-section border-[#5865F2]">
<h3 class="community-section-title text-[#5865F2]">Discord</h3>
<p class="community-section-desc">We have a vibrant Discord server for real-time chat and support.</p>
<p class="community-section-desc">{{ t('contact.discord.desc') }}</p>
<a :href="DISCORD_INVITE_LINK" target="_blank" class="community-link-discord">
Join Discord Server <span>→</span>
{{ t('contact.discord.link') }} <span></span>
</a>
</div>
<div class="community-section border-red-600">
<h3 class="community-section-title text-red-600">YouTube</h3>
<p class="community-section-desc">Watch our latest vlogs and development videos on our channel.</p>
<p class="community-section-desc">{{ t('contact.youtube.desc') }}</p>
<a href="https://www.youtube.com/@teletypegames" target="_blank" class="community-link-youtube">
Visit Channel <span>→</span>
{{ t('contact.youtube.link') }} <span></span>
</a>
</div>
<div class="community-section border-green-500">
<h3 class="community-section-title text-green-700">IRC</h3>
<p class="text-sm text-gray-600 mb-3">If Discord is too modern, find us on IRC!</p>
<p class="text-sm text-gray-600 mb-3">{{ t('contact.irc.desc') }}</p>
<div class="irc-box">
<span class="irc-network">Network: Libera.Chat</span>
<code class="irc-channel">#teletypegames</code>
@@ -64,7 +58,7 @@
<div class="community-section border-amber-500">
<h3 class="community-section-title text-amber-700">BBS</h3>
<p class="community-section-desc">Join us for some retro vibes on our BBS!</p>
<p class="community-section-desc">{{ t('contact.bbs.desc') }}</p>
<div class="bbs-box">
<span class="bbs-host">Host: games.teletype.hu</span>
<code class="bbs-port">Port: 2323</code>
@@ -77,6 +71,10 @@
</template>
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const DISCORD_INVITE_LINK = import.meta.env.DISCORD_INVITE_LINK || 'https://discord.gg/your-discord-link'
function openEmail() {
+29 -28
View File
@@ -8,13 +8,11 @@
</div>
<div class="hero-container">
<div class="hero-badge">Welcome to</div>
<div class="hero-badge">{{ t('home.welcomeTo') }}</div>
<h1 class="hero-title">
Teletype <span class="text-transparent bg-clip-text bg-gradient-to-r from-violet-400 to-fuchsia-400">Games</span>
</h1>
<p class="hero-subtitle">
Teletype Games is an independent game development community built on creative freedom, equality, and an open-source mindset. Our goal is to create experimental and full-fledged games with short development cycles.
</p>
<p class="hero-subtitle">{{ t('home.subtitle') }}</p>
</div>
</header>
@@ -24,10 +22,10 @@
<div class="countdown-card">
<h2 class="countdown-event-name">{{ nextEvent.name }}</h2>
<div class="countdown-timer">{{ countdownText }}</div>
<p class="countdown-date">Starts on: <span class="font-semibold">{{ nextEvent.dateText }}</span></p>
<p class="countdown-date">{{ t('home.startsOn') }} <span class="font-semibold">{{ nextEvent.dateText }}</span></p>
<div v-if="followingEvents.length > 0" class="upcoming-events-section">
<h3 class="upcoming-events-title">Upcoming Events</h3>
<h3 class="upcoming-events-title">{{ t('home.upcomingEvents') }}</h3>
<div class="space-y-4">
<div v-for="event in followingEvents" :key="event.name" class="upcoming-event-item">
<span class="font-bold text-gray-800">{{ event.name }}</span>
@@ -46,8 +44,8 @@
</div>
<div class="featured-content">
<div class="flex items-center gap-2 mb-4">
<span class="featured-badge">Featured Game</span>
<span :class="`status-badge status-${highlightedSoftware.software.status}`">{{ highlightedSoftware.software.status }}</span>
<span class="featured-badge">{{ t('home.featuredGame') }}</span>
<span :class="`status-badge status-${highlightedSoftware.software.status}`">{{ t(`common.status.${highlightedSoftware.software.status}`) }}</span>
<span v-if="highlightedStableRelease" class="featured-version-badge">{{ highlightedStableRelease.version }}</span>
</div>
<h2 class="featured-title">{{ highlightedSoftware.software.title }}</h2>
@@ -59,9 +57,9 @@
:href="highlightedStableRelease.htmlFolderPath"
target="_blank"
class="featured-btn-play"
> Play in Browser</a>
>{{ t('home.playInBrowser') }}</a>
<RouterLink :to="`/catalog/${highlightedSoftware.software.name}`" class="featured-btn-secondary">
View Project Details
{{ t('home.viewProjectDetails') }}
</RouterLink>
</div>
</div>
@@ -78,10 +76,10 @@
<div class="yt-status-blob">
<div class="yt-status-dot"></div>
</div>
<span class="yt-label-text">Latest from YouTube</span>
<span class="yt-label-text">{{ t('home.latestFromYoutube') }}</span>
</div>
<a href="https://www.youtube.com/@teletypegames" target="_blank" class="yt-channel-link">
Visit Channel
{{ t('home.visitChannel') }}
</a>
</div>
@@ -96,7 +94,7 @@
<div class="text-[11px] text-slate-500 text-center px-4 font-bold uppercase tracking-tighter">{{ ytError }}</div>
</div>
<div v-else-if="ytState === 'no-config'" class="state-overlay absolute inset-0 flex items-center justify-center bg-slate-950">
<div class="text-slate-500 text-xs">Missing API Configuration</div>
<div class="text-slate-500 text-xs">{{ t('common.missingApiConfig') }}</div>
</div>
<iframe
v-else-if="ytVideoId"
@@ -110,7 +108,7 @@
<div v-if="ytVideoId" class="yt-info-sidebar">
<div class="mb-auto">
<span class="yt-new-tag">New Release</span>
<span class="yt-new-tag">{{ t('home.newRelease') }}</span>
<h3 class="yt-video-title">{{ ytTitle }}</h3>
<div class="yt-stats-row">
<span v-if="ytPublishDate">📅 {{ ytPublishDate }}</span>
@@ -120,7 +118,7 @@
<div class="mt-6">
<a :href="`https://www.youtube.com/watch?v=${ytVideoId}`" target="_blank" class="yt-play-button">
<span class="text-xl"></span> Watch on YouTube
<span class="text-xl"></span> {{ t('home.watchOnYoutube') }}
</a>
</div>
</div>
@@ -134,10 +132,10 @@
<div class="nav-card-icon bg-purple-50 group-hover:bg-purple-100">
<span class="text-3xl">🎮</span>
</div>
<h2 class="nav-card-title">Catalog</h2>
<p class="nav-card-desc">Here you can find our games. Discover our creations for TIC-80 and other platforms, many of which you can try directly in your browser.</p>
<h2 class="nav-card-title">{{ t('home.catalog.title') }}</h2>
<p class="nav-card-desc">{{ t('home.catalog.desc') }}</p>
<RouterLink to="/catalog" class="btn-primary bg-purple-600 hover:bg-purple-700 text-white hover:shadow-purple-200">
Browse Games
{{ t('home.catalog.btn') }}
</RouterLink>
</div>
@@ -145,10 +143,10 @@
<div class="nav-card-icon bg-green-50 group-hover:bg-green-100">
<span class="text-3xl">📚</span>
</div>
<h2 class="nav-card-title">Wiki</h2>
<p class="nav-card-desc">We gather our public interest development knowledge here. Read through technical descriptions and development logs.</p>
<h2 class="nav-card-title">{{ t('home.wiki.title') }}</h2>
<p class="nav-card-desc">{{ t('home.wiki.desc') }}</p>
<a href="https://wiki.teletype.hu" target="_blank" rel="noopener noreferrer" class="btn-primary bg-green-600 hover:bg-green-700 text-white hover:shadow-green-200">
Knowledge Base
{{ t('home.wiki.btn') }}
</a>
</div>
@@ -156,10 +154,10 @@
<div class="nav-card-icon bg-slate-50 group-hover:bg-slate-100">
<span class="text-3xl">💻</span>
</div>
<h2 class="nav-card-title">Git Repos</h2>
<p class="nav-card-desc">All our projects are open source. Browse our code, study our solutions, or even participate in the development.</p>
<h2 class="nav-card-title">{{ t('home.git.title') }}</h2>
<p class="nav-card-desc">{{ t('home.git.desc') }}</p>
<a href="https://git.teletype.hu" target="_blank" rel="noopener noreferrer" class="btn-primary bg-slate-800 hover:bg-slate-900 text-white hover:shadow-slate-200">
Gitea
{{ t('home.git.btn') }}
</a>
</div>
@@ -167,10 +165,10 @@
<div class="nav-card-icon bg-red-50 group-hover:bg-red-100">
<span class="text-3xl">📺</span>
</div>
<h2 class="nav-card-title">YouTube</h2>
<p class="nav-card-desc">Watch our development vlogs, gameplay videos, and tutorials on our official YouTube channel.</p>
<h2 class="nav-card-title">{{ t('home.youtube.title') }}</h2>
<p class="nav-card-desc">{{ t('home.youtube.desc') }}</p>
<a href="https://www.youtube.com/@teletypegames" target="_blank" rel="noopener noreferrer" class="btn-primary bg-red-600 hover:bg-red-700 text-white hover:shadow-red-200">
Watch on YouTube
{{ t('home.youtube.btn') }}
</a>
</div>
</section>
@@ -179,7 +177,7 @@
<div class="mb-12 mt-12 bg-white rounded-2xl border border-indigo-100 p-6 shadow-sm flex items-center gap-4">
<div class="w-12 h-12 rounded-full bg-indigo-50 flex items-center justify-center text-2xl flex-shrink-0">🤖</div>
<div class="flex-grow text-slate-600 text-sm md:text-base italic">
<strong>Note:</strong> Our HowTo-s and blog posts are written by AI with human supervision.
<strong>Note:</strong> {{ t('home.note') }}
</div>
</div>
</main>
@@ -190,10 +188,13 @@
import { onMounted, onUnmounted } from 'vue'
import { storeToRefs } from 'pinia'
import { RouterLink } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useEventStore } from '../../stores/event.store'
import { useSoftwareStore } from '../../stores/software.store'
import { useYoutubeStore } from '../../stores/youtube.store'
const { t } = useI18n()
const eventStore = useEventStore()
const { nextEvent, followingEvents, countdownText } = storeToRefs(eventStore)
@@ -7,15 +7,13 @@
</div>
<div class="hero-container">
<div class="hero-badge">Knowledge Base</div>
<div class="hero-badge">{{ t('howtos.badge') }}</div>
<h1 class="hero-title">
Tech <span class="text-transparent bg-clip-text bg-gradient-to-r from-indigo-400 to-purple-400">HowTo Center</span>
</h1>
<p class="hero-subtitle mb-10">
The latest documentation, technical guides, and development logs from our knowledge base.
</p>
<p class="hero-subtitle mb-10">{{ t('howtos.subtitle') }}</p>
<a :href="WIKI_BASE" target="_blank" class="btn-hero-indigo">
Knowledge Base
{{ t('howtos.knowledgeBase') }}
</a>
</div>
</header>
@@ -24,15 +22,15 @@
<div v-if="error" class="error-banner" role="alert">
<span class="text-2xl"></span>
<div>
<h3 class="error-banner-title">Failed to connect to Wiki</h3>
<h3 class="error-banner-title">{{ t('howtos.errorTitle') }}</h3>
<p class="error-banner-desc">{{ error }}</p>
</div>
</div>
<div v-else-if="!loading && recentPages.length === 0" class="empty-state">
<div class="empty-state-icon">📭</div>
<h2 class="empty-state-title">No pages found</h2>
<p class="empty-state-desc">It seems like there aren't any public pages available on the wiki at the moment.</p>
<h2 class="empty-state-title">{{ t('howtos.noPagesTitle') }}</h2>
<p class="empty-state-desc">{{ t('howtos.noPagesDesc') }}</p>
</div>
<div v-else class="card-grid">
@@ -47,7 +45,7 @@
<div class="howto-card-timestamp">
<span v-if="isNew(page.createdAt)" class="new-badge">
<span class="new-dot"></span>
New
{{ t('howtos.new') }}
</span>
<span>{{ formatDateTime(page.updatedAt) }}</span>
</div>
@@ -56,18 +54,18 @@
<h2 class="howto-card-title">{{ page.title }}</h2>
<p v-if="page.description" class="howto-card-desc">{{ page.description }}</p>
<div v-else class="howto-card-no-desc">No description provided.</div>
<div v-else class="howto-card-no-desc">{{ t('howtos.noDesc') }}</div>
<div class="howto-card-footer">
<span class="howto-card-path">{{ page.path }}</span>
<span class="howto-card-read-link">Read <span class="text-lg">→</span></span>
<span class="howto-card-read-link">{{ t('howtos.read') }} <span class="text-lg"></span></span>
</div>
</a>
</div>
<div v-if="!error && recentPages.length > 0" class="howtos-footer">
<a :href="WIKI_BASE" target="_blank" class="browse-wiki-btn">
Browse Wiki Home
{{ t('howtos.browseWiki') }}
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
@@ -80,10 +78,13 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useI18n } from 'vue-i18n'
import { formatDateTime } from '../../lib/dateFormat'
import { WIKI_BASE } from '../../api/wiki.api'
import { useHowtosStore } from '../../stores/howtos.store'
const { t } = useI18n()
const store = useHowtosStore()
const { pages: recentPages, loading, error } = storeToRefs(store)
const { isNew } = store
@@ -1,10 +1,8 @@
<template>
<header class="hero-section-gradient from-purple-600 to-blue-700 py-20 text-white">
<div class="hero-container text-center">
<h1 class="hero-title mb-6">Our Team</h1>
<p class="hero-subtitle mb-10 text-purple-100">
Meet the brilliant minds behind Teletype Games.
</p>
<h1 class="hero-title mb-6">{{ t('team.title') }}</h1>
<p class="hero-subtitle mb-10 text-purple-100">{{ t('team.subtitle') }}</p>
</div>
</header>
@@ -26,8 +24,11 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useI18n } from 'vue-i18n'
import { useMemberStore } from '../../stores/member.store'
const { t } = useI18n()
const store = useMemberStore()
const { members } = storeToRefs(store)
const { resolvedAvatarUrl } = store