332 lines
13 KiB
Vue

<template>
<Modal :is-open="isOpen" @close="close" title="Help & information" :initialWidth="800" :initialHeight="600">
<div class="flex flex-col h-full">
<!-- Tabs -->
<div class="flex border-b border-gray-200">
<button v-for="(tab, index) in tabs" :key="index" @click="activeTab = index" class="px-4 py-2 font-medium text-sm transition-colors" :class="activeTab === index ? 'text-blue-600 border-b-2 border-blue-500' : 'text-gray-600 hover:text-blue-500'">
{{ tab.name }}
</button>
</div>
<!-- Tab Content -->
<div class="flex-1 overflow-auto p-4">
<!-- Video Instructions Tab -->
<div v-if="activeTab === 0" class="h-full flex flex-col">
<h3 class="text-lg font-semibold mb-4">Video tutorial</h3>
<div class="flex-1 bg-gray-100 rounded-lg flex items-center justify-center">
<div class="w-full aspect-video max-w-3xl mx-auto">
<video controls class="w-full h-full object-contain rounded-lg shadow-md">
<source src="@/assets/tut.mp4" type="video/mp4" />
</video>
</div>
</div>
</div>
<!-- About & Instructions Tab -->
<div v-if="activeTab === 1" class="h-full overflow-y-auto">
<h3 class="text-lg font-semibold mb-4">About Spritesheet generator</h3>
<div class="max-w-none text-gray-700">
<p class="mb-4 text-base leading-relaxed">Spritesheet generator is a free, open-source tool for creating spritesheets for game development and animation projects. This tool allows you to upload individual sprite images and arrange them into a spritesheet with customizable layout.</p>
<h4 class="mt-6 mb-3 text-lg font-medium text-gray-900">How to use:</h4>
<ol class="list-decimal pl-6 space-y-2 mb-4">
<li>Upload your sprite images by dragging and dropping them or clicking the upload area</li>
<li>If you upload a single large image, you'll be asked if you want to split it into individual sprites</li>
<li>Arrange your sprites by dragging them to the desired position</li>
<li>Adjust the number of columns to change the layout</li>
<li>Preview your animation using the "Preview Animation" button</li>
<li>Download your spritesheet as a PNG file</li>
</ol>
<h4 class="mt-6 mb-3 text-lg font-medium text-gray-900">Tips:</h4>
<ul class="list-disc pl-6 space-y-2">
<li>For best results, use sprites with consistent dimensions</li>
<li>When uploading a spritesheet, you can split it automatically into individual sprites</li>
<li>The spritesheet splitter allows you to specify rows and columns or try auto-detection</li>
<li>The preview animation plays frames in the order they appear in the spritesheet (left to right, top to bottom)</li>
<li>You can adjust the animation speed in the preview window</li>
<li>The tool works entirely in your browser - no files are uploaded to any server</li>
</ul>
</div>
</div>
<!-- Donations Tab -->
<div v-if="activeTab === 2" class="h-full overflow-y-auto">
<h3 class="text-lg font-semibold mb-4">Support my <a href="https://xvx.sh" title="XVX" target="_blank" class="text-blue-500 hover:text-blue-600 transition-colors">projects</a>.</h3>
<p class="mb-6">
Spritesheet generator is free and open-source software. If you find it useful, please consider supporting the project with a donation. Check out the source code on
<a href="https://gitea.directonline.io/noxious/spritesheet-generator" title="Source code" target="_blank" class="text-blue-500 hover:text-blue-600 transition-colors">Gitea</a>.
</p>
<div class="space-y-6">
<!-- Bitcoin -->
<div class="p-4 bg-gray-50 rounded-lg border border-gray-200">
<div class="flex items-center mb-3">
<div class="w-8 h-8 mr-3 flex items-center justify-center bg-orange-100 rounded-full">
<svg class="w-5 h-5 text-orange-500" viewBox="0 0 24 24" fill="currentColor">
<path
d="M23.638 14.904c-1.602 6.43-8.113 10.34-14.542 8.736C2.67 22.05-1.244 15.525.362 9.105 1.962 2.67 8.475-1.243 14.9.358c6.43 1.605 10.342 8.115 8.738 14.548v-.002zm-6.35-4.613c.24-1.59-.974-2.45-2.64-3.03l.54-2.153-1.315-.33-.525 2.107c-.345-.087-.705-.167-1.064-.25l.526-2.127-1.32-.33-.54 2.165c-.285-.067-.565-.132-.84-.2l-1.815-.45-.35 1.407s.975.225.955.236c.535.136.63.486.615.766l-1.477 5.92c-.075.166-.24.406-.614.314.015.02-.96-.24-.96-.24l-.66 1.51 1.71.426.93.242-.54 2.19 1.32.327.54-2.17c.36.1.705.19 1.05.273l-.51 2.154 1.32.33.545-2.19c2.24.427 3.93.257 4.64-1.774.57-1.637-.03-2.58-1.217-3.196.854-.193 1.5-.76 1.68-1.93h.01zm-3.01 4.22c-.404 1.64-3.157.75-4.05.53l.72-2.9c.896.23 3.757.67 3.33 2.37zm.41-4.24c-.37 1.49-2.662.735-3.405.55l.654-2.64c.744.18 3.137.524 2.75 2.084v.006z"
/>
</svg>
</div>
<h4 class="text-md font-medium">Bitcoin (BTC)</h4>
</div>
<div class="flex">
<input type="text" readonly value="bc1ql2a3nxnhfwft7qex0cclj5ar2lfsslvs0aygeq" class="flex-1 p-2 text-sm bg-white border border-gray-200 rounded-l-md focus:outline-none" />
<button @click="copyToClipboard('bc1ql2a3nxnhfwft7qex0cclj5ar2lfsslvs0aygeq')" class="px-3 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 transition-colors">Copy</button>
</div>
</div>
<!-- Litecoin -->
<div class="p-4 bg-gray-50 rounded-lg border border-gray-200">
<div class="flex items-center mb-3">
<div class="w-8 h-8 mr-3 flex items-center justify-center bg-blue-100 rounded-full">
<svg class="w-5 h-5 text-blue-500" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0a12 12 0 1 0 0 24 12 12 0 0 0 0-24zm-.738 4.8h2.616l-1.654 6.187 2.566-.338-1.017 3.63-2.115.338-1.23 4.6h7.12l-.56 2.07H6.96l1.654-6.16-2.565.337.952-3.38 2.18-.338 1.23-4.662H4.8l.504-2.285h5.958z" />
</svg>
</div>
<h4 class="text-md font-medium">Litecoin (LTC)</h4>
</div>
<div class="flex">
<input type="text" readonly value="ltc1qdkn46hpt39ppmhk25ed2eycu7m2pj5cdzuxw84" class="flex-1 p-2 text-sm bg-white border border-gray-200 rounded-l-md focus:outline-none" />
<button @click="copyToClipboard('ltc1qdkn46hpt39ppmhk25ed2eycu7m2pj5cdzuxw84')" class="px-3 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 transition-colors">Copy</button>
</div>
</div>
<!-- Ethereum -->
<div class="p-4 bg-gray-50 rounded-lg border border-gray-200">
<div class="flex items-center mb-3">
<div class="w-8 h-8 mr-3 flex items-center justify-center bg-purple-100 rounded-full">
<svg class="w-5 h-5 text-purple-500" viewBox="0 0 24 24" fill="currentColor">
<path d="M11.944 17.97L4.58 13.62 11.943 24l7.37-10.38-7.372 4.35h.003zM12.056 0L4.69 12.223l7.365 4.354 7.365-4.35L12.056 0z" />
</svg>
</div>
<h4 class="text-md font-medium">Ethereum (ETH)</h4>
</div>
<div class="flex">
<input type="text" readonly value="0x30843c72DF6E9A9226d967bf2403602f1C2aB67b" class="flex-1 p-2 text-sm bg-white border border-gray-200 rounded-l-md focus:outline-none" />
<button @click="copyToClipboard('0x30843c72DF6E9A9226d967bf2403602f1C2aB67b')" class="px-3 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 transition-colors">Copy</button>
</div>
</div>
<p class="text-center text-gray-600 mt-6">Thank you for your support! ❤️</p>
</div>
</div>
<!-- Changelog Tab -->
<div v-if="activeTab === 3" class="h-full overflow-y-auto changelog-container">
<h3 class="text-lg font-semibold mb-4">Changelog</h3>
<div v-if="!changelogHtml" class="text-gray-500 text-center py-8">Loading changelog...</div>
<div v-else class="prose prose-sm max-w-none" v-html="changelogHtml"></div>
</div>
</div>
</div>
</Modal>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { marked } from 'marked';
import Modal from './utilities/Modal.vue';
const props = defineProps<{
isOpen: boolean;
}>();
const emit = defineEmits<{
(e: 'close'): void;
}>();
const activeTab = ref(0);
const changelogHtml = ref<string>('');
const tabs = [{ name: 'Video tutorial' }, { name: 'About & instructions' }, { name: 'Support the project' }, { name: 'Changelog' }];
const loadChangelog = async () => {
try {
const response = await fetch('/CHANGELOG.md');
const text = await response.text();
// Configure marked options
marked.setOptions({
gfm: true, // GitHub Flavored Markdown
breaks: true, // Convert line breaks to <br>
});
// Convert markdown to HTML
changelogHtml.value = await marked(text);
} catch (error) {
console.error('Failed to load changelog:', error);
changelogHtml.value = '<p class="text-red-500">Failed to load changelog</p>';
}
};
onMounted(() => {
loadChangelog();
});
const close = () => {
emit('close');
};
const copyToClipboard = (text: string) => {
navigator.clipboard
.writeText(text)
.then(() => {
alert('Address copied to clipboard!');
})
.catch(err => {
console.error('Failed to copy: ', err);
});
};
</script>
<style>
/* Global styles for changelog content */
.changelog-container .prose {
color: #374151; /* text-gray-700 */
}
.changelog-container .prose h1 {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 1rem;
margin-top: 1.5rem;
}
.changelog-container .prose h2 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.75rem;
margin-top: 1.25rem;
border-left-width: 2px;
padding-left: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.changelog-container .prose h2:first-of-type {
border-left-color: #3b82f6; /* border-blue-500 */
}
.changelog-container .prose h2:not(:first-of-type) {
border-left-color: #d1d5db; /* border-gray-300 */
}
.changelog-container .prose h3 {
font-size: 1.125rem;
font-weight: 500;
margin-bottom: 0.5rem;
margin-top: 1rem;
}
.changelog-container .prose h4 {
font-size: 1rem;
font-weight: 500;
margin-bottom: 0.5rem;
margin-top: 0.75rem;
}
.changelog-container .prose ul {
list-style-type: disc;
list-style-position: inside;
margin-bottom: 1rem;
margin-left: 1rem;
}
.changelog-container .prose ol {
list-style-type: decimal;
list-style-position: inside;
margin-bottom: 1rem;
margin-left: 1rem;
}
.changelog-container .prose li {
margin-bottom: 0.25rem;
}
.changelog-container .prose p {
margin-bottom: 1rem;
}
.changelog-container .prose a {
color: #3b82f6; /* text-blue-500 */
text-decoration: underline;
}
.changelog-container .prose a:hover {
color: #2563eb; /* text-blue-600 */
}
.changelog-container .prose strong {
font-weight: 700;
}
.changelog-container .prose em {
font-style: italic;
}
.changelog-container .prose blockquote {
padding-left: 1rem;
border-left-width: 4px;
border-left-color: #d1d5db; /* border-gray-300 */
font-style: italic;
margin-top: 1rem;
margin-bottom: 1rem;
}
.changelog-container .prose code {
background-color: #f3f4f6; /* bg-gray-100 */
border-radius: 0.25rem;
padding-left: 0.25rem;
padding-right: 0.25rem;
padding-top: 0.125rem;
padding-bottom: 0.125rem;
font-family: monospace;
font-size: 0.875rem;
}
.changelog-container .prose pre {
background-color: #f3f4f6; /* bg-gray-100 */
border-radius: 0.25rem;
padding: 1rem;
margin-bottom: 1rem;
overflow-x: auto;
}
.changelog-container .prose pre code {
background-color: transparent;
padding: 0;
}
.changelog-container .prose hr {
margin-top: 2rem;
margin-bottom: 2rem;
border-top-width: 1px;
border-color: #e5e7eb; /* border-gray-200 */
}
.changelog-container .prose table {
width: 100%;
margin-bottom: 1rem;
}
.changelog-container .prose th {
padding: 0.5rem 1rem;
border-width: 1px;
border-color: #d1d5db; /* border-gray-300 */
background-color: #f9fafb; /* bg-gray-50 */
font-weight: 600;
}
.changelog-container .prose td {
padding: 0.5rem 1rem;
border-width: 1px;
border-color: #d1d5db; /* border-gray-300 */
}
</style>