Commit 8aa32432 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: complete theming system — 67 emoji replacements, juice settings, full asset registry

ASSET_REGISTRY.json: machine-readable sweep of entire codebase
- 67 emojis with exact file:line locations
- 80 unique colors
- 14 gradients
- 24 animations
- 56 CSS variables

Admin panel new sections:
- 🧃 Juice Settings: particles on/off, shake intensity, haptic, confetti count,
  coin fly count, bounce scale, slam scale, float amount
- 😀 Emoji Replacements: 27 most visible emojis as upload slots
  Each with: current emoji preview, size hint, usage description
  Upload SVG/PNG → replaces emoji throughout the app

theme.js updated:
- emoji() helper: returns uploaded image OR fallback emoji at exact size
- image-rendering: -webkit-optimize-contrast (fixes pixelation on non-retina)
- All images use object-fit:contain (never overflow their expected box)
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent b2c350bc
{
"emojis": [
{
"emoji": "←",
"locations": [
"./public/js/modules/org/scenes/home.js:12",
"./public/js/modules/profile/scenes/settings.js:13",
"./public/js/modules/chess/scenes/review.js:19"
],
"context": "<button class=\"btn btn-secondary\" id=\"back-btn\" style=\"min-height:30px;padding:4"
},
{
"emoji": "→",
"locations": [
"./public/js/modules/rank/scenes/tournaments.js:77"
],
"context": "// Card click → detail"
},
{
"emoji": "⏳",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:19"
],
"context": "{ key: 'zugzwang', name: 'تسوغزوانغ', nameEn: 'Zugzwang', icon: '⏳' },"
},
{
"emoji": "◆",
"locations": [
"./public/js/core/hud.js:42"
],
"context": "<span class=\"icon\">◆</span>"
},
{
"emoji": "◎",
"locations": [
"./public/js/modules/play/scenes/table.js:13"
],
"context": "{ key: 'backgammon', name: 'طاولة', nameEn: 'Backgammon', color: '#F59E0B', seco"
},
{
"emoji": "●",
"locations": [
"./public/js/core/hud.js:38"
],
"context": "<span class=\"icon\">●</span>"
},
{
"emoji": "●●●",
"locations": [
"./public/js/modules/chess/scenes/game.js:53"
],
"context": "${t('game.thinking')} <span class=\"pulse\">●●●</span>"
},
{
"emoji": "★",
"locations": [
"./public/js/modules/chess/scenes/review.js:178",
"./public/js/modules/chess/logic/classifier.js:43"
],
"context": "return { class: 'best', symbol: '★', color: '#10B981' };"
},
{
"emoji": "♕",
"locations": [
"./public/js/modules/chess/scenes/game.js:176"
],
"context": "if (color === 'w') { names.q = '♕'; names.r = '♖'; names.b = '♗'; names.n = '♘';"
},
{
"emoji": "♖",
"locations": [
"./public/js/modules/chess/scenes/game.js:176"
],
"context": "if (color === 'w') { names.q = '♕'; names.r = '♖'; names.b = '♗'; names.n = '♘';"
},
{
"emoji": "♗",
"locations": [
"./public/js/modules/chess/scenes/game.js:176"
],
"context": "if (color === 'w') { names.q = '♕'; names.r = '♖'; names.b = '♗'; names.n = '♘';"
},
{
"emoji": "♘",
"locations": [
"./public/js/modules/chess/scenes/game.js:176"
],
"context": "if (color === 'w') { names.q = '♕'; names.r = '♖'; names.b = '♗'; names.n = '♘';"
},
{
"emoji": "♚",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:4",
"./public/js/modules/chess/scenes/game.js:331"
],
"context": "const pieceSymbols = { p: '♟', n: '♞', b: '♝', r: '♜', q: '♛', k: '♚' };"
},
{
"emoji": "♛",
"locations": [
"./public/js/modules/chess/scenes/game.js:175",
"./public/js/modules/chess/scenes/game.js:331"
],
"context": "const pieceSymbols = { p: '♟', n: '♞', b: '♝', r: '♜', q: '♛', k: '♚' };"
},
{
"emoji": "♜",
"locations": [
"./public/js/modules/chess/scenes/game.js:175",
"./public/js/modules/chess/scenes/game.js:331"
],
"context": "const pieceSymbols = { p: '♟', n: '♞', b: '♝', r: '♜', q: '♛', k: '♚' };"
},
{
"emoji": "♝",
"locations": [
"./public/js/modules/chess/scenes/game.js:175",
"./public/js/modules/chess/scenes/game.js:331"
],
"context": "const pieceSymbols = { p: '♟', n: '♞', b: '♝', r: '♜', q: '♛', k: '♚' };"
},
{
"emoji": "♞",
"locations": [
"./public/js/modules/chess/scenes/game.js:175",
"./public/js/modules/chess/scenes/game.js:331"
],
"context": "const pieceSymbols = { p: '♟', n: '♞', b: '♝', r: '♜', q: '♛', k: '♚' };"
},
{
"emoji": "♟",
"locations": [
"./admin/branding.php:158",
"./public/js/modules/puzzles/logic/themes.js:30",
"./public/js/modules/chess/scenes/game.js:331"
],
"context": "const pieceSymbols = { p: '♟', n: '♞', b: '♝', r: '♜', q: '♛', k: '♚' };"
},
{
"emoji": "⚐",
"locations": [
"./public/js/modules/chess/scenes/game.js:82"
],
"context": "<button class=\"ctrl-btn\" id=\"btn-resign\">⚐ ${t('game.resign')}</button>"
},
{
"emoji": "⚔️",
"locations": [
"./public/js/modules/social/scenes/friends.js:49",
"./public/js/modules/puzzles/logic/themes.js:13",
"./public/js/modules/puzzles/logic/themes.js:14"
],
"context": "{ key: 'attack', name: 'هجوم', nameEn: 'Attack', icon: '⚔️' },"
},
{
"emoji": "⚡",
"locations": [
"./admin/branding.php:185",
"./public/js/modules/puzzles/scenes/puzzle.js:54",
"./public/js/modules/puzzles/logic/themes.js:5"
],
"context": "{ key: 'short', name: 'قصيرة', nameEn: 'Short', icon: '⚡' },"
},
{
"emoji": "✅",
"locations": [
"./public/js/modules/org/scenes/home.js:74",
"./public/js/modules/shop/scenes/browse.js:90",
"./public/js/modules/puzzles/scenes/puzzle.js:126"
],
"context": "status.textContent = '✅ أحسنت! حل صحيح';"
},
{
"emoji": "✓",
"locations": [
"./admin/branding.php:88",
"./admin/branding.php:287",
"./public/js/modules/social/scenes/friends.js:35"
],
"context": "<button class=\"btn btn-primary\" style=\"font-size:14px;\" onclick=\"navigator.clipb"
},
{
"emoji": "✕",
"locations": [
"./public/js/modules/play/scenes/table.js:222"
],
"context": "<button class=\"game-menu-close\" id=\"menu-close\">✕</button>"
},
{
"emoji": "✦",
"locations": [
"./public/js/core/juice.js:23"
],
"context": "el.textContent = '✦';"
},
{
"emoji": "❌",
"locations": [
"./public/js/modules/puzzles/scenes/puzzle.js:139"
],
"context": "status.textContent = '❌ حل خاطئ';"
},
{
"emoji": "⬚",
"locations": [
"./public/js/modules/domino/scenes/room.js:10",
"./public/js/modules/rank/scenes/leaderboard.js:15"
],
"context": "<button class=\"btn btn-secondary\" data-game=\"domino\" style=\"font-size:12px;min-h"
},
{
"emoji": "⬡",
"locations": [
"./public/js/core/hud.js:34",
"./public/js/modules/ludo/scenes/room.js:10",
"./public/js/modules/rank/scenes/leaderboard.js:16"
],
"context": "<button class=\"btn btn-secondary\" data-game=\"ludo\" style=\"font-size:12px;min-hei"
},
{
"emoji": "⭐",
"locations": [
"./public/js/modules/chess/scenes/review.js:20",
"./public/js/modules/chess/scenes/review.js:136"
],
"context": "<div style=\"text-align:center;font-size:15px;font-weight:700;color:#f8fafc;margi"
},
{
"emoji": "🎁",
"locations": [
"./public/js/modules/rewards/scenes/daily.js:10"
],
"context": "<div style=\"font-size:48px;animation:float 3s ease-in-out infinite;\">🎁</div>"
},
{
"emoji": "🎨",
"locations": [
"./admin/branding.php:85",
"./admin/branding.php:94",
"./public/js/modules/shop/scenes/browse.js:40"
],
"context": "🎨"
},
{
"emoji": "🎮",
"locations": [
"./admin/branding.php:129",
"./public/js/modules/play/scenes/table.js:226"
],
"context": "<div class=\"menu-btn-icon\">🎮</div>"
},
{
"emoji": "🎲",
"locations": [
"./public/js/modules/ludo/scenes/game.js:34",
"./public/js/modules/play/scenes/table.js:12"
],
"context": "{ key: 'ludo', name: 'لودو', nameEn: 'Ludo', color: '#8B5CF6', secondary: '#EC48"
},
{
"emoji": "🏁",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:16"
],
"context": "{ key: 'endgame', name: 'نهايات', nameEn: 'Endgame', icon: '🏁' },"
},
{
"emoji": "🏆",
"locations": [
"./public/js/modules/domino/scenes/result.js:9",
"./public/js/modules/ludo/scenes/result.js:12",
"./public/js/modules/chess/scenes/result.js:18"
],
"context": "win: { icon: '🏆', title: t('game.you_win'), color: '#34D399' },"
},
{
"emoji": "🏛️",
"locations": [
"./public/js/modules/org/scenes/home.js:38",
"./public/js/modules/org/scenes/browser.js:35"
],
"context": "${org.logo_url ? `<img src=\"${org.logo_url}\" style=\"width:100%;height:100%;objec"
},
{
"emoji": "🏰",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:11"
],
"context": "{ key: 'back-rank', name: 'الصف الأخير', nameEn: 'Back Rank', icon: '🏰' },"
},
{
"emoji": "👁️",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:12"
],
"context": "{ key: 'discovery', name: 'كشف', nameEn: 'Discovered Attack', icon: '👁️' },"
},
{
"emoji": "👑",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:17"
],
"context": "{ key: 'promotion', name: 'ترقية', nameEn: 'Promotion', icon: '👑' },"
},
{
"emoji": "👤",
"locations": [
"./public/js/modules/org/scenes/home.js:59",
"./public/js/modules/social/scenes/friends.js:43",
"./public/js/modules/profile/scenes/view.js:21"
],
"context": "${player.avatar_url ? `<img src=\"${player.avatar_url}\" style=\"width:100%;height:"
},
{
"emoji": "👥",
"locations": [
"./public/js/modules/org/scenes/browser.js:39",
"./public/js/modules/social/scenes/friends.js:32",
"./public/js/modules/rank/scenes/tournaments.js:50"
],
"context": "<span style=\"font-size:11px;color:#94a3b8;\">👥 ${tour.player_count || 0}/${tour.m"
},
{
"emoji": "💀",
"locations": [
"./public/js/modules/domino/scenes/result.js:9",
"./public/js/modules/ludo/scenes/result.js:12",
"./public/js/modules/chess/scenes/result.js:19"
],
"context": "loss: { icon: '💀', title: t('game.you_lose'), color: '#F87171' },"
},
{
"emoji": "💎",
"locations": [
"./public/js/modules/shop/scenes/browse.js:46"
],
"context": "${item.price_gems ? `${item.price_gems} 💎` : ''}"
},
{
"emoji": "💥",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:7"
],
"context": "{ key: 'sacrifice', name: 'تضحية', nameEn: 'Sacrifice', icon: '💥' },"
},
{
"emoji": "💰",
"locations": [
"./public/js/modules/rank/scenes/tournaments.js:52"
],
"context": "${tour.entry_fee_coins ? `<span style=\"font-size:11px;color:#F87171;\">💰 ${tour.e"
},
{
"emoji": "💾",
"locations": [
"./admin/branding.php:235"
],
"context": "<button type=\"submit\">💾 حفظ كل التغييرات</button>"
},
{
"emoji": "📊",
"locations": [
"./public/js/modules/chess/scenes/review.js:189",
"./public/js/modules/chess/scenes/analysis.js:25",
"./public/js/modules/chess/scenes/result.js:74"
],
"context": "<button class=\"btn btn-secondary w-full\" id=\"btn-analyze\" style=\"font-size:13px;"
},
{
"emoji": "📋",
"locations": [
"./public/js/modules/chess/scenes/result.js:77",
"./public/js/modules/chess/scenes/result.js:155",
"./public/js/modules/play/scenes/table.js:243"
],
"context": "<div class=\"feature-chip\" id=\"btn-history\">📋 مبارياتي</div>"
},
{
"emoji": "📌",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:9"
],
"context": "{ key: 'pin', name: 'تثبيت', nameEn: 'Pin', icon: '📌' },"
},
{
"emoji": "📏",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:6"
],
"context": "{ key: 'long', name: 'طويلة', nameEn: 'Long', icon: '📏' },"
},
{
"emoji": "📖",
"locations": [
"./public/js/modules/chess/scenes/review.js:161",
"./public/js/modules/chess/scenes/review.js:180",
"./public/js/modules/chess/scenes/analysis.js:183"
],
"context": "<div style=\"font-size:9px;color:#64748b;margin-bottom:2px;\">📖 كتاب الافتتاحات</d"
},
{
"emoji": "📤",
"locations": [
"./admin/branding.php:289",
"./public/js/modules/social/scenes/friends.js:35",
"./public/js/modules/chess/scenes/result.js:76"
],
"context": "<button class=\"btn btn-secondary\" id=\"btn-share\" style=\"flex:1;font-size:12px;\">"
},
{
"emoji": "📦",
"locations": [
"./admin/branding.php:239"
],
"context": "<h2>📦 الأصول البصرية</h2>"
},
{
"emoji": "🔇",
"locations": [
"./public/js/modules/profile/scenes/settings.js:20"
],
"context": "<button class=\"btn btn-secondary\" id=\"toggle-audio\" style=\"min-height:36px;paddi"
},
{
"emoji": "🔊",
"locations": [
"./public/js/modules/profile/scenes/settings.js:20"
],
"context": "<button class=\"btn btn-secondary\" id=\"toggle-audio\" style=\"min-height:36px;paddi"
},
{
"emoji": "🔘",
"locations": [
"./admin/branding.php:210"
],
"context": "<h2>🔘 أشكال الأزرار</h2>"
},
{
"emoji": "🔱",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:8"
],
"context": "{ key: 'fork', name: 'شوكة', nameEn: 'Fork', icon: '🔱' },"
},
{
"emoji": "🗡️",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:10"
],
"context": "{ key: 'skewer', name: 'سيخ', nameEn: 'Skewer', icon: '🗡️' },"
},
{
"emoji": "🛡️",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:15"
],
"context": "{ key: 'defense', name: 'دفاع', nameEn: 'Defense', icon: '🛡️' },"
},
{
"emoji": "🤖",
"locations": [
"./public/js/modules/chess/scenes/game.js:40"
],
"context": "<img src=\"https://stockfishapi.caprover.al-arcade.com/portraits/${botId || 'amin"
},
{
"emoji": "🤝",
"locations": [
"./public/js/modules/chess/scenes/result.js:20"
],
"context": "draw: { icon: '🤝', title: t('game.draw_result'), color: '#E4AC38' }"
},
{
"emoji": "🥇",
"locations": [
"./public/js/modules/rank/scenes/leaderboard.js:64"
],
"context": "const medals = ['🥇', '🥈', '🥉'];"
},
{
"emoji": "🥈",
"locations": [
"./public/js/modules/rank/scenes/leaderboard.js:64"
],
"context": "const medals = ['🥇', '🥈', '🥉'];"
},
{
"emoji": "🥉",
"locations": [
"./public/js/modules/rank/scenes/leaderboard.js:64"
],
"context": "const medals = ['🥇', '🥈', '🥉'];"
},
{
"emoji": "🧩",
"locations": [
"./public/js/modules/play/scenes/table.js:244"
],
"context": "${game.key === 'chess' ? '<div class=\"feature-chip\" id=\"btn-puzzles\">🧩 أحجيات</d"
},
{
"emoji": "🪙",
"locations": [
"./public/js/modules/rewards/scenes/daily.js:31",
"./public/js/modules/shop/scenes/browse.js:45",
"./public/js/modules/shop/scenes/browse.js:70"
],
"context": "<div style=\"font-size:14px;color:var(--gold);margin-bottom:var(--s-4);\">${item.p"
},
{
"emoji": "🪤",
"locations": [
"./public/js/modules/puzzles/logic/themes.js:18"
],
"context": "{ key: 'trapped', name: 'محاصرة', nameEn: 'Trapped Piece', icon: '🪤' },"
}
],
"colors": {
"#000": [
"./admin/branding.php",
"./public/js/modules/chess/canvas/board.js"
],
"#00ffff": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#050810": [
"./index.php",
"./admin/branding.php"
],
"#065f46": [
"./public/js/modules/play/scenes/table.js"
],
"#06b6d4": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#0a0a18": [
"./public/js/modules/chess/scenes/analysis.js",
"./public/js/modules/chess/scenes/analysis.js"
],
"#0a0a1a": [
"./admin/branding.php",
"./admin/branding.php"
],
"#0a1020": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#0a2a0e": [
"./public/js/modules/domino/scenes/game.js",
"./public/js/modules/domino/scenes/game.js"
],
"#0d3311": [
"./public/js/modules/domino/scenes/game.js"
],
"#0f0f1e": [
"./public/js/modules/chess/scenes/game.js",
"./public/js/modules/chess/scenes/game.js"
],
"#10b981": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#111": [
"./public/js/modules/domino/scenes/game.js",
"./public/js/modules/domino/scenes/game.js"
],
"#121a2e": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#1a1a1a": [
"./admin/branding.php",
"./admin/branding.php"
],
"#1a1a2e": [
"./admin/branding.php",
"./admin/branding.php"
],
"#1a2440": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#1b5e20": [
"./public/js/modules/domino/scenes/game.js",
"./public/js/modules/domino/scenes/game.js"
],
"#1e1e3a": [
"./public/js/modules/chess/scenes/game.js",
"./public/js/modules/chess/scenes/game.js"
],
"#1e293b": [
"./public/js/modules/chess/scenes/analysis.js",
"./public/js/modules/chess/logic/explorer.js"
],
"#1e40af": [
"./public/js/modules/play/scenes/table.js"
],
"#1e88e5": [
"./public/js/modules/ludo/scenes/game.js"
],
"#2082f0": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#223050": [
"./public/css/tokens.css"
],
"#2563eb": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#2a1a40": [
"./public/css/core.css"
],
"#2a2a4a": [
"./admin/branding.php",
"./admin/branding.php"
],
"#2a2a5a": [
"./public/js/modules/chess/scenes/game.js",
"./public/js/modules/chess/scenes/game.js"
],
"#2e7d32": [
"./public/js/modules/domino/scenes/game.js"
],
"#333": [
"./admin/branding.php",
"./admin/branding.php"
],
"#34d399": [
"./admin/branding.php",
"./admin/branding.php"
],
"#34d39944": [
"./public/js/modules/chess/scenes/result.js"
],
"#388e3c": [
"./public/js/modules/domino/scenes/game.js",
"./public/js/modules/domino/scenes/game.js"
],
"#3a3a7a": [
"./public/js/modules/chess/scenes/game.js"
],
"#3b82f6": [
"./public/js/core/juice.js",
"./public/js/modules/ludo/logic/rules.js"
],
"#43a047": [
"./public/js/modules/ludo/scenes/game.js"
],
"#475569": [
"./public/css/tokens.css",
"./public/js/modules/chess/scenes/game.js"
],
"#4caf50": [
"./public/js/modules/domino/scenes/game.js",
"./public/js/modules/domino/scenes/game.js"
],
"#555": [
"./public/js/modules/domino/scenes/game.js"
],
"#5b21b6": [
"./public/js/modules/play/scenes/table.js"
],
"#64748b": [
"./admin/branding.php",
"./admin/branding.php"
],
"#6834be": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#81c784": [
"./public/js/modules/domino/scenes/game.js",
"./public/js/modules/domino/scenes/game.js"
],
"#8b5cf6": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#90caf9": [
"./public/js/modules/ludo/scenes/game.js"
],
"#92400e": [
"./public/js/modules/play/scenes/table.js"
],
"#94a3b8": [
"./admin/branding.php",
"./admin/branding.php"
],
"#999": [
"./public/js/modules/domino/scenes/game.js",
"./public/js/modules/domino/scenes/game.js"
],
"#a5d6a7": [
"./public/js/modules/ludo/scenes/game.js"
],
"#aaa23a": [
"./admin/branding.php",
"./public/js/modules/chess/canvas/board.js"
],
"#b58863": [
"./admin/branding.php",
"./public/js/modules/chess/canvas/board.js"
],
"#cdd16a": [
"./admin/branding.php",
"./public/js/modules/chess/canvas/board.js"
],
"#e0e0e0": [
"./public/js/modules/ludo/scenes/game.js",
"./public/js/modules/ludo/scenes/game.js"
],
"#e2e8f0": [
"./public/js/modules/chess/scenes/game.js"
],
"#e4ac38": [
"./admin/branding.php",
"./admin/branding.php"
],
"#e53935": [
"./public/js/modules/ludo/scenes/game.js"
],
"#e84d1e": [
"./public/css/tokens.css",
"./public/js/modules/rank/scenes/tournaments.js"
],
"#ec4899": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#ef4444": [
"./admin/branding.php",
"./admin/branding.php"
],
"#ef444444": [
"./public/js/modules/chess/scenes/result.js"
],
"#ef9a9a": [
"./public/js/modules/ludo/scenes/game.js"
],
"#f0d9b5": [
"./admin/branding.php",
"./public/js/modules/chess/canvas/board.js"
],
"#f59e0b": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#f5b731": [
"./admin/branding.php",
"./public/css/tokens.css"
],
"#f87171": [
"./public/css/tokens.css",
"./public/js/modules/chess/scenes/game.js"
],
"#f8fafc": [
"./admin/branding.php",
"./admin/branding.php"
],
"#f97316": [
"./public/js/modules/chess/scenes/game.js",
"./public/js/modules/chess/scenes/review.js"
],
"#fafafa": [
"./public/js/modules/ludo/scenes/game.js"
],
"#fbbf24": [
"./public/js/modules/chess/scenes/game.js",
"./public/js/modules/chess/scenes/review.js"
],
"#fdd835": [
"./public/js/modules/ludo/scenes/game.js"
],
"#ff6b6b": [
"./admin/branding.php"
],
"#ff6b6b88": [
"./public/js/modules/chess/canvas/board.js"
],
"#ffc107": [
"./public/js/core/juice.js"
],
"#ffcc66": [
"./admin/branding.php",
"./admin/branding.php"
],
"#ffd700": [
"./public/js/core/juice.js"
],
"#ffe082": [
"./public/js/core/juice.js"
],
"#fff": [
"./admin/branding.php",
"./admin/branding.php"
],
"#fff59d": [
"./public/js/modules/ludo/scenes/game.js"
],
"#fffff0": [
"./public/js/modules/domino/scenes/game.js"
],
"#ffffff": [
"./public/js/modules/ludo/scenes/game.js"
]
},
"gradients": [
"linear-gradient(135deg, #065f46, #10b981)",
"linear-gradient(135deg, #1a2440, #2a1a40)",
"linear-gradient(135deg, #1e40af, #3b82f6)",
"linear-gradient(135deg, #2563eb, #3b82f6)",
"linear-gradient(135deg, #5b21b6, #8b5cf6)",
"linear-gradient(135deg, #92400e, #f59e0b)",
"linear-gradient(135deg, #E4AC38, #FFCC66)",
"linear-gradient(135deg, var(--game-primary, var(--blue)",
"linear-gradient(135deg, var(--gold)",
"linear-gradient(135deg,#E4AC38,#FFCC66)",
"linear-gradient(90deg, var(--bg-card)",
"linear-gradient(90deg,#2563EB,#3B82F6)",
"radial-gradient(ellipse at 30% 20%, rgba(228,172,56,0.03)",
"radial-gradient(ellipse at 70% 80%, rgba(32,130,240,0.02)"
],
"animations": [
"bgGradientMove 25s ease infinite",
"bounceIn 0.45s cubic-bezier(0.34, 1.56, 0.64, 1)",
"breatheGlow 2.5s ease-in-out infinite",
"clockPulse 1s infinite",
"float 2s ease-in-out infinite",
"float 3s ease-in-out infinite",
"glow 2s ease-in-out infinite",
"loadBar 2s ease-out forwards",
"none",
"numberPop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)",
"popIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1)",
"pulse 1s ease-in-out infinite",
"pulse 2s ease-in-out infinite",
"radarPing 2s ease-out infinite",
"sceneIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1)",
"sceneOut 0.15s ease-in forwards",
"shake 0.3s ease",
"shimmer 1.5s infinite",
"slamIn 0.45s cubic-bezier(0.34, 1.56, 0.64, 1)",
"slideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1)",
"slideUpBounce 0.4s cubic-bezier(0.16, 1, 0.3, 1)",
"toastIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1)",
"toastOut 0.2s ease-in forwards",
"wobble 0.5s ease"
],
"css_variables": [
"bg-base",
"bg-card",
"bg-deep",
"bg-elevated",
"blue",
"border",
"border-hover",
"btn-min-height",
"btn-press-scale",
"btn-shadow",
"btn-weight",
"card-border-width",
"cyan",
"domino-primary",
"dur-fast",
"dur-normal",
"ease-out",
"ease-spring",
"error",
"font-ar",
"font-lat",
"game-gradient",
"game-primary",
"game-secondary",
"gold",
"gold-soft",
"hud-height",
"loss",
"ludo-primary",
"orange",
"purple",
"r-btn-secondary",
"r-full",
"r-input",
"r-lg",
"r-md",
"r-sm",
"r-xl",
"s-1",
"s-2",
"s-3",
"s-4",
"s-5",
"s-6",
"s-8",
"safe-bottom",
"safe-top",
"shadow-lg",
"shadow-md",
"shadow-sm",
"success",
"tab-height",
"text-muted",
"text-primary",
"text-secondary",
"win"
],
"totals": {
"emojis": 67,
"colors": 80,
"gradients": 14,
"animations": 24,
"css_variables": 56
}
}
\ No newline at end of file
...@@ -235,6 +235,93 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) { ...@@ -235,6 +235,93 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['asset'])) {
<button type="submit">💾 حفظ كل التغييرات</button> <button type="submit">💾 حفظ كل التغييرات</button>
</form> </form>
<!-- JUICE SETTINGS -->
<h2>🧃 إعدادات الـ Juice (التأثيرات)</h2>
<div class="section">
<div class="grid">
<?php
$juiceSettings = [
['key' => 'juice_particles', 'label' => 'تفعيل الجسيمات (Particles)', 'default' => '1', 'hint' => '1=مفعل, 0=معطل', 'type' => 'number'],
['key' => 'juice_shake_intensity', 'label' => 'شدة الاهتزاز', 'default' => '4', 'hint' => '0=بلا, 2=خفيف, 4=عادي, 8=قوي', 'type' => 'number'],
['key' => 'juice_haptic', 'label' => 'تفعيل الاهتزاز (Haptic)', 'default' => '1', 'hint' => '1=مفعل, 0=معطل', 'type' => 'number'],
['key' => 'juice_confetti_count', 'label' => 'عدد جسيمات الاحتفال', 'default' => '30', 'hint' => 'عند الفوز — 0 لإلغاء', 'type' => 'number'],
['key' => 'juice_coin_fly_count', 'label' => 'عدد العملات الطائرة', 'default' => '5', 'hint' => 'عملات تطير للـ HUD', 'type' => 'number'],
['key' => 'juice_bounce_scale', 'label' => 'حجم الارتداد (Bounce)', 'default' => '1.08', 'hint' => '1.0=بلا, 1.05=خفيف, 1.15=مبالغ', 'type' => 'text'],
['key' => 'juice_slam_scale', 'label' => 'حجم الاصطدام (Slam)', 'default' => '1.5', 'hint' => 'عند ظهور النتائج', 'type' => 'text'],
['key' => 'juice_float_amount', 'label' => 'مسافة الطفو (px)', 'default' => '5', 'hint' => 'حركة المربعات في الصفحة الرئيسية', 'type' => 'number'],
];
foreach ($juiceSettings as $j):
$val = $theme[$j['key']] ?? $j['default'];
?>
<div class="field">
<label><?= $j['label'] ?></label>
<input type="<?= $j['type'] ?>" name="theme[<?= $j['key'] ?>]" value="<?= $val ?>">
<div class="hint"><?= $j['hint'] ?></div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- EMOJI REPLACEMENTS -->
<h2>😀 استبدال الرموز التعبيرية</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">استبدل أي رمز تعبيري بصورة SVG أو PNG — تظهر بنفس الحجم بلا تشويه</p>
<div class="section">
<div class="grid">
<?php
$emojiSlots = [
['key' => 'trophy', 'emoji' => '🏆', 'label' => 'كأس الفوز', 'size' => 72, 'hint' => 'شاشة نتيجة الفوز'],
['key' => 'skull', 'emoji' => '💀', 'label' => 'أيقونة الخسارة', 'size' => 72, 'hint' => 'شاشة نتيجة الخسارة'],
['key' => 'handshake', 'emoji' => '🤝', 'label' => 'أيقونة التعادل', 'size' => 72, 'hint' => 'شاشة نتيجة التعادل'],
['key' => 'gift', 'emoji' => '🎁', 'label' => 'صندوق المكافأة', 'size' => 48, 'hint' => 'شاشة المكافأة اليومية'],
['key' => 'robot', 'emoji' => '🤖', 'label' => 'أيقونة البوت', 'size' => 32, 'hint' => 'بجانب اسم الخصم الآلي'],
['key' => 'star', 'emoji' => '⭐', 'label' => 'نجمة التقييم', 'size' => 20, 'hint' => 'شاشة المراجعة'],
['key' => 'dice', 'emoji' => '🎲', 'label' => 'أيقونة النرد', 'size' => 48, 'hint' => 'مربع اللودو + زر الرمي'],
['key' => 'gamepad', 'emoji' => '🎮', 'label' => 'لاعب واحد', 'size' => 24, 'hint' => 'قائمة اللعبة — لاعب واحد'],
['key' => 'swords', 'emoji' => '⚔️', 'label' => 'متعدد اللاعبين', 'size' => 24, 'hint' => 'قائمة اللعبة — أونلاين'],
['key' => 'medal_gold', 'emoji' => '🥇', 'label' => 'ميدالية ذهبية', 'size' => 20, 'hint' => 'المركز الأول في الترتيب'],
['key' => 'medal_silver', 'emoji' => '🥈', 'label' => 'ميدالية فضية', 'size' => 20, 'hint' => 'المركز الثاني'],
['key' => 'medal_bronze', 'emoji' => '🥉', 'label' => 'ميدالية برونزية', 'size' => 20, 'hint' => 'المركز الثالث'],
['key' => 'coin', 'emoji' => '🪙', 'label' => 'عملة', 'size' => 16, 'hint' => 'بجانب أسعار المتجر'],
['key' => 'gem', 'emoji' => '💎', 'label' => 'جوهرة', 'size' => 16, 'hint' => 'بجانب أسعار الجواهر'],
['key' => 'person', 'emoji' => '👤', 'label' => 'صورة لاعب افتراضية', 'size' => 32, 'hint' => 'الأفاتار الافتراضي'],
['key' => 'people', 'emoji' => '👥', 'label' => 'أيقونة الأصدقاء', 'size' => 32, 'hint' => 'شاشة الأصدقاء الفارغة'],
['key' => 'bell', 'emoji' => '🔔', 'label' => 'جرس الإشعارات', 'size' => 18, 'hint' => 'أيقونة الإشعارات في الهيدر'],
['key' => 'speaker_on', 'emoji' => '🔊', 'label' => 'صوت مفعل', 'size' => 20, 'hint' => 'إعدادات الصوت'],
['key' => 'speaker_off', 'emoji' => '🔇', 'label' => 'صوت معطل', 'size' => 20, 'hint' => 'إعدادات الصوت'],
['key' => 'art', 'emoji' => '🎨', 'label' => 'أيقونة المتجر', 'size' => 24, 'hint' => 'بطاقات المتجر'],
['key' => 'puzzle', 'emoji' => '🧩', 'label' => 'أحجيات', 'size' => 16, 'hint' => 'زر الأحجيات في القائمة'],
['key' => 'chart', 'emoji' => '📊', 'label' => 'تحليل', 'size' => 16, 'hint' => 'زر التحليل'],
['key' => 'clipboard', 'emoji' => '📋', 'label' => 'مبارياتي', 'size' => 16, 'hint' => 'زر المباريات'],
['key' => 'share', 'emoji' => '📤', 'label' => 'مشاركة', 'size' => 16, 'hint' => 'زر المشاركة'],
['key' => 'checkmark', 'emoji' => '✅', 'label' => 'علامة صح', 'size' => 20, 'hint' => 'نجاح العملية'],
['key' => 'cross', 'emoji' => '❌', 'label' => 'علامة خطأ', 'size' => 20, 'hint' => 'فشل العملية'],
['key' => 'flag', 'emoji' => '⚐', 'label' => 'استسلام', 'size' => 16, 'hint' => 'زر الاستسلام في الشطرنج'],
['key' => 'book', 'emoji' => '📖', 'label' => 'كتاب/نظرية', 'size' => 14, 'hint' => 'تصنيف نقلة نظرية'],
];
foreach ($emojiSlots as $e):
$current = $theme['assets']['emoji_' . $e['key']] ?? null;
?>
<div class="field">
<form method="POST" enctype="multipart/form-data">
<input type="hidden" name="slot" value="emoji_<?= $e['key'] ?>">
<input type="hidden" name="expected_w" value="<?= $e['size'] ?>">
<input type="hidden" name="expected_h" value="<?= $e['size'] ?>">
<label><?= $e['emoji'] ?> <?= $e['label'] ?></label>
<div class="upload-box" onclick="this.querySelector('input[type=file]').click()">
<input type="file" name="asset" accept=".svg,.png,.jpg,.webp" style="display:none" onchange="this.form.submit()">
<?php if ($current): ?>
<div class="current"><img src="<?= $current ?>" style="width:<?= min($e['size'], 48) ?>px;height:<?= min($e['size'], 48) ?>px;object-fit:contain;"></div>
<?php else: ?>
<span style="font-size:24px;"><?= $e['emoji'] ?></span> → ارفع بديل
<?php endif; ?>
<div class="size-hint"><?= $e['size'] ?>×<?= $e['size'] ?>px — <?= $e['hint'] ?></div>
</div>
</form>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- ASSET UPLOADS --> <!-- ASSET UPLOADS -->
<h2>📦 الأصول البصرية</h2> <h2>📦 الأصول البصرية</h2>
<p style="color:#64748b;font-size:12px;margin-bottom:16px;">ارفع صور SVG أو PNG — ستظهر فوراً كافتراضي لكل اللاعبين</p> <p style="color:#64748b;font-size:12px;margin-bottom:16px;">ارفع صور SVG أو PNG — ستظهر فوراً كافتراضي لكل اللاعبين</p>
......
...@@ -78,7 +78,15 @@ function applyAnimations() { ...@@ -78,7 +78,15 @@ function applyAnimations() {
export function assetImg(slot, fallbackEmoji, width, height) { export function assetImg(slot, fallbackEmoji, width, height) {
const url = getAsset(slot); const url = getAsset(slot);
if (url) { if (url) {
return `<img src="${url}" alt="" style="width:${width}px;height:${height}px;object-fit:contain;image-rendering:auto;" draggable="false">`; return `<img src="${url}" alt="" style="width:${width}px;height:${height}px;object-fit:contain;image-rendering:-webkit-optimize-contrast;" draggable="false">`;
} }
return `<span style="font-size:${Math.min(width, height) * 0.7}px;line-height:1;">${fallbackEmoji}</span>`; return `<span style="font-size:${Math.min(width, height) * 0.7}px;line-height:1;">${fallbackEmoji}</span>`;
} }
export function emoji(key, fallback, size = 20) {
const url = getAsset('emoji_' + key);
if (url) {
return `<img src="${url}" alt="" style="width:${size}px;height:${size}px;object-fit:contain;vertical-align:middle;image-rendering:-webkit-optimize-contrast;" draggable="false">`;
}
return fallback;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment