VibeCodingStudio's picture
Initial DeepSite commit
48182f5 verified
Raw
History Blame Contribute Delete
17.9 kB
// Art Gallery Data
const artworks = [
{
id: 1,
title: "Cyberpunk City Nights",
artist: "NeonDreamer",
category: "digital",
image: "https://static.photos/technology/640x480/1",
avatar: "https://static.photos/people/200x200/1",
likes: 2453,
views: "12.5K",
comments: 89,
description: "A futuristic cityscape inspired by Blade Runner and cyberpunk aesthetics. Created in Blender and Photoshop over 3 weeks.",
tags: ["cyberpunk", "cityscape", "digital", "scifi"]
},
{
id: 2,
title: "Ethereal Forest Spirit",
artist: "MysticArt",
category: "digital",
image: "https://static.photos/nature/640x360/2",
avatar: "https://static.photos/people/200x200/2",
likes: 1892,
views: "8.2K",
comments: 56,
description: "Digital painting of an ancient forest guardian. Exploring light and atmosphere in fantasy environments.",
tags: ["fantasy", "forest", "spirit", "digitalpainting"]
},
{
id: 3,
title: "Portrait Study #42",
artist: "ArtStudent_99",
category: "traditional",
image: "https://static.photos/people/640x480/3",
avatar: "https://static.photos/people/200x200/3",
likes: 567,
views: "2.1K",
comments: 23,
description: "Oil on canvas. 40x50cm. Working on skin tones and lighting from natural window light.",
tags: ["oilpainting", "portrait", "traditional", "study"]
},
{
id: 4,
title: "Abstract Emotions",
artist: "ColorSplash",
category: "digital",
image: "https://static.photos/abstract/640x360/4",
avatar: "https://static.photos/people/200x200/4",
likes: 3201,
views: "15K",
comments: 134,
description: "Exploring the relationship between color and emotion. Digital abstract composition using custom brushes.",
tags: ["abstract", "colors", "emotional", "digital"]
},
{
id: 5,
title: "Mountain Mist",
artist: "NatureLens",
category: "photography",
image: "https://static.photos/nature/640x480/5",
avatar: "https://static.photos/people/200x200/5",
likes: 892,
views: "4.5K",
comments: 45,
description: "Early morning shot in the Alps. Sony A7III with 24-70mm lens. f/8, 1/125s, ISO 100.",
tags: ["photography", "landscape", "mountains", "nature"]
},
{
id: 6,
title: "Mecha Warrior",
artist: "RobotDesigner",
category: "3d",
image: "https://static.photos/technology/640x360/6",
avatar: "https://static.photos/people/200x200/6",
likes: 4521,
views: "22K",
comments: 201,
description: "Hard surface modeling practice. Rendered in Octane. Concept inspired by Gundam and Pacific Rim.",
tags: ["3d", "mecha", "robot", "scifi", "blender"]
},
{
id: 7,
title: "Cherry Blossom Dreams",
artist: "SakuraArtist",
category: "anime",
image: "https://static.photos/nature/640x480/7",
avatar: "https://static.photos/people/200x200/7",
likes: 5678,
views: "28K",
comments: 312,
description: "Original character illustration. Clip Studio Paint. Focusing on soft lighting and spring atmosphere.",
tags: ["anime", "manga", "illustration", "sakura"]
},
{
id: 8,
title: "Urban Decay",
artist: "StreetWalker",
category: "photography",
image: "https://static.photos/cityscape/640x360/8",
avatar: "https://static.photos/people/200x200/8",
likes: 1234,
views: "6.7K",
comments: 78,
description: "Abandoned factory exploration. High contrast black and white processing to emphasize texture.",
tags: ["urban", "abandoned", "blackwhite", "photography"]
},
{
id: 9,
title: "Dragon's Lair",
artist: "FantasyMaster",
category: "digital",
image: "https://static.photos/abstract/640x480/9",
avatar: "https://static.photos/people/200x200/9",
likes: 3892,
views: "18K",
comments: 156,
description: "Epic fantasy scene. Photoshop and Wacom Cintiq. 40+ hours of work on the scales alone!",
tags: ["dragon", "fantasy", "epic", "creature"]
},
{
id: 10,
title: "Minimalist Architecture",
artist: "CleanLines",
category: "photography",
image: "https://static.photos/minimal/640x360/10",
avatar: "https://static.photos/people/200x200/10",
likes: 2156,
views: "9.3K",
comments: 67,
description: "Geometry in modern architecture. Shot in Tokyo. Minimal post-processing to maintain clean lines.",
tags: ["architecture", "minimal", "geometry", "modern"]
},
{
id: 11,
title: "Space Station Omega",
artist: "StarTraveler",
category: "3d",
image: "https://static.photos/technology/640x480/11",
avatar: "https://static.photos/people/200x200/11",
likes: 1789,
views: "7.8K",
comments: 89,
description: "Environment design for sci-fi game project. Blender and Unreal Engine 5. Real-time rendering.",
tags: ["3d", "environment", "scifi", "space"]
},
{
id: 12,
title: "Watercolor Sunset",
artist: "BrushAndPaint",
category: "traditional",
image: "https://static.photos/nature/640x360/12",
avatar: "https://static.photos/people/200x200/12",
likes: 923,
views: "3.4K",
comments: 34,
description: "Wet-on-wet technique. Winsor & Newton watercolors. Arches cold press 300gsm.",
tags: ["watercolor", "traditional", "sunset", "seascape"]
}
];
let currentCategory = 'all';
let currentSort = 'newest';
let likedArtworks = new Set();
// Initialize
document.addEventListener('DOMContentLoaded', () => {
lucide.createIcons();
renderGallery();
setupEventListeners();
checkNullUndefined();
});
// Error handling demonstration for undefined/null
function checkNullUndefined() {
// Simulating safe data access patterns
const safeGet = (obj, path, defaultValue = null) => {
try {
return path.split('.').reduce((acc, part) =>
acc && acc[part] !== undefined ? acc[part] : defaultValue, obj);
} catch (e) {
console.warn('Null/Undefined value encountered:', path);
return defaultValue;
}
};
// Example of handling potentially undefined data
const testData = { artwork: { title: "Test", stats: null } };
// Safe access without errors
const title = safeGet(testData, 'artwork.title', 'Untitled');
const likes = safeGet(testData, 'artwork.stats.likes', 0);
console.log('Null/Undefined handling demo:', { title, likes });
}
function renderGallery() {
const gallery = document.getElementById('gallery');
const filtered = filterArtworks();
const sorted = sortArtworks(filtered);
gallery.innerHTML = sorted.map(artwork => createArtworkCard(artwork)).join('');
lucide.createIcons();
// Update count
document.getElementById('resultCount').textContent = `Showing ${sorted.length} artworks`;
}
function createArtworkCard(artwork) {
const isLiked = likedArtworks.has(artwork.id);
const likeClass = isLiked ? 'text-red-500 fill-current' : 'text-white';
return `
<div class="group relative bg-card rounded-lg overflow-hidden cursor-pointer transition-transform hover:scale-[1.02] hover:shadow-2xl"
onclick="openModal(${artwork.id})">
<div class="aspect-w-4 aspect-h-3 relative overflow-hidden">
<img src="${artwork.image}"
alt="${artwork.title}"
class="w-full h-64 object-cover transition-transform duration-500 group-hover:scale-110"
loading="lazy"
onerror="this.src='https://static.photos/abstract/640x480/999'">
<!-- Hover Overlay -->
<div class="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div class="absolute inset-0 hover-overlay"></div>
<div class="absolute bottom-0 left-0 right-0 p-4 transform translate-y-2 group-hover:translate-y-0 transition-transform">
<h3 class="font-bold text-white text-lg mb-1 line-clamp-1">${artwork.title}</h3>
<p class="text-gray-300 text-sm mb-2">by ${artwork.artist}</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-3 text-xs text-gray-300">
<span class="flex items-center gap-1">
<i data-lucide="heart" class="w-3 h-3"></i>
${formatNumber(artwork.likes)}
</span>
<span class="flex items-center gap-1">
<i data-lucide="message-circle" class="w-3 h-3"></i>
${artwork.comments}
</span>
</div>
<button onclick="toggleLike(event, ${artwork.id})"
class="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors ${likeClass}">
<i data-lucide="heart" class="w-4 h-4 ${isLiked ? 'fill-current'}"></i>
</button>
</div>
</div>
</div>
<!-- Category Badge -->
<div class="absolute top-3 left-3">
<span class="px-2 py-1 bg-black/60 backdrop-blur-sm rounded text-xs font-medium text-white capitalize">
${artwork.category}
</span>
</div>
</div>
</div>
`;
}
function filterArtworks() {
if (currentCategory === 'all') return artworks;
return artworks.filter(art => art.category === currentCategory);
}
function sortArtworks(artworks) {
const sorted = [...artworks];
switch(currentSort) {
case 'popular':
return sorted.sort((a, b) => b.likes - a.likes);
case 'comments':
return sorted.sort((a, b) => b.comments - a.comments);
default:
return sorted.sort((a, b) => b.id - a.id);
}
}
function formatNumber(num) {
if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K';
}
return num;
}
function toggleLike(event, id) {
event.stopPropagation();
if (likedArtworks.has(id)) {
likedArtworks.delete(id);
showToast('Removed from favorites');
} else {
likedArtworks.add(id);
showToast('Added to favorites');
// Add animation class
const btn = event.currentTarget;
btn.classList.add('like-anim');
setTimeout(() => btn.classList.remove('like-anim'), 300);
}
renderGallery();
}
function openModal(id) {
const artwork = artworks.find(a => a.id === id);
if (!artwork) {
console.error('Artwork not found:', id);
return;
}
document.getElementById('modalImage').src = artwork.image;
document.getElementById('modalTitle').textContent = artwork.title;
document.getElementById('modalCategory').textContent = artwork.category.charAt(0).toUpperCase() + artwork.category.slice(1);
document.getElementById('modalArtist').textContent = artwork.artist;
document.getElementById('modalAvatar').src = artwork.avatar;
document.getElementById('modalDescription').textContent = artwork.description;
document.getElementById('modalViews').textContent = artwork.views;
document.getElementById('modalComments').textContent = artwork.comments;
document.getElementById('modalLikes').textContent = artwork.likes;
// Render tags
const tagsContainer = document.getElementById('modalTags');
tagsContainer.innerHTML = artwork.tags.map(tag =>
`<span class="px-3 py-1 bg-gray-800 rounded-full text-xs text-gray-300 hover:bg-accent hover:text-dark cursor-pointer transition-colors">#${tag}</span>`
).join('');
// Setup like button in modal
const likeBtn = document.getElementById('modalLikeBtn');
const isLiked = likedArtworks.has(id);
updateModalLikeButton(likeBtn, isLiked);
likeBtn.onclick = () => {
if (likedArtworks.has(id)) {
likedArtworks.delete(id);
updateModalLikeButton(likeBtn, false);
showToast('Removed from favorites');
} else {
likedArtworks.add(id);
updateModalLikeButton(likeBtn, true);
showToast('Added to favorites');
}
renderGallery();
};
document.getElementById('imageModal').classList.remove('hidden');
document.body.style.overflow = 'hidden';
lucide.createIcons();
}
function updateModalLikeButton(btn, isLiked) {
const icon = btn.querySelector('i');
if (isLiked) {
icon.classList.add('text-red-500', 'fill-current');
icon.classList.remove('text-white');
} else {
icon.classList.remove('text-red-500', 'fill-current');
icon.classList.add('text-white');
}
}
function closeModal() {
document.getElementById('imageModal').classList.add('hidden');
document.body.style.overflow = '';
}
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('translate-y-20', 'opacity-0');
setTimeout(() => {
toast.classList.add('translate-y-20', 'opacity-0');
}, 3000);
}
function setupEventListeners() {
// Filter buttons
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.filter-btn').forEach(b => {
b.classList.remove('active', 'bg-accent', 'text-dark');
b.classList.add('bg-card', 'border-gray-700');
});
e.target.classList.remove('bg-card', 'border-gray-700');
e.target.classList.add('active', 'bg-accent', 'text-dark');
currentCategory = e.target.dataset.category;
renderGallery();
});
});
// Sort select
document.getElementById('sortSelect').addEventListener('change', (e) => {
currentSort = e.target.value;
renderGallery();
});
// Search
document.getElementById('searchInput').addEventListener('input', (e) => {
const query = e.target.value.toLowerCase();
if (query === '') {
renderGallery();
return;
}
const filtered = artworks.filter(art =>
art.title.toLowerCase().includes(query) ||
art.artist.toLowerCase().includes(query) ||
art.tags.some(tag => tag.toLowerCase().includes(query))
);
const gallery = document.getElementById('gallery');
gallery.innerHTML = filtered.map(artwork => createArtworkCard(artwork)).join('');
lucide.createIcons();
document.getElementById('resultCount').textContent = `Found ${filtered.length} artworks`;
});
// Modal close
document.getElementById('closeModal').addEventListener('click', closeModal);
document.getElementById('imageModal').addEventListener('click', (e) => {
if (e.target.id === 'imageModal') closeModal();
});
// Keyboard navigation
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeModal();
});
// Load more (simulation)
document.getElementById('loadMoreBtn').addEventListener('click', () => {
const loadingState = document.getElementById('loadingState');
const btn = document.getElementById('loadMoreBtn');
btn.classList.add('hidden');
loadingState.classList.remove('hidden');
setTimeout(() => {
loadingState.classList.add('hidden');
btn.classList.remove('hidden');
showToast('More artworks loaded!');
// Duplicate existing artworks with new IDs to simulate more data
const newArtworks = artworks.map(art => ({
...art,
id: art.id + artworks.length,
likes: Math.floor(Math.random() * 5000)
}));
artworks.push(...newArtworks);
renderGallery();
}, 1500);
});
// Theme toggle
let isDark = true;
document.getElementById('themeToggle').addEventListener('click', () => {
isDark = !isDark;
const icon = document.querySelector('#themeToggle i');
if (isDark) {
document.documentElement.classList.remove('light');
icon.setAttribute('data-lucide', 'moon');
} else {
document.documentElement.classList.add('light');
icon.setAttribute('data-lucide', 'sun');
}
lucide.createIcons();
});
}
// Handle null/undefined image errors globally
window.addEventListener('error', (e) => {
if (e.target.tagName === 'IMG') {
console.warn('Image failed to load:', e.target.src);
e.target.src = 'https://static.photos/abstract/640x480/999';
e.target.onerror = null; // Prevent infinite loop
}
}, true);