feat(hytale): add HytaleDemoGrid component and demo data

- Introduced HytaleDemoGrid.vue to showcase live Hytale plugins with a responsive layout.
- Created hytaleDemos.ts to manage demo data, including details for VotePipe and GravityFlip plugins.
- Updated Hytale page to include the new demo grid section.
- Enhanced AppFooter and ServicesSection with i18n support for better localization.
- Added new blog post detailing the development process of the GravityFlip plugin, available in both English and French.

This commit enhances the visibility of Hytale plugins and improves the overall user experience on the site.
This commit is contained in:
2026-04-25 15:39:53 +02:00
parent 0d238c5b0d
commit d5dbbb6dff
13 changed files with 646 additions and 40 deletions
+133
View File
@@ -0,0 +1,133 @@
<script setup lang="ts">
import { hytaleDemos } from '~/data/hytaleDemos'
const { t } = useI18n()
</script>
<template>
<section
id="demos"
class="py-16 md:py-24 px-4 sm:px-6 lg:px-8"
>
<div class="max-w-7xl mx-auto">
<div class="text-center mb-12">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">
{{ t('hytale.demos.label') }}
</span>
<h2
class="text-3xl sm:text-4xl lg:text-5xl font-bold mt-3 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent"
>
{{ t('hytale.demos.title') }}
</h2>
<p class="text-base sm:text-lg text-gray-500 dark:text-gray-400 mt-4 max-w-2xl mx-auto leading-relaxed">
{{ t('hytale.demos.subtitle') }}
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<UCard
v-for="demo in hytaleDemos"
:key="demo.id"
class="hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1 transition-all duration-200 overflow-hidden"
:class="{ 'ring-2 ring-brand-500': demo.featured }"
>
<template #header>
<div class="aspect-video w-full bg-gray-100 dark:bg-gray-900 -m-4 mb-2 overflow-hidden">
<NuxtImg
:src="demo.image"
:alt="t(`hytale.demos.${demo.id}.title`)"
width="600"
height="338"
loading="lazy"
class="w-full h-full object-cover"
/>
</div>
</template>
<div class="flex flex-col gap-3 p-2">
<div class="flex items-center justify-between gap-2">
<h3 class="text-lg font-bold text-gray-900 dark:text-white">
{{ t(`hytale.demos.${demo.id}.title`) }}
</h3>
<UBadge
v-if="demo.featured"
color="primary"
variant="subtle"
size="sm"
>
{{ t('hytale.demos.featured') }}
</UBadge>
</div>
<p class="text-sm text-gray-600 dark:text-gray-300 leading-relaxed line-clamp-3">
{{ t(`hytale.demos.${demo.id}.tagline`) }}
</p>
<div class="flex flex-wrap gap-1.5">
<UBadge
v-for="t in demo.tech"
:key="t"
color="neutral"
variant="soft"
size="xs"
>
{{ t }}
</UBadge>
</div>
<div class="flex flex-wrap gap-2 mt-2">
<UButton
v-if="demo.website"
:to="demo.website"
target="_blank"
rel="noopener noreferrer"
color="primary"
variant="solid"
size="sm"
trailing-icon="i-lucide-external-link"
>
{{ t('hytale.demos.viewSite') }}
</UButton>
<UButton
v-if="demo.modtale"
:to="demo.modtale"
target="_blank"
rel="noopener noreferrer"
color="neutral"
variant="outline"
size="sm"
>
Modtale
</UButton>
<UButton
v-if="demo.curseforge"
:to="demo.curseforge"
target="_blank"
rel="noopener noreferrer"
color="neutral"
variant="outline"
size="sm"
>
CurseForge
</UButton>
<UButton
v-if="demo.github"
:to="demo.github"
target="_blank"
rel="noopener noreferrer"
color="neutral"
variant="outline"
size="sm"
icon="i-simple-icons-github"
/>
</div>
</div>
</UCard>
</div>
<p class="text-center text-sm text-gray-500 dark:text-gray-400 mt-10 italic">
{{ t('hytale.demos.footnote') }}
</p>
</div>
</section>
</template>
+6 -6
View File
@@ -28,7 +28,7 @@ src="/images/logo.webp" alt="Killian' DAL-CIN" width="36" height="36" loading="l
<span class="text-lg font-bold text-gray-900 dark:text-white">Killian' DAL-CIN</span>
</NuxtLink>
<p class="text-sm text-gray-500 dark:text-gray-400 leading-relaxed max-w-xs">
Full Stack Developer &amp; Hytale Plugin Developer. Building modern web experiences and game plugins.
{{ t('footer.tagline') }}
</p>
</div>
@@ -49,13 +49,13 @@ v-for="link in quickLinks" :key="link.key" :to="localePath(link.path)"
<!-- Services links -->
<div>
<h3 class="font-mono text-xs text-gray-400 dark:text-gray-500 uppercase tracking-widest mb-5">
Services
{{ t('footer.services') }}
</h3>
<nav class="flex flex-col gap-3">
<span class="text-sm text-gray-600 dark:text-gray-400">Web Development</span>
<span class="text-sm text-gray-600 dark:text-gray-400">Hytale Plugins</span>
<span class="text-sm text-gray-600 dark:text-gray-400">Consulting</span>
<span class="text-sm text-gray-600 dark:text-gray-400">Maintenance</span>
<span class="text-sm text-gray-600 dark:text-gray-400">{{ t('footer.servicesList.hytalePlugins') }}</span>
<span class="text-sm text-gray-600 dark:text-gray-400">{{ t('footer.servicesList.webDev') }}</span>
<span class="text-sm text-gray-600 dark:text-gray-400">{{ t('footer.servicesList.retainer') }}</span>
<span class="text-sm text-gray-600 dark:text-gray-400">{{ t('footer.servicesList.consulting') }}</span>
</nav>
</div>
+5 -5
View File
@@ -2,16 +2,16 @@
const { t } = useI18n()
const services = computed(() => [
{
icon: 'i-lucide-package',
title: t('home.services.hytalePlugins.title'),
description: t('home.services.hytalePlugins.description'),
},
{
icon: 'i-lucide-monitor',
title: t('home.services.webDev.title'),
description: t('home.services.webDev.description'),
},
{
icon: 'i-lucide-smartphone',
title: t('home.services.mobileApps.title'),
description: t('home.services.mobileApps.description'),
},
{
icon: 'i-lucide-zap',
title: t('home.services.optimization.title'),
+34
View File
@@ -0,0 +1,34 @@
// Live Hytale plugins shipped publicly — featured on /hytale#demos
// Each entry has corresponding i18n keys at hytale.demos.{id}.*
export interface HytaleDemo {
id: string
image: string
github?: string
curseforge?: string
modtale?: string
website?: string
tech: string[]
status: 'live' | 'pending' | 'soon'
featured?: boolean
}
export const hytaleDemos: HytaleDemo[] = [
{
id: 'votepipe',
image: '/images/projects/votepipe.svg',
website: 'https://votepipe.com',
modtale: 'https://modtale.net/mod/votepipe',
curseforge: 'https://www.curseforge.com/hytale/mods/votepipe',
tech: ['Java 25', 'SaaS', 'Webhooks', 'Votifier'],
status: 'live',
featured: true,
},
{
id: 'gravity-flip',
image: '/images/projects/gravityflip.png',
modtale: 'https://modtale.net/mod/gravity-flip',
curseforge: 'https://curseforge.com/hytale/mods/gravity-flip',
tech: ['Java 25', 'Gradle Shadow', 'Hytale API'],
status: 'live',
},
]
+61 -2
View File
@@ -3,6 +3,67 @@ import type { Project } from '~~/shared/types'
// Base project data without translations
// Titles and descriptions are resolved via i18n keys: projects.${id}.title, projects.${id}.description
export const projects: Omit<Project, 'title' | 'description' | 'longDescription'>[] = [
{
id: 'votepipe',
image: '/images/projects/votepipe.svg',
technologies: ['Java 25', 'Hytale Plugin API', 'TypeScript', 'SaaS', 'HTTPS Webhooks', 'Votifier RSA/HMAC'],
category: 'Hytale Plugin',
date: '2026',
featured: true,
buttons: [
{
title: 'Website',
link: 'https://votepipe.com',
},
{
title: 'Modtale',
link: 'https://modtale.net/mod/votepipe',
},
{
title: 'CurseForge',
link: 'https://www.curseforge.com/hytale/mods/votepipe',
},
{
title: 'Documentation',
link: 'https://votepipe.com/docs',
},
],
},
{
id: 'gravity-flip',
image: '/images/projects/gravityflip.png',
technologies: ['Java 25', 'Hytale Plugin API', 'Gradle Shadow', 'JUnit 5'],
category: 'Hytale Plugin',
date: '2026',
featured: true,
buttons: [
{
title: 'Modtale',
link: 'https://modtale.net/mod/gravity-flip',
},
{
title: 'CurseForge',
link: 'https://curseforge.com/hytale/mods/gravity-flip',
},
],
},
{
id: 'playhours',
image: '/images/projects/playhours.png',
technologies: ['Java 17', 'Forge 1.20.1', 'LuckPerms', 'TOML Config'],
category: 'Minecraft Mod',
date: '2025',
buttons: [
{
title: 'CurseForge',
link: 'https://www.curseforge.com/minecraft/mc-mods/playhours',
},
{
title: 'Repository',
link: 'https://gitea.kamisama.ovh/kayjaydee/PlayHours',
},
],
},
{
id: 'virtual-tour',
image: '/images/virtualtour.webp',
@@ -22,7 +83,6 @@ export const projects: Omit<Project, 'title' | 'description' | 'longDescription'
technologies: ['Node.js', 'Discord.js', 'MongoDB', 'Express'],
category: 'Bot Development',
date: '2023',
featured: true,
buttons: [
{
title: 'Invite',
@@ -36,7 +96,6 @@ export const projects: Omit<Project, 'title' | 'description' | 'longDescription'
technologies: ['JavaScript', 'Node.js', 'Canvas', 'npm'],
category: 'Open Source',
date: '2022',
featured: true,
buttons: [
{
title: 'Repository',
+3
View File
@@ -31,6 +31,9 @@ useHead({
<div>
<HytaleHeroSection />
<HytaleServicesSection />
<div class="relative bg-gray-50/50 dark:bg-gray-900/20">
<HytaleDemoGrid />
</div>
<HytalePricingSection />
<div class="relative bg-gray-50/50 dark:bg-gray-900/20">
<TestimonialsSection />