mirror of
https://github.com/usetrmnl/tailor-designs.git
synced 2026-04-29 13:45:22 -07:00
gallery first draft
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
# Tailor Designs Repository
|
||||
|
||||
## Purpose
|
||||
This repository stores screen designs created by individuals. Screens are pushed via pull requests and validated through a GitHub Actions workflow.
|
||||
|
||||
## File Structure
|
||||
- `screens/` - Directory containing all screen files
|
||||
- `.github/workflows/validate-images.yml` - Workflow that validates file naming conventions
|
||||
|
||||
## Naming Convention
|
||||
Files in the `screens/` directory must follow this format:
|
||||
```
|
||||
{WIDTH}x{HEIGHT}-{TYPE}-{NAME}.{EXT}
|
||||
```
|
||||
|
||||
Where:
|
||||
- `{WIDTH}x{HEIGHT}` - Screen resolution (e.g., 800x480)
|
||||
- `{TYPE}` - Type of screen, either "loading" or "splash"
|
||||
- `{NAME}` - Descriptive name using lowercase letters, numbers, underscores
|
||||
- `{EXT}` - File extension (must be PNG)
|
||||
|
||||
## Example Files
|
||||
- `800x480-splash-hourglass_with_butterflies.png`
|
||||
- `800x480-loading-dungeon_crawler_carl_princess_donut.png`
|
||||
|
||||
## Gallery Features
|
||||
The gallery webpage provides:
|
||||
- Filter by screen type (splash/loading)
|
||||
- Search functionality
|
||||
- Responsive grid layout
|
||||
- Modern UI with Tailwind CSS styling
|
||||
|
||||
## Validation Workflow
|
||||
The validation workflow ensures all files:
|
||||
1. Use only lowercase letters, numbers, underscores, and hyphens
|
||||
2. Have 3 or 4 segments separated by hyphens
|
||||
3. Have a valid PNG file format
|
||||
4. Match the specified dimensions in filename with actual image dimensions
|
||||
5. Have the correct type (loading or splash)
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Screen Gallery</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#3b82f6',
|
||||
secondary: '#1e40af',
|
||||
dark: '#0f172a',
|
||||
light: '#f8fafc'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.gallery-item {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.gallery-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.filter-btn.active {
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
.screen-image {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.screen-container {
|
||||
aspect-ratio: 16/9;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
<header class="bg-white shadow-sm">
|
||||
<div class="container mx-auto px-4 py-6">
|
||||
<h1 class="text-3xl font-bold text-gray-900">Screen Gallery</h1>
|
||||
<p class="text-gray-600 mt-2">Browse all the screens created by contributors</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container mx-auto px-4 py-8">
|
||||
<!-- Filters -->
|
||||
<div class="mb-8 bg-white rounded-lg shadow-sm p-6">
|
||||
<div class="flex flex-wrap gap-4 items-center justify-between">
|
||||
<h2 class="text-xl font-semibold text-gray-900">Filter Screens</h2>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button id="all-filter" class="filter-btn px-4 py-2 rounded-lg bg-gray-100 hover:bg-blue-100 text-gray-800 hover:text-blue-700 transition-colors">All</button>
|
||||
<button id="splash-filter" class="filter-btn px-4 py-2 rounded-lg bg-gray-100 hover:bg-blue-100 text-gray-800 hover:text-blue-700 transition-colors">Splash</button>
|
||||
<button id="loading-filter" class="filter-btn px-4 py-2 rounded-lg bg-gray-100 hover:bg-blue-100 text-gray-800 hover:text-blue-700 transition-colors">Loading</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="text" id="search-input" placeholder="Search screens..." class="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
|
||||
<button id="clear-search" class="px-3 py-2 text-gray-500 hover:text-gray-700">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Screen Grid -->
|
||||
<div id="gallery-container" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
<!-- Screens will be dynamically loaded here -->
|
||||
</div>
|
||||
|
||||
<!-- Empty state -->
|
||||
<div id="empty-state" class="text-center py-12 hidden">
|
||||
<i class="fas fa-search text-gray-400 text-5xl mb-4"></i>
|
||||
<h3 class="text-xl font-medium text-gray-900 mb-2">No screens found</h3>
|
||||
<p class="text-gray-500">Try adjusting your search or filter criteria</p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="bg-white border-t mt-12">
|
||||
<div class="container mx-auto px-4 py-6 text-center text-gray-500">
|
||||
<p>Screen Gallery - A collection of screen designs</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Sample data for screens (in a real implementation, this would come from a JSON file or API)
|
||||
const screens = [
|
||||
{
|
||||
filename: "800x480-splash-hourglass_with_butterflies.png",
|
||||
width: 800,
|
||||
height: 480,
|
||||
type: "splash",
|
||||
name: "hourglass_with_butterflies"
|
||||
},
|
||||
{
|
||||
filename: "800x480-splash-blank.png",
|
||||
width: 800,
|
||||
height: 480,
|
||||
type: "splash",
|
||||
name: "blank"
|
||||
},
|
||||
{
|
||||
filename: "800x480-splash-a_fishing_party_winslow_homer-clevelandart_org.png",
|
||||
width: 800,
|
||||
height: 480,
|
||||
type: "splash",
|
||||
name: "a_fishing_party_winslow_homer-clevelandart_org"
|
||||
},
|
||||
{
|
||||
filename: "800x480-loading-dungeon_crawler_carl_princess_donut.png",
|
||||
width: 800,
|
||||
height: 480,
|
||||
type: "loading",
|
||||
name: "dungeon_crawler_carl_princess_donut"
|
||||
}
|
||||
];
|
||||
|
||||
// Function to parse filename and extract metadata
|
||||
function parseFilename(filename) {
|
||||
const parts = filename.split('-');
|
||||
if (parts.length < 3) return null;
|
||||
|
||||
const [resolution, type, ...nameParts] = parts;
|
||||
const [width, height] = resolution.split('x').map(Number);
|
||||
const name = nameParts.join('_').split('.')[0];
|
||||
|
||||
return {
|
||||
filename,
|
||||
width,
|
||||
height,
|
||||
type: type.toLowerCase(),
|
||||
name
|
||||
};
|
||||
}
|
||||
|
||||
// Function to render screens
|
||||
function renderScreens(screensToRender) {
|
||||
const container = document.getElementById('gallery-container');
|
||||
const emptyState = document.getElementById('empty-state');
|
||||
|
||||
if (screensToRender.length === 0) {
|
||||
emptyState.classList.remove('hidden');
|
||||
container.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
emptyState.classList.add('hidden');
|
||||
container.innerHTML = screensToRender.map(screen => `
|
||||
<div class="gallery-item bg-white rounded-lg shadow-sm overflow-hidden">
|
||||
<div class="screen-container flex items-center justify-center p-4 bg-gray-100">
|
||||
<img src="screens/${screen.filename}"
|
||||
alt="${screen.name}"
|
||||
class="screen-image max-h-64 object-contain"
|
||||
onerror="this.src='https://placehold.co/300x200?text=Image+Not+Found'">
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<h3 class="font-semibold text-gray-900 truncate">${screen.name.replace(/_/g, ' ')}</h3>
|
||||
<div class="flex justify-between items-center mt-2">
|
||||
<span class="text-sm text-gray-500">${screen.width}×${screen.height}</span>
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 capitalize">
|
||||
${screen.type}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Filter and search functionality
|
||||
function filterScreens() {
|
||||
const searchTerm = document.getElementById('search-input').value.toLowerCase();
|
||||
const activeFilter = document.querySelector('.filter-btn.active')?.id;
|
||||
|
||||
let filteredScreens = screens;
|
||||
|
||||
// Apply type filter
|
||||
if (activeFilter === 'splash-filter') {
|
||||
filteredScreens = filteredScreens.filter(screen => screen.type === 'splash');
|
||||
} else if (activeFilter === 'loading-filter') {
|
||||
filteredScreens = filteredScreens.filter(screen => screen.type === 'loading');
|
||||
}
|
||||
|
||||
// Apply search filter
|
||||
if (searchTerm) {
|
||||
filteredScreens = filteredScreens.filter(screen =>
|
||||
screen.name.toLowerCase().includes(searchTerm) ||
|
||||
`${screen.width}×${screen.height}`.toLowerCase().includes(searchTerm)
|
||||
);
|
||||
}
|
||||
|
||||
renderScreens(filteredScreens);
|
||||
}
|
||||
|
||||
// Initialize the gallery
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Parse filenames to get metadata
|
||||
const parsedScreens = screens.map(screen => parseFilename(screen.filename));
|
||||
|
||||
// Set up filter buttons
|
||||
document.querySelectorAll('.filter-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
// Remove active class from all buttons
|
||||
document.querySelectorAll('.filter-btn').forEach(btn => {
|
||||
btn.classList.remove('active');
|
||||
});
|
||||
// Add active class to clicked button
|
||||
this.classList.add('active');
|
||||
|
||||
filterScreens();
|
||||
});
|
||||
});
|
||||
|
||||
// Set up search functionality
|
||||
const searchInput = document.getElementById('search-input');
|
||||
searchInput.addEventListener('input', filterScreens);
|
||||
|
||||
// Clear search button
|
||||
document.getElementById('clear-search').addEventListener('click', function() {
|
||||
searchInput.value = '';
|
||||
filterScreens();
|
||||
});
|
||||
|
||||
// Initial render
|
||||
renderScreens(parsedScreens);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7c31f2e363a92765914e34d2135edc9168e78690de86d0b756bbfb35f8796e18
|
||||
size 9475
|
||||
Reference in New Issue
Block a user