752 lines
22 KiB
HTML
752 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Stock Assets Viewer</title>
|
|
<style>
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: system-ui, -apple-system, sans-serif;
|
|
background: #1a1a2e;
|
|
color: #eee;
|
|
min-height: 100vh;
|
|
margin: 0;
|
|
padding: 2rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 2rem;
|
|
}
|
|
|
|
h1 {
|
|
margin: 0;
|
|
font-weight: 400;
|
|
}
|
|
|
|
h2 {
|
|
margin: 0;
|
|
font-size: 1rem;
|
|
font-weight: 400;
|
|
color: #aaa;
|
|
}
|
|
|
|
/* Tab navigation */
|
|
.tabs {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.tab-btn {
|
|
padding: 0.75rem 1.5rem;
|
|
background: #2a2a4e;
|
|
border: 2px solid #333;
|
|
border-radius: 8px 8px 0 0;
|
|
color: #eee;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
transition: background 0.15s, border-color 0.15s;
|
|
}
|
|
|
|
.tab-btn:hover {
|
|
background: #3a3a5e;
|
|
}
|
|
|
|
.tab-btn:focus {
|
|
outline: 2px solid #fff;
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.tab-btn.active {
|
|
background: #3a3a5e;
|
|
border-color: #4ECDC4;
|
|
border-bottom-color: #3a3a5e;
|
|
}
|
|
|
|
.tab-content {
|
|
display: none;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 2rem;
|
|
width: 100%;
|
|
max-width: 600px;
|
|
}
|
|
|
|
.tab-content.active {
|
|
display: flex;
|
|
}
|
|
|
|
/* Avatar section */
|
|
.avatar-container {
|
|
position: relative;
|
|
width: 192px;
|
|
height: 192px;
|
|
}
|
|
|
|
.avatar-layer {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.avatar-layer svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.controls {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.5rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.control-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.color-picker-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
}
|
|
|
|
label {
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
input[type="color"] {
|
|
width: 60px;
|
|
height: 40px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.presets {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
max-width: 400px;
|
|
}
|
|
|
|
.preset-btn {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 2px solid #333;
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
transition: transform 0.15s, border-color 0.15s;
|
|
}
|
|
|
|
.preset-btn:hover {
|
|
transform: scale(1.1);
|
|
border-color: #fff;
|
|
}
|
|
|
|
.preset-btn:focus {
|
|
outline: 2px solid #fff;
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.emotions {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
max-width: 500px;
|
|
}
|
|
|
|
.emotion-btn {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
padding: 0.5rem 0.75rem;
|
|
background: #2a2a4e;
|
|
border: 2px solid #333;
|
|
border-radius: 8px;
|
|
color: #eee;
|
|
cursor: pointer;
|
|
transition: transform 0.15s, border-color 0.15s, background 0.15s;
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.emotion-btn:hover {
|
|
transform: scale(1.05);
|
|
border-color: #666;
|
|
background: #3a3a5e;
|
|
}
|
|
|
|
.emotion-btn:focus {
|
|
outline: 2px solid #fff;
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.emotion-btn.active {
|
|
border-color: #4ECDC4;
|
|
background: #3a3a5e;
|
|
}
|
|
|
|
.emotion-btn .hotkey {
|
|
font-size: 0.65rem;
|
|
color: #888;
|
|
background: #1a1a2e;
|
|
padding: 0.1rem 0.3rem;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.hint {
|
|
font-size: 0.75rem;
|
|
color: #666;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
/* Props section */
|
|
.props-grid {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1.5rem;
|
|
justify-content: center;
|
|
max-width: 600px;
|
|
}
|
|
|
|
.prop-category {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
align-items: center;
|
|
width: 100%;
|
|
}
|
|
|
|
.prop-category h3 {
|
|
margin: 0;
|
|
font-weight: 500;
|
|
color: #4ECDC4;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.prop-items {
|
|
display: flex;
|
|
gap: 1rem;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
}
|
|
|
|
.prop-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 1rem;
|
|
background: #2a2a4e;
|
|
border: 2px solid #333;
|
|
border-radius: 12px;
|
|
transition: transform 0.15s, border-color 0.15s;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.prop-card:hover {
|
|
transform: scale(1.05);
|
|
border-color: #4ECDC4;
|
|
}
|
|
|
|
.prop-card:focus {
|
|
outline: 2px solid #fff;
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.prop-card.selected {
|
|
border-color: #4ECDC4;
|
|
background: #3a3a5e;
|
|
}
|
|
|
|
.prop-preview {
|
|
width: 64px;
|
|
height: 64px;
|
|
}
|
|
|
|
.prop-preview svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.prop-name {
|
|
font-size: 0.75rem;
|
|
color: #aaa;
|
|
text-transform: capitalize;
|
|
}
|
|
|
|
/* Selected prop display */
|
|
.selected-prop-container {
|
|
width: 192px;
|
|
height: 192px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #2a2a4e;
|
|
border-radius: 12px;
|
|
border: 2px solid #333;
|
|
}
|
|
|
|
.selected-prop-container svg {
|
|
width: 160px;
|
|
height: 160px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Stock Assets Viewer</h1>
|
|
|
|
<nav class="tabs" role="tablist">
|
|
<button class="tab-btn active" data-tab="avatars" role="tab" aria-selected="true">Avatars</button>
|
|
<button class="tab-btn" data-tab="props" role="tab" aria-selected="false">Props</button>
|
|
<button class="tab-btn" data-tab="flags" role="tab" aria-selected="false">Flags</button>
|
|
</nav>
|
|
|
|
<!-- Avatars Tab -->
|
|
<section id="avatars-tab" class="tab-content active" role="tabpanel" aria-labelledby="avatars-tab-btn">
|
|
<div class="avatar-container">
|
|
<div id="face-layer" class="avatar-layer"></div>
|
|
<div id="expression-layer" class="avatar-layer"></div>
|
|
</div>
|
|
|
|
<div class="controls">
|
|
<div class="control-section">
|
|
<h2>Face Color</h2>
|
|
<div class="color-picker-row">
|
|
<input type="color" id="colorPicker" value="#FFCC00" aria-label="Face color picker">
|
|
</div>
|
|
<div class="presets" role="group" aria-label="Color presets">
|
|
<button class="preset-btn" style="background: #FFCC00;" data-color="#FFCC00" title="Yellow" aria-label="Yellow"></button>
|
|
<button class="preset-btn" style="background: #FF6B6B;" data-color="#FF6B6B" title="Red" aria-label="Red"></button>
|
|
<button class="preset-btn" style="background: #4ECDC4;" data-color="#4ECDC4" title="Teal" aria-label="Teal"></button>
|
|
<button class="preset-btn" style="background: #95E1D3;" data-color="#95E1D3" title="Mint" aria-label="Mint"></button>
|
|
<button class="preset-btn" style="background: #DDA0DD;" data-color="#DDA0DD" title="Plum" aria-label="Plum"></button>
|
|
<button class="preset-btn" style="background: #87CEEB;" data-color="#87CEEB" title="Sky Blue" aria-label="Sky Blue"></button>
|
|
<button class="preset-btn" style="background: #FFB347;" data-color="#FFB347" title="Orange" aria-label="Orange"></button>
|
|
<button class="preset-btn" style="background: #98D8C8;" data-color="#98D8C8" title="Seafoam" aria-label="Seafoam"></button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="control-section">
|
|
<h2>Emotion</h2>
|
|
<div class="emotions" role="group" aria-label="Emotion selection">
|
|
<button class="emotion-btn" data-emotion="neutral" data-key="0">
|
|
<span>Neutral</span>
|
|
<span class="hotkey">0</span>
|
|
</button>
|
|
<button class="emotion-btn active" data-emotion="smile" data-key="1">
|
|
<span>Happy</span>
|
|
<span class="hotkey">1</span>
|
|
</button>
|
|
<button class="emotion-btn" data-emotion="sad" data-key="2">
|
|
<span>Sad</span>
|
|
<span class="hotkey">2</span>
|
|
</button>
|
|
<button class="emotion-btn" data-emotion="angry" data-key="3">
|
|
<span>Angry</span>
|
|
<span class="hotkey">3</span>
|
|
</button>
|
|
<button class="emotion-btn" data-emotion="surprised" data-key="4">
|
|
<span>Surprised</span>
|
|
<span class="hotkey">4</span>
|
|
</button>
|
|
<button class="emotion-btn" data-emotion="thinking" data-key="5">
|
|
<span>Thinking</span>
|
|
<span class="hotkey">5</span>
|
|
</button>
|
|
<button class="emotion-btn" data-emotion="laughing" data-key="6">
|
|
<span>Laughing</span>
|
|
<span class="hotkey">6</span>
|
|
</button>
|
|
<button class="emotion-btn" data-emotion="wink" data-key="7">
|
|
<span>Wink</span>
|
|
<span class="hotkey">7</span>
|
|
</button>
|
|
<button class="emotion-btn" data-emotion="sleeping" data-key="8">
|
|
<span>Sleeping</span>
|
|
<span class="hotkey">8</span>
|
|
</button>
|
|
<button class="emotion-btn" data-emotion="confused" data-key="9">
|
|
<span>Confused</span>
|
|
<span class="hotkey">9</span>
|
|
</button>
|
|
<button class="emotion-btn" data-emotion="crying" data-key="c">
|
|
<span>Crying</span>
|
|
<span class="hotkey">C</span>
|
|
</button>
|
|
<button class="emotion-btn" data-emotion="love" data-key="l">
|
|
<span>Love</span>
|
|
<span class="hotkey">L</span>
|
|
</button>
|
|
</div>
|
|
<p class="hint">Press 0-9, C, L to switch emotions</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Props Tab -->
|
|
<section id="props-tab" class="tab-content" role="tabpanel" aria-labelledby="props-tab-btn">
|
|
<div class="selected-prop-container" id="selected-prop" aria-live="polite"></div>
|
|
|
|
<div class="props-grid">
|
|
<div class="prop-category">
|
|
<h3>Hookahs</h3>
|
|
<div class="prop-items" id="hookah-props" role="group" aria-label="Hookah props"></div>
|
|
</div>
|
|
|
|
<div class="prop-category">
|
|
<h3>Coffee</h3>
|
|
<div class="prop-items" id="coffee-props" role="group" aria-label="Coffee props"></div>
|
|
</div>
|
|
|
|
<div class="prop-category">
|
|
<h3>Sodas</h3>
|
|
<div class="prop-items" id="soda-props" role="group" aria-label="Soda props"></div>
|
|
</div>
|
|
|
|
<div class="prop-category">
|
|
<h3>Tea</h3>
|
|
<div class="prop-items" id="tea-props" role="group" aria-label="Tea props"></div>
|
|
</div>
|
|
|
|
<div class="prop-category">
|
|
<h3>Misc</h3>
|
|
<div class="prop-items" id="misc-props" role="group" aria-label="Miscellaneous props"></div>
|
|
</div>
|
|
|
|
<div class="prop-category">
|
|
<h3>Good Pol</h3>
|
|
<div class="prop-items" id="goodpol-props" role="group" aria-label="Good Pol props"></div>
|
|
</div>
|
|
|
|
<div class="prop-category">
|
|
<h3>Screens</h3>
|
|
<div class="prop-items" id="screen-props" role="group" aria-label="Screen props"></div>
|
|
</div>
|
|
|
|
<div class="prop-category">
|
|
<h3>Keyboards</h3>
|
|
<div class="prop-items" id="keyboard-props" role="group" aria-label="Keyboard props"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Flags Tab -->
|
|
<section id="flags-tab" class="tab-content" role="tabpanel" aria-labelledby="flags-tab-btn">
|
|
<div class="selected-prop-container" id="selected-flag" aria-live="polite"></div>
|
|
|
|
<div class="props-grid">
|
|
<div class="prop-category">
|
|
<h3>National Flags</h3>
|
|
<div class="prop-items" id="flag-items" role="group" aria-label="National flags"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<script>
|
|
// Tab switching
|
|
const tabBtns = document.querySelectorAll('.tab-btn');
|
|
const tabContents = document.querySelectorAll('.tab-content');
|
|
|
|
function switchToTab(tabId, updateHash = true) {
|
|
const btn = document.querySelector(`.tab-btn[data-tab="${tabId}"]`);
|
|
if (!btn) return;
|
|
|
|
tabBtns.forEach(b => {
|
|
b.classList.remove('active');
|
|
b.setAttribute('aria-selected', 'false');
|
|
});
|
|
btn.classList.add('active');
|
|
btn.setAttribute('aria-selected', 'true');
|
|
|
|
tabContents.forEach(content => {
|
|
content.classList.remove('active');
|
|
});
|
|
document.getElementById(`${tabId}-tab`).classList.add('active');
|
|
|
|
if (updateHash) {
|
|
window.location.hash = `tab=${tabId}`;
|
|
}
|
|
}
|
|
|
|
function getTabFromHash() {
|
|
const hash = window.location.hash;
|
|
if (!hash) return null;
|
|
const match = hash.match(/^#tab=(\w+)$/);
|
|
return match ? match[1] : null;
|
|
}
|
|
|
|
tabBtns.forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
switchToTab(btn.dataset.tab);
|
|
});
|
|
});
|
|
|
|
window.addEventListener('hashchange', () => {
|
|
const tabId = getTabFromHash();
|
|
if (tabId) {
|
|
switchToTab(tabId, false);
|
|
}
|
|
});
|
|
|
|
// Avatar functionality
|
|
let currentColor = '#FFCC00';
|
|
let currentEmotion = 'smile';
|
|
|
|
function lightenColor(hex, percent) {
|
|
const num = parseInt(hex.replace('#', ''), 16);
|
|
const amt = Math.round(2.55 * percent);
|
|
const R = Math.min(255, (num >> 16) + amt);
|
|
const G = Math.min(255, ((num >> 8) & 0x00FF) + amt);
|
|
const B = Math.min(255, (num & 0x0000FF) + amt);
|
|
return `#${(1 << 24 | R << 16 | G << 8 | B).toString(16).slice(1).toUpperCase()}`;
|
|
}
|
|
|
|
function darkenColor(hex, percent) {
|
|
const num = parseInt(hex.replace('#', ''), 16);
|
|
const amt = Math.round(2.55 * percent);
|
|
const R = Math.max(0, (num >> 16) - amt);
|
|
const G = Math.max(0, ((num >> 8) & 0x00FF) - amt);
|
|
const B = Math.max(0, (num & 0x0000FF) - amt);
|
|
return `#${(1 << 24 | R << 16 | G << 8 | B).toString(16).slice(1).toUpperCase()}`;
|
|
}
|
|
|
|
async function loadSVG(url, containerId) {
|
|
const response = await fetch(url);
|
|
const svgText = await response.text();
|
|
document.getElementById(containerId).innerHTML = svgText;
|
|
}
|
|
|
|
function updateFaceColor(primaryColor) {
|
|
currentColor = primaryColor;
|
|
const faceLayer = document.getElementById('face-layer');
|
|
const svg = faceLayer.querySelector('svg');
|
|
if (!svg) return;
|
|
|
|
const highlight = lightenColor(primaryColor, 25);
|
|
const shadow = darkenColor(primaryColor, 20);
|
|
|
|
svg.style.setProperty('--face-primary', primaryColor);
|
|
svg.style.setProperty('--face-highlight', highlight);
|
|
svg.style.setProperty('--face-shadow', shadow);
|
|
}
|
|
|
|
async function setEmotion(emotion) {
|
|
currentEmotion = emotion;
|
|
await loadSVG(`avatar/${emotion}.svg`, 'expression-layer');
|
|
|
|
document.querySelectorAll('.emotion-btn').forEach(btn => {
|
|
btn.classList.toggle('active', btn.dataset.emotion === emotion);
|
|
});
|
|
}
|
|
|
|
// Props functionality
|
|
const props = {
|
|
hookah: ['traditional', 'modern', 'mini', 'ornate', 'tall'],
|
|
coffee: ['espresso', 'latte', 'iced', 'frenchpress', 'pourover', 'turkish', 'cup-empty'],
|
|
soda: ['cola', 'lemonlime', 'orange', 'grape', 'rootbeer'],
|
|
tea: ['iced', 'pot', 'cup', 'cup-empty', 'bag'],
|
|
misc: ['iou', 'signed-dollar', 'thankyou', 'yousuck', 'businesscard'],
|
|
goodpol: ['cccp', 'china', 'palestine'],
|
|
screen: ['projector-screen', 'projector-screen-with-stand', 'projector-remote-control'],
|
|
keyboard: ['standard']
|
|
};
|
|
|
|
// Flags
|
|
const flags = ['chinese'];
|
|
|
|
let selectedProp = null;
|
|
let selectedFlag = null;
|
|
|
|
async function loadFlagPreview(name, container) {
|
|
const filename = `flags/${name}.svg`;
|
|
const response = await fetch(filename);
|
|
const svgText = await response.text();
|
|
|
|
const card = document.createElement('button');
|
|
card.className = 'prop-card';
|
|
card.dataset.flag = name;
|
|
card.setAttribute('aria-label', `${name} flag`);
|
|
|
|
const preview = document.createElement('div');
|
|
preview.className = 'prop-preview';
|
|
preview.innerHTML = svgText;
|
|
|
|
const label = document.createElement('span');
|
|
label.className = 'prop-name';
|
|
label.textContent = name.replace(/([A-Z])/g, ' $1').trim();
|
|
|
|
card.appendChild(preview);
|
|
card.appendChild(label);
|
|
|
|
card.addEventListener('click', () => selectFlag(name, card));
|
|
|
|
container.appendChild(card);
|
|
}
|
|
|
|
async function selectFlag(name, card) {
|
|
document.querySelectorAll('#flag-items .prop-card').forEach(c => c.classList.remove('selected'));
|
|
card.classList.add('selected');
|
|
|
|
const filename = `flags/${name}.svg`;
|
|
const response = await fetch(filename);
|
|
const svgText = await response.text();
|
|
document.getElementById('selected-flag').innerHTML = svgText;
|
|
selectedFlag = name;
|
|
}
|
|
|
|
async function loadPropPreview(category, name, container) {
|
|
const filename = `props/${category}-${name}.svg`;
|
|
const response = await fetch(filename);
|
|
const svgText = await response.text();
|
|
|
|
const card = document.createElement('button');
|
|
card.className = 'prop-card';
|
|
card.dataset.prop = `${category}-${name}`;
|
|
card.setAttribute('aria-label', `${name} ${category}`);
|
|
|
|
const preview = document.createElement('div');
|
|
preview.className = 'prop-preview';
|
|
preview.innerHTML = svgText;
|
|
|
|
const label = document.createElement('span');
|
|
label.className = 'prop-name';
|
|
label.textContent = name.replace(/([A-Z])/g, ' $1').trim();
|
|
|
|
card.appendChild(preview);
|
|
card.appendChild(label);
|
|
|
|
card.addEventListener('click', () => selectProp(category, name, card));
|
|
|
|
container.appendChild(card);
|
|
}
|
|
|
|
async function selectProp(category, name, card) {
|
|
document.querySelectorAll('.prop-card').forEach(c => c.classList.remove('selected'));
|
|
card.classList.add('selected');
|
|
|
|
const filename = `props/${category}-${name}.svg`;
|
|
const response = await fetch(filename);
|
|
const svgText = await response.text();
|
|
document.getElementById('selected-prop').innerHTML = svgText;
|
|
selectedProp = `${category}-${name}`;
|
|
}
|
|
|
|
async function initProps() {
|
|
const hookahContainer = document.getElementById('hookah-props');
|
|
const coffeeContainer = document.getElementById('coffee-props');
|
|
const sodaContainer = document.getElementById('soda-props');
|
|
const teaContainer = document.getElementById('tea-props');
|
|
const miscContainer = document.getElementById('misc-props');
|
|
const goodpolContainer = document.getElementById('goodpol-props');
|
|
const screenContainer = document.getElementById('screen-props');
|
|
const keyboardContainer = document.getElementById('keyboard-props');
|
|
|
|
for (const name of props.hookah) {
|
|
await loadPropPreview('hookah', name, hookahContainer);
|
|
}
|
|
for (const name of props.coffee) {
|
|
await loadPropPreview('coffee', name, coffeeContainer);
|
|
}
|
|
for (const name of props.soda) {
|
|
await loadPropPreview('soda', name, sodaContainer);
|
|
}
|
|
for (const name of props.tea) {
|
|
await loadPropPreview('tea', name, teaContainer);
|
|
}
|
|
for (const name of props.misc) {
|
|
await loadPropPreview('misc', name, miscContainer);
|
|
}
|
|
for (const name of props.goodpol) {
|
|
await loadPropPreview('goodpol', name, goodpolContainer);
|
|
}
|
|
for (const name of props.screen) {
|
|
await loadPropPreview('screen', name, screenContainer);
|
|
}
|
|
for (const name of props.keyboard) {
|
|
await loadPropPreview('keyboard', name, keyboardContainer);
|
|
}
|
|
|
|
// Select first prop by default
|
|
const firstCard = document.querySelector('#props-tab .prop-card');
|
|
if (firstCard) {
|
|
selectProp('hookah', 'traditional', firstCard);
|
|
}
|
|
|
|
// Load flags
|
|
const flagContainer = document.getElementById('flag-items');
|
|
for (const name of flags) {
|
|
await loadFlagPreview(name, flagContainer);
|
|
}
|
|
|
|
// Select first flag by default
|
|
const firstFlagCard = document.querySelector('#flag-items .prop-card');
|
|
if (firstFlagCard) {
|
|
selectFlag('chinese', firstFlagCard);
|
|
}
|
|
}
|
|
|
|
// Initialize
|
|
async function init() {
|
|
// Check for tab in URL hash and switch to it
|
|
const initialTab = getTabFromHash();
|
|
if (initialTab) {
|
|
switchToTab(initialTab, false);
|
|
}
|
|
|
|
await Promise.all([
|
|
loadSVG('avatar/face.svg', 'face-layer'),
|
|
loadSVG('avatar/smile.svg', 'expression-layer')
|
|
]);
|
|
|
|
updateFaceColor('#FFCC00');
|
|
|
|
const colorPicker = document.getElementById('colorPicker');
|
|
colorPicker.addEventListener('input', (e) => {
|
|
updateFaceColor(e.target.value);
|
|
});
|
|
|
|
document.querySelectorAll('.preset-btn').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const color = btn.dataset.color;
|
|
colorPicker.value = color;
|
|
updateFaceColor(color);
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll('.emotion-btn').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
setEmotion(btn.dataset.emotion);
|
|
});
|
|
});
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.target.tagName === 'INPUT') return;
|
|
|
|
const key = e.key.toLowerCase();
|
|
if ((key >= '0' && key <= '9') || key === 'c' || key === 'l') {
|
|
const btn = document.querySelector(`.emotion-btn[data-key="${key}"]`);
|
|
if (btn) {
|
|
setEmotion(btn.dataset.emotion);
|
|
}
|
|
}
|
|
});
|
|
|
|
await initProps();
|
|
}
|
|
|
|
init();
|
|
</script>
|
|
</body>
|
|
</html>
|