[FEAT] More UI enhancements
This commit is contained in:
78
src/App.vue
78
src/App.vue
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div class="min-h-screen lg:h-screen flex flex-col p-4 sm:p-8 bg-slate-50 dark:bg-gray-950 transition-colors duration-300">
|
||||
<div class="flex flex-col flex-1 lg:overflow-hidden">
|
||||
<header class="mb-4 sm:mb-3">
|
||||
<div class="flex flex-col sm:flex-row justify-between items-center gap-4 mb-6">
|
||||
<div class="min-h-screen flex flex-col p-4 sm:p-8 bg-slate-50 dark:bg-gray-950 transition-colors duration-300" :class="{ 'lg:h-screen': layers.some(l => l.sprites.length) }">
|
||||
<div class="flex flex-col flex-1" :class="{ 'lg:overflow-hidden': layers.some(l => l.sprites.length) }">
|
||||
<header class="mb-6 sm:mb-5">
|
||||
<div class="flex flex-col sm:flex-row justify-between items-center gap-6 mb-8">
|
||||
<div class="text-center sm:text-left">
|
||||
<h1 class="text-3xl sm:text-5xl font-bold text-gray-900 dark:text-gray-100 tracking-tight mb-2">Spritesheet generator</h1>
|
||||
<p class="text-sm sm:text-base text-gray-600 dark:text-gray-400">Create professional spritesheets for your game development projects</p>
|
||||
<h1 class="text-3xl sm:text-5xl font-extrabold text-transparent bg-clip-text bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 tracking-tight mb-3">Spritesheet generator</h1>
|
||||
<p class="text-sm sm:text-base text-gray-600 dark:text-gray-400 font-medium">Create professional spritesheets for your game development projects</p>
|
||||
</div>
|
||||
<nav class="flex flex-wrap items-center justify-center gap-2">
|
||||
<nav class="flex flex-wrap items-center justify-center gap-3">
|
||||
<a
|
||||
href="https://gitea.adhd.sh/root/spritesheet-generator"
|
||||
target="_blank"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 bg-white/80 hover:bg-white dark:bg-gray-800/80 dark:hover:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:shadow-md transition-all"
|
||||
class="btn btn-secondary hover:shadow-md"
|
||||
data-rybbit-event="source-link"
|
||||
>
|
||||
<i class="fab fa-github"></i>
|
||||
@@ -20,7 +20,7 @@
|
||||
<a
|
||||
href="https://discord.gg/JTev3nzeDa"
|
||||
target="_blank"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 bg-white/80 hover:bg-white dark:bg-gray-800/80 dark:hover:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:shadow-md transition-all"
|
||||
class="btn btn-secondary hover:shadow-md"
|
||||
data-rybbit-event="discord-link"
|
||||
>
|
||||
<i class="fab fa-discord"></i>
|
||||
@@ -29,7 +29,7 @@
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="openHelpModal"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 bg-white/80 hover:bg-white dark:bg-gray-800/80 dark:hover:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:shadow-md transition-all"
|
||||
class="btn btn-secondary hover:shadow-md"
|
||||
data-rybbit-event="help-link"
|
||||
>
|
||||
<i class="fas fa-question-circle"></i>
|
||||
@@ -38,7 +38,7 @@
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="openFeedbackModal"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 bg-white/80 hover:bg-white dark:bg-gray-800/80 dark:hover:bg-gray-800 border border-gray-200 dark:border-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:shadow-md transition-all"
|
||||
class="btn btn-secondary hover:shadow-md"
|
||||
data-rybbit-event="feedback-link"
|
||||
>
|
||||
<i class="fas fa-comment-dots"></i>
|
||||
@@ -49,7 +49,7 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="flex-shrink-0 p-3 mb-3 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-700 border border-blue-200 dark:border-gray-600 rounded-2xl">
|
||||
<div class="flex-shrink-0 p-4 mb-6 bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-700 border border-blue-100 dark:border-gray-600 rounded-2xl shadow-sm">
|
||||
<div class="flex flex-col sm:flex-row items-center justify-between gap-2">
|
||||
<div class="flex items-center gap-2 text-center sm:text-left">
|
||||
<i class="text-lg text-blue-600 dark:text-blue-400 fas fa-ad"></i>
|
||||
@@ -71,9 +71,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main class="flex flex-col flex-1 bg-white/90 dark:bg-gray-800/90 backdrop-blur-sm rounded-3xl shadow-xl border border-gray-200/50 dark:border-gray-700/50 lg:overflow-hidden transition-all duration-300">
|
||||
<main class="flex flex-col flex-1 bg-white/80 dark:bg-gray-900/80 backdrop-blur-md rounded-3xl shadow-2xl border border-gray-200/50 dark:border-gray-700/50 transition-all duration-300" :class="{ 'lg:overflow-hidden': layers.some(l => l.sprites.length) }">
|
||||
<!-- Welcome state -->
|
||||
<div v-if="!layers.some(l => l.sprites.length)" class="p-6 sm:p-10 overflow-y-auto">
|
||||
<div v-if="!layers.some(l => l.sprites.length)" class="p-6 sm:p-10">
|
||||
<div class="mb-8">
|
||||
<h2 class="text-2xl font-bold text-gray-800 dark:text-gray-100 mb-1">Upload sprites or single image</h2>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Drag and drop images or import from JSON</p>
|
||||
@@ -115,7 +115,7 @@
|
||||
<div v-if="layers.some(l => l.sprites.length)" class="flex flex-col flex-1 lg:grid lg:grid-cols-[380px_1fr] xl:grid-cols-[420px_1fr] lg:overflow-hidden">
|
||||
<!-- Left sidebar - Controls -->
|
||||
<div class="p-6 bg-gray-50/50 dark:bg-gray-900/30 border-b lg:border-b-0 lg:border-r border-gray-200 dark:border-gray-700 lg:overflow-y-auto lg:overflow-x-auto">
|
||||
<div class="space-y-6">
|
||||
<div class="space-y-8">
|
||||
<!-- Upload Section -->
|
||||
<section>
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
@@ -123,15 +123,15 @@
|
||||
<i class="text-sm text-gray-700 dark:text-gray-300 fas fa-upload"></i>
|
||||
Upload
|
||||
</h3>
|
||||
<button @click="openJSONImportDialog" class="flex items-center gap-1.5 px-3 py-1.5 bg-gray-700 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-700 text-xs font-medium text-white rounded-lg transition-all cursor-pointer" data-rybbit-event="import-json">
|
||||
<button @click="openJSONImportDialog" class="btn btn-dark btn-sm" data-rybbit-event="import-json">
|
||||
<i class="text-xs fas fa-file-import"></i>
|
||||
<span>JSON</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-4 text-center border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-xl hover:border-gray-500 dark:hover:border-gray-400 transition-colors cursor-pointer" @click="openFileDialog">
|
||||
<i class="fas fa-plus-circle text-2xl text-gray-700 dark:text-gray-300 mb-2"></i>
|
||||
<p class="text-xs text-gray-600 dark:text-gray-400">Add sprites</p>
|
||||
</div>
|
||||
<button class="w-full p-6 text-center border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-xl hover:border-gray-500 dark:hover:border-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-all cursor-pointer focus:outline-none focus:ring-2 focus:ring-gray-500 group" @click="openFileDialog">
|
||||
<i class="fas fa-plus-circle text-3xl text-gray-400 dark:text-gray-500 group-hover:text-gray-600 dark:group-hover:text-gray-300 mb-3 transition-colors"></i>
|
||||
<p class="text-sm font-medium text-gray-600 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-gray-200 transition-colors">Add sprites</p>
|
||||
</button>
|
||||
<input ref="uploadInput" type="file" multiple accept="image/*" class="hidden" @change="handleUploadChange" />
|
||||
<input ref="jsonFileInput" type="file" accept=".json,application/json" class="hidden" @change="handleJSONFileChange" />
|
||||
</section>
|
||||
@@ -143,7 +143,7 @@
|
||||
<i class="text-sm text-gray-700 dark:text-gray-300 fas fa-layer-group"></i>
|
||||
Layers
|
||||
</h3>
|
||||
<button @click="addLayer()" class="flex items-center gap-1.5 px-3 py-1.5 bg-gray-700 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-700 text-xs font-medium text-white rounded-lg transition-all cursor-pointer">
|
||||
<button @click="addLayer()" class="btn btn-dark btn-sm">
|
||||
<i class="text-xs fas fa-plus"></i>
|
||||
<span>Add</span>
|
||||
</button>
|
||||
@@ -155,7 +155,7 @@
|
||||
class="flex items-center gap-2 px-3 py-2 bg-white dark:bg-gray-800 border rounded-lg transition-all"
|
||||
:class="[layer.id === activeLayerId ? 'border-gray-800 ring-1 ring-gray-800 dark:border-gray-400 dark:ring-gray-400' : 'border-gray-200 dark:border-gray-700', !layer.visible ? 'opacity-50' : '']"
|
||||
>
|
||||
<button @click.stop="layer.visible = !layer.visible" class="p-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors cursor-pointer" :title="layer.visible ? 'Hide layer' : 'Show layer'">
|
||||
<button @click.stop="layer.visible = !layer.visible" class="btn btn-ghost btn-icon-sm rounded" :title="layer.visible ? 'Hide layer' : 'Show layer'">
|
||||
<i :class="layer.visible ? 'text-sm text-gray-800 dark:text-gray-200 fas fa-eye' : 'text-sm text-gray-400 dark:text-gray-500 fas fa-eye-slash'"></i>
|
||||
</button>
|
||||
<input
|
||||
@@ -173,16 +173,16 @@
|
||||
{{ layer.name }}
|
||||
<span v-if="layer.sprites.length" class="ml-1 text-xs opacity-60">({{ layer.sprites.length }})</span>
|
||||
</button>
|
||||
<button v-if="editingLayerId !== layer.id" @click="startEditingLayer(layer.id, layer.name)" class="p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors cursor-pointer" title="Rename">
|
||||
<button v-if="editingLayerId !== layer.id" @click="startEditingLayer(layer.id, layer.name)" class="btn btn-ghost btn-icon-xs rounded" title="Rename">
|
||||
<i class="text-xs text-gray-600 dark:text-gray-400 fas fa-pen"></i>
|
||||
</button>
|
||||
<button @click="moveLayer(layer.id, 'up')" class="p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors cursor-pointer" title="Move up">
|
||||
<button @click="moveLayer(layer.id, 'up')" class="btn btn-ghost btn-icon-xs rounded" title="Move up">
|
||||
<i class="text-xs text-gray-600 dark:text-gray-400 fas fa-chevron-up"></i>
|
||||
</button>
|
||||
<button @click="moveLayer(layer.id, 'down')" class="p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors cursor-pointer" title="Move down">
|
||||
<button @click="moveLayer(layer.id, 'down')" class="btn btn-ghost btn-icon-xs rounded" title="Move down">
|
||||
<i class="text-xs text-gray-600 dark:text-gray-400 fas fa-chevron-down"></i>
|
||||
</button>
|
||||
<button v-if="layers.length > 1" @click="removeLayer(layer.id)" class="p-1 text-red-600 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-900/30 rounded transition-colors cursor-pointer" title="Delete">
|
||||
<button v-if="layers.length > 1" @click="removeLayer(layer.id)" class="btn btn-danger btn-icon-xs rounded" title="Delete">
|
||||
<i class="text-xs fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -198,7 +198,7 @@
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between px-3 py-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg">
|
||||
<label for="columns" class="text-sm font-medium text-gray-700 dark:text-gray-200">Columns</label>
|
||||
<input id="columns" type="number" v-model.number="columns" min="1" max="10" class="w-16 px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded outline-none focus:ring-2 focus:ring-gray-500" />
|
||||
<input id="columns" type="number" v-model.number="columns" min="1" max="10" class="input-field w-16" />
|
||||
</div>
|
||||
|
||||
<div class="px-3 py-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg">
|
||||
@@ -212,7 +212,7 @@
|
||||
v-model.number="settingsStore.manualCellWidth"
|
||||
min="1"
|
||||
max="2048"
|
||||
class="w-full min-w-0 px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded outline-none focus:ring-2 focus:ring-gray-500"
|
||||
class="input-field w-full min-w-0"
|
||||
placeholder="W"
|
||||
/>
|
||||
<span class="flex-shrink-0 text-gray-500 dark:text-gray-400">×</span>
|
||||
@@ -221,7 +221,7 @@
|
||||
v-model.number="settingsStore.manualCellHeight"
|
||||
min="1"
|
||||
max="2048"
|
||||
class="w-full min-w-0 px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded outline-none focus:ring-2 focus:ring-gray-500"
|
||||
class="input-field w-full min-w-0"
|
||||
placeholder="H"
|
||||
/>
|
||||
</div>
|
||||
@@ -237,22 +237,22 @@
|
||||
Align
|
||||
</h3>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<button @click="alignSprites('left')" class="px-3 py-2 text-xs font-medium text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg transition-all cursor-pointer" title="Left">
|
||||
<button @click="alignSprites('left')" class="btn btn-secondary btn-sm" title="Left">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('center')" class="px-3 py-2 text-xs font-medium text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg transition-all cursor-pointer" title="Center">
|
||||
<button @click="alignSprites('center')" class="btn btn-secondary btn-sm" title="Center">
|
||||
<i class="fas fa-arrows-left-right"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('right')" class="px-3 py-2 text-xs font-medium text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg transition-all cursor-pointer" title="Right">
|
||||
<button @click="alignSprites('right')" class="btn btn-secondary btn-sm" title="Right">
|
||||
<i class="fas fa-arrow-right"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('top')" class="px-3 py-2 text-xs font-medium text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg transition-all cursor-pointer" title="Top">
|
||||
<button @click="alignSprites('top')" class="btn btn-secondary btn-sm" title="Top">
|
||||
<i class="fas fa-arrow-up"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('middle')" class="px-3 py-2 text-xs font-medium text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg transition-all cursor-pointer" title="Middle">
|
||||
<button @click="alignSprites('middle')" class="btn btn-secondary btn-sm" title="Middle">
|
||||
<i class="fas fa-arrows-up-down"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('bottom')" class="px-3 py-2 text-xs font-medium text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg transition-all cursor-pointer" title="Bottom">
|
||||
<button @click="alignSprites('bottom')" class="btn btn-secondary btn-sm" title="Bottom">
|
||||
<i class="fas fa-arrow-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -265,19 +265,19 @@
|
||||
Export
|
||||
</h3>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<button @click="downloadSpritesheet" class="flex items-center justify-center gap-1.5 px-3 py-2 text-xs font-medium text-white bg-gray-700 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-700 rounded-lg transition-all cursor-pointer" data-rybbit-event="download-spritesheet">
|
||||
<button @click="downloadSpritesheet" class="btn btn-dark btn-sm" data-rybbit-event="download-spritesheet">
|
||||
<i class="fas fa-image"></i>
|
||||
<span>PNG</span>
|
||||
</button>
|
||||
<button @click="exportSpritesheetJSON" class="flex items-center justify-center gap-1.5 px-3 py-2 text-xs font-medium text-white bg-gray-700 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-700 rounded-lg transition-all cursor-pointer" data-rybbit-event="export-json">
|
||||
<button @click="exportSpritesheetJSON" class="btn btn-dark btn-sm" data-rybbit-event="export-json">
|
||||
<i class="fas fa-file-code"></i>
|
||||
<span>JSON</span>
|
||||
</button>
|
||||
<button @click="openGifFpsModal" class="flex items-center justify-center gap-1.5 px-3 py-2 text-xs font-medium text-white bg-gray-700 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-700 rounded-lg transition-all cursor-pointer" data-rybbit-event="download-gif">
|
||||
<button @click="openGifFpsModal" class="btn btn-dark btn-sm" data-rybbit-event="download-gif">
|
||||
<i class="fas fa-film"></i>
|
||||
<span>GIF</span>
|
||||
</button>
|
||||
<button @click="downloadAsZip" class="flex items-center justify-center gap-1.5 px-3 py-2 text-xs font-medium text-white bg-gray-700 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-700 rounded-lg transition-all cursor-pointer" data-rybbit-event="download-zip">
|
||||
<button @click="downloadAsZip" class="btn btn-dark btn-sm" data-rybbit-event="download-zip">
|
||||
<i class="fas fa-file-archive"></i>
|
||||
<span>ZIP</span>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user