generated ui client

This commit is contained in:
2025-09-16 23:15:02 -04:00
parent e4e3ff75fc
commit b80f7b314f
6 changed files with 1715 additions and 0 deletions

View File

@@ -0,0 +1,174 @@
# WoW Item Scaler Web Frontend
A beautiful World of Warcraft-themed web interface for scaling items using your existing Go backend.
## Features
- **Immersive WoW-styled UI** with layered backgrounds, magical particle effects, and authentic textures
- **Modern visual effects** including animated glowing elements, parallax backgrounds, and fantasy decorations
- **Multiple input methods**:
- Single item by entry ID
- Comma-separated list of entry IDs
- Search by item name with dropdown selection
- **Visual item comparison** showing before/after scaling
- **Wowhead integration** for item images and tooltips
- **Responsive design** that works on desktop and mobile
- **Export functionality** for SQL and copy-to-clipboard
## Dependencies & Libraries Used
### External CDN Libraries
1. **Tailwind CSS** (`https://cdn.tailwindcss.com`)
- Utility-first CSS framework
- Used for responsive layouts, spacing, and utility classes
- No build process required
2. **Wowhead Power** (`https://wow.zamimg.com/widgets/power.js`)
- Official Wowhead tooltips and item integration
- Provides item images and tooltip functionality
- Authentic WoW item data
3. **Google Fonts**
- **Cinzel**: Elegant serif font for body text and UI elements
- **Uncial Antiqua**: Fantasy/medieval font for titles and headings
- Provides authentic WoW-like typography
### Custom Theme System
- **Custom CSS Variables** for consistent WoW color scheme
- **Modular component classes** (`.wow-panel`, `.wow-button`, etc.)
- **Quality-based item coloring** (epic purple, legendary orange)
- **Animated effects** and hover states
## Setup
1. Open `index.html` in a web browser
2. Update the backend URL in `js/item-scaler.js` (line 4) to match your Go backend server
3. Ensure your backend provides these API endpoints:
- `POST /api/scale-items` - Scale items with parameters
- `GET /api/search-items?q=query` - Search items by name
- `POST /api/export-sql` - Export scaled items as SQL
## Backend API Expected Format
### Scale Items Request
```json
{
"itemLevel": 325,
"phase": 1,
"quality": 4,
"catchup": 1.5,
"entries": [19019, 19103, 17076]
}
```
### Scale Items Response
```json
{
"items": [
{
"success": true,
"entryId": 19019,
"original": {
"entry": 19019,
"name": "Thunderfury, Blessed Blade of the Windseeker",
"quality": 5,
"itemLevel": 80,
"stats": { "Stamina": 15, "Agility": 5 }
},
"scaled": {
"entry": 19019,
"name": "Thunderfury, Blessed Blade of the Windseeker",
"quality": 5,
"itemLevel": 325,
"stats": { "Stamina": 45, "Agility": 15 }
},
"referenceItem": {
"name": "Some Reference Item",
"entry": 12345
}
}
],
"summary": {
"total": 1,
"successful": 1,
"failed": 0,
"successRate": "100.0"
}
}
```
### Search Items Response
```json
[
{
"entry": 19019,
"name": "Thunderfury, Blessed Blade of the Windseeker",
"quality": 5
}
]
```
## File Structure
```
wow-item-scaler-web/
├── index.html # Main HTML file with CDN dependencies
├── css/
│ └── wow-theme.css # Complete WoW theme styles (8.4KB)
├── js/
│ ├── wow-theme.js # Theme utilities and UI components (9.8KB)
│ └── item-scaler.js # Main application logic and API calls (12KB)
└── README.md # This documentation
```
## Technical Details
### CSS Architecture
- **CSS Custom Properties** for theme consistency
- **Component-based styling** with `.wow-` prefixed classes
- **Layered background system** with multiple gradients, textures, and particle effects
- **Animated decorative elements** including glowing gems, magical sparkles, and pulsing effects
- **Responsive breakpoints** for mobile compatibility
- **Advanced visual effects** using pure CSS (no external images required)
### JavaScript Architecture
- **Class-based ES6 modules** for organization
- **Event-driven architecture** for user interactions
- **LocalStorage integration** for user preferences
- **Error handling** with user-friendly notifications
### Browser Compatibility
- Modern browsers with ES6+ support
- Chrome, Firefox, Safari, Edge
- Mobile browsers (responsive design)
## Customization
### Backend URL
Update the `backendUrl` in `js/item-scaler.js`:
```javascript
this.backendUrl = 'http://your-backend-server:8080';
```
### Styling
The WoW theme can be customized in `css/wow-theme.css`. Key variables:
```css
:root {
--wow-gold: #ffd100; /* Primary accent color */
--wow-blue: #0084ff; /* Rare item color */
--wow-purple: #a335ee; /* Epic item color */
--wow-orange: #ff8000; /* Legendary item color */
--wow-bg-dark: #0a0a0a; /* Primary background */
--wow-bg-panel: #1a1a1a; /* Panel background */
}
```
### Item Icons
Item icons are loaded from Wowhead. Enhance the `getItemIcon()` function in `wow-theme.js` with your own icon mapping logic.
## No Build Process Required
This is a pure frontend application that runs directly in the browser:
- No bundlers (Webpack, Vite, etc.)
- No transpilation needed
- No package.json or npm install
- Just open `index.html` and go!

View File

@@ -0,0 +1,772 @@
/* Araxia Online - Wrath of the Lich King Theme */
:root {
--araxia-primary: #11111f; /* Deep navy from Araxia */
--araxia-text: #f8f8ff; /* Ghost white from Araxia */
--wotlk-ice-blue: #4fc3f7; /* Frost blue accent */
--wotlk-ice-light: #81d4fa; /* Light ice blue */
--wotlk-ice-dark: #0288d1; /* Dark ice blue */
--wotlk-frost: #b3e5fc; /* Frost white-blue */
--wotlk-shadow: #1a237e; /* Deep shadow blue */
--wow-purple: #a335ee; /* Epic purple (unchanged) */
--wow-orange: #ff8000; /* Legendary orange (unchanged) */
--wow-green: #1eff00; /* Uncommon green (unchanged) */
--wow-red: #ff0000; /* Error red (unchanged) */
--wow-bg-dark: var(--araxia-primary);
--wow-bg-panel: #1e1e3f; /* Slightly lighter navy */
--wow-bg-card: #252547; /* Card background - navy tinted */
--wow-border: #4fc3f7; /* Ice blue borders */
--wow-border-primary: var(--wotlk-ice-blue);
--wow-text-primary: var(--araxia-text);
--wow-text-secondary: var(--wotlk-frost);
--wow-text-muted: #7986cb; /* Muted ice blue */
}
/* Background with Araxia/WotLK ice-themed layers and mage imagery */
/* Body and main container for sticky footer */
body.wow-bg {
min-height: 100vh;
display: flex;
flex-direction: column;
background:
/* Frost crystal pattern overlay */
radial-gradient(circle at 25% 25%, rgba(79, 195, 247, 0.06) 0%, transparent 50%),
radial-gradient(circle at 75% 75%, rgba(129, 212, 250, 0.04) 0%, transparent 50%),
radial-gradient(circle at 50% 10%, rgba(26, 35, 126, 0.05) 0%, transparent 60%),
/* CSS-based frost/ice landscape simulation */
radial-gradient(ellipse at 30% 20%, rgba(179, 229, 252, 0.1) 0%, transparent 40%),
radial-gradient(ellipse at 70% 80%, rgba(26, 35, 126, 0.08) 0%, transparent 50%),
/* Mountain/landscape silhouettes */
linear-gradient(180deg,
transparent 0%,
transparent 60%,
rgba(79, 195, 247, 0.05) 65%,
rgba(129, 212, 250, 0.08) 75%,
rgba(17, 17, 31, 0.3) 85%,
rgba(37, 37, 71, 0.6) 100%
),
/* Enhanced CSS frost landscape simulation */
conic-gradient(from 180deg at 20% 30%,
rgba(79, 195, 247, 0.1) 0deg,
rgba(26, 35, 126, 0.05) 60deg,
rgba(179, 229, 252, 0.08) 120deg,
rgba(79, 195, 247, 0.1) 180deg,
rgba(26, 35, 126, 0.05) 240deg,
rgba(179, 229, 252, 0.08) 300deg,
rgba(79, 195, 247, 0.1) 360deg
),
/* Iceberg/mountain silhouette effect */
radial-gradient(ellipse 800px 400px at 30% 100%,
rgba(37, 37, 71, 0.4) 0%,
rgba(79, 195, 247, 0.1) 40%,
transparent 70%
),
radial-gradient(ellipse 600px 300px at 70% 100%,
rgba(26, 35, 126, 0.3) 0%,
rgba(129, 212, 250, 0.08) 50%,
transparent 80%
),
/* Local images with proper sizing and color filtering */
url('images/wotlk-bg1.jpg'),
url('images/wotlk-bg2.jpg'),
url('images/icecrown-bg.jpg'),
/* Theme matching overlay for images */
linear-gradient(45deg,
rgba(17, 17, 31, 0.85) 0%,
rgba(30, 30, 63, 0.88) 25%,
rgba(17, 17, 31, 0.85) 50%,
rgba(37, 37, 71, 0.88) 75%,
rgba(17, 17, 31, 0.85) 100%
),
/* Base Araxia navy gradient */
linear-gradient(135deg, var(--araxia-primary) 0%, #1e1e3f 30%, var(--araxia-primary) 60%, #252547 100%),
/* Ice crystal texture pattern */
repeating-linear-gradient(
30deg,
transparent,
transparent 3px,
rgba(79, 195, 247, 0.015) 3px,
rgba(79, 195, 247, 0.015) 6px
),
repeating-linear-gradient(
-30deg,
transparent,
transparent 3px,
rgba(129, 212, 250, 0.01) 3px,
rgba(129, 212, 250, 0.01) 6px
);
background-attachment: fixed;
background-size:
1000px 1000px,
800px 800px,
600px 600px,
1200px 800px,
800px 600px,
cover,
cover,
cover,
cover,
cover,
/* Image sizing with proper aspect ratio maintenance */
cover,
cover,
cover,
cover,
cover,
6px 6px,
6px 6px;
background-position:
center,
center,
center,
center top,
center bottom,
center,
center,
center,
center,
center,
/* Image positioning for best visual impact */
center center,
left center,
right center,
center,
center,
center,
center;
background-repeat: no-repeat;
color: var(--wow-text-primary);
font-family: 'Cinzel', serif;
position: relative;
/* Color adjustment filter for better theme matching */
filter: hue-rotate(-10deg) saturate(1.1) brightness(0.95);
}
/* Main content area that pushes footer down */
main {
flex: 1;
}
/* Add subtle frost animated effects */
.wow-bg::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 20%, rgba(79, 195, 247, 0.03) 0%, transparent 30%),
radial-gradient(circle at 80% 80%, rgba(129, 212, 250, 0.02) 0%, transparent 40%),
radial-gradient(circle at 60% 60%, rgba(26, 35, 126, 0.02) 0%, transparent 50%);
animation: wow-frost-glow 12s ease-in-out infinite alternate;
pointer-events: none;
z-index: -1;
}
@keyframes wow-frost-glow {
0% { opacity: 0.4; transform: scale(1) rotate(0deg); }
100% { opacity: 0.8; transform: scale(1.03) rotate(0.5deg); }
}
/* Header with Araxia/WotLK ice theme */
.wow-header {
background:
/* Frost border pattern */
linear-gradient(90deg, transparent 0%, rgba(79, 195, 247, 0.15) 20%, rgba(79, 195, 247, 0.25) 50%, rgba(79, 195, 247, 0.15) 80%, transparent 100%),
/* Ice texture */
linear-gradient(180deg, #252547 0%, #1e1e3f 20%, #2a2a52 40%, #1e1e3f 60%, #252547 100%),
/* Crystal-like pattern */
repeating-linear-gradient(45deg, transparent, transparent 1px, rgba(79, 195, 247, 0.03) 1px, rgba(79, 195, 247, 0.03) 2px);
border-image: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), transparent) 1;
position: relative;
overflow: hidden;
}
.wow-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), transparent);
box-shadow: 0 0 5px rgba(79, 195, 247, 0.4);
}
.wow-header::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 200px;
height: 3px;
background: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), var(--wotlk-ice-light), var(--wotlk-ice-blue), transparent);
box-shadow: 0 0 15px rgba(79, 195, 247, 0.6);
}
.wow-logo-container {
text-align: center;
}
.araxia-logo {
max-width: 120px;
height: auto;
filter: drop-shadow(0 0 10px rgba(79, 195, 247, 0.4));
transition: all 0.3s ease;
}
.araxia-logo:hover {
filter: drop-shadow(0 0 20px rgba(79, 195, 247, 0.8));
transform: scale(1.05);
}
.wow-server-name {
color: var(--wotlk-ice-light);
font-size: 0.9rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
margin-top: 0.25rem;
text-shadow: 0 0 10px rgba(129, 212, 250, 0.6);
}
.wow-title {
font-family: 'Uncial Antiqua', serif;
color: var(--wotlk-ice-blue);
text-shadow:
2px 2px 4px rgba(0, 0, 0, 0.9),
0 0 20px rgba(79, 195, 247, 0.8),
0 0 40px rgba(129, 212, 250, 0.4);
letter-spacing: 2px;
position: relative;
display: inline-block;
}
.wow-title::before {
content: '❄';
position: absolute;
left: -2rem;
top: 50%;
transform: translateY(-50%);
color: var(--wotlk-ice-light);
font-size: 0.8em;
text-shadow: 0 0 15px rgba(129, 212, 250, 0.9);
animation: wow-ice-pulse 4s ease-in-out infinite;
}
.wow-title::after {
content: '❄';
position: absolute;
right: -2rem;
top: 50%;
transform: translateY(-50%) rotate(45deg);
color: var(--wotlk-ice-light);
font-size: 0.8em;
text-shadow: 0 0 15px rgba(129, 212, 250, 0.9);
animation: wow-ice-pulse 4s ease-in-out infinite reverse;
}
@keyframes wow-ice-pulse {
0%, 100% { opacity: 0.7; transform: translateY(-50%) scale(1) rotate(0deg); }
50% { opacity: 1; transform: translateY(-50%) scale(1.2) rotate(15deg); }
}
.wow-subtitle {
color: var(--wow-text-secondary);
font-style: italic;
margin-top: -5px;
}
/* Panels */
.wow-panel {
background:
/* Ornate corner decorations */
radial-gradient(circle at 0% 0%, rgba(255, 209, 0, 0.1) 0%, transparent 15%),
radial-gradient(circle at 100% 0%, rgba(255, 209, 0, 0.1) 0%, transparent 15%),
radial-gradient(circle at 0% 100%, rgba(255, 209, 0, 0.1) 0%, transparent 15%),
radial-gradient(circle at 100% 100%, rgba(255, 209, 0, 0.1) 0%, transparent 15%),
/* Stone-like texture */
linear-gradient(135deg, rgba(26, 26, 26, 0.95) 0%, rgba(30, 30, 30, 0.98) 50%, rgba(22, 22, 22, 0.95) 100%),
/* Subtle pattern */
repeating-linear-gradient(0deg, transparent, transparent 1px, rgba(255, 255, 255, 0.01) 1px, rgba(255, 255, 255, 0.01) 2px);
border: 2px solid var(--wow-border);
border-radius: 8px;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.6),
inset 0 1px 0 rgba(255, 209, 0, 0.1),
inset 0 -1px 0 rgba(0, 0, 0, 0.5);
overflow: hidden;
position: relative;
}
.wow-panel::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
linear-gradient(45deg, transparent 48%, rgba(255, 209, 0, 0.05) 49%, rgba(255, 209, 0, 0.05) 51%, transparent 52%),
linear-gradient(-45deg, transparent 48%, rgba(255, 209, 0, 0.03) 49%, rgba(255, 209, 0, 0.03) 51%, transparent 52%);
pointer-events: none;
}
.wow-panel-header {
background:
/* Ornate header pattern */
linear-gradient(90deg,
rgba(255, 209, 0, 0.05) 0%,
rgba(255, 209, 0, 0.1) 10%,
rgba(255, 209, 0, 0.08) 50%,
rgba(255, 209, 0, 0.1) 90%,
rgba(255, 209, 0, 0.05) 100%
),
linear-gradient(90deg, var(--wow-bg-card), var(--wow-bg-panel));
border-bottom: 2px solid var(--wow-border-gold);
padding: 1rem 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.wow-panel-header::before {
content: '';
position: absolute;
top: 50%;
left: 1rem;
transform: translateY(-50%);
width: 8px;
height: 8px;
background: radial-gradient(circle, var(--wow-gold), var(--wow-gold-dark));
border-radius: 50%;
box-shadow: 0 0 8px rgba(255, 209, 0, 0.6);
}
.wow-panel-header::after {
content: '';
position: absolute;
top: 50%;
right: 1rem;
transform: translateY(-50%);
width: 8px;
height: 8px;
background: radial-gradient(circle, var(--wow-gold), var(--wow-gold-dark));
border-radius: 50%;
box-shadow: 0 0 8px rgba(255, 209, 0, 0.6);
}
.wow-panel-content {
padding: 1.5rem;
}
.wow-section-title {
color: var(--wow-gold);
font-family: 'Uncial Antiqua', serif;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
}
/* Form Elements */
.wow-input-group {
display: flex;
flex-direction: column;
}
.wow-label {
color: var(--wow-gold);
font-weight: 600;
margin-bottom: 0.5rem;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 1px;
}
.wow-input,
.wow-select {
background: var(--wow-bg-card);
border: 2px solid var(--wow-border);
border-radius: 4px;
padding: 0.75rem;
color: var(--wow-text-primary);
font-family: 'Cinzel', serif;
transition: all 0.3s ease;
}
.wow-input:focus,
.wow-select:focus {
outline: none;
border-color: var(--wow-gold);
box-shadow: 0 0 10px rgba(255, 209, 0, 0.3);
}
.wow-help-text {
color: var(--wow-text-muted);
font-size: 0.8rem;
margin-top: 0.25rem;
font-style: italic;
}
/* Tabs */
.wow-tab-container {
display: flex;
border-bottom: 2px solid var(--wow-border);
margin-bottom: 1rem;
}
.wow-tab {
background: var(--wow-bg-card);
border: 2px solid var(--wow-border);
border-bottom: none;
padding: 0.75rem 1.5rem;
color: var(--wow-text-secondary);
font-family: 'Cinzel', serif;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
margin-right: 2px;
}
.wow-tab:hover {
background: var(--wow-bg-panel);
color: var(--wow-gold);
}
.wow-tab-active {
background: var(--wow-bg-panel);
color: var(--wow-gold);
border-color: var(--wow-border-gold);
position: relative;
}
.wow-tab-active::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 2px;
background: var(--wow-gold);
}
/* Buttons */
.wow-button {
background: linear-gradient(135deg, var(--wow-bg-card), var(--wow-bg-panel));
border: 2px solid var(--wow-border);
color: var(--wow-text-primary);
padding: 0.75rem 2rem;
border-radius: 4px;
font-family: 'Cinzel', serif;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.wow-button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 209, 0, 0.2), transparent);
transition: left 0.5s ease;
}
.wow-button:hover::before {
left: 100%;
}
.wow-button:hover {
border-color: var(--wow-gold);
box-shadow: 0 0 20px rgba(255, 209, 0, 0.3);
transform: translateY(-2px);
}
.wow-button-primary {
background: linear-gradient(135deg, #2a4d6e, #1e3a52);
border-color: var(--wow-blue);
color: var(--wow-text-primary);
}
.wow-button-primary:hover {
border-color: var(--wow-gold);
background: linear-gradient(135deg, #3a5d7e, #2e4a62);
}
.wow-button-secondary {
background: linear-gradient(135deg, #4a2a6e, #3a1e52);
border-color: var(--wow-purple);
}
.wow-button-secondary:hover {
border-color: var(--wow-gold);
background: linear-gradient(135deg, #5a3a7e, #4a2e62);
}
.wow-button-small {
padding: 0.5rem 1rem;
font-size: 0.85rem;
}
.wow-button-text {
position: relative;
z-index: 1;
}
/* Search Results */
.wow-search-results {
background: var(--wow-bg-card);
border: 2px solid var(--wow-border);
border-top: none;
max-height: 200px;
overflow-y: auto;
position: absolute;
width: 100%;
z-index: 10;
border-radius: 0 0 4px 4px;
}
.wow-search-item {
padding: 0.75rem;
cursor: pointer;
border-bottom: 1px solid var(--wow-border);
display: flex;
align-items: center;
gap: 0.75rem;
transition: background 0.2s ease;
}
.wow-search-item:hover {
background: var(--wow-bg-panel);
}
.wow-search-item:last-child {
border-bottom: none;
}
/* Item Cards */
.wow-item-card {
background: var(--wow-bg-card);
border: 2px solid var(--wow-border);
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1rem;
position: relative;
overflow: hidden;
}
.wow-item-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, transparent, var(--wow-gold), transparent);
}
.wow-item-epic {
border-color: var(--wow-purple);
}
.wow-item-legendary {
border-color: var(--wow-orange);
}
.wow-item-comparison {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 2rem;
align-items: center;
}
.wow-item-before,
.wow-item-after {
text-align: center;
}
.wow-item-arrow {
font-size: 2rem;
color: var(--wow-gold);
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
}
.wow-item-image {
width: 64px;
height: 64px;
border: 2px solid var(--wow-border);
border-radius: 4px;
margin: 0 auto 1rem;
background: var(--wow-bg-panel);
}
.wow-item-name {
font-weight: 700;
font-size: 1.1rem;
margin-bottom: 0.5rem;
}
.wow-item-stats {
font-size: 0.9rem;
line-height: 1.5;
}
.wow-stat-line {
margin-bottom: 0.25rem;
}
.wow-stat-improved {
color: var(--wow-green);
}
.wow-stat-decreased {
color: var(--wow-red);
}
/* Quality Colors */
.quality-poor { color: #9d9d9d; }
.quality-common { color: #ffffff; }
.quality-uncommon { color: #1eff00; }
.quality-rare { color: #0070dd; }
.quality-epic { color: var(--wow-purple); }
.quality-legendary { color: var(--wow-orange); }
/* Loading Animation */
.wow-loading {
text-align: center;
padding: 3rem;
}
.wow-spinner {
width: 60px;
height: 60px;
border: 4px solid var(--wow-border);
border-top: 4px solid var(--wow-gold);
border-radius: 50%;
animation: wow-spin 1s linear infinite;
margin: 0 auto 1rem;
}
.wow-loading-text {
color: var(--wow-gold);
font-size: 1.1rem;
font-weight: 600;
animation: wow-pulse 1.5s ease-in-out infinite;
}
@keyframes wow-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes wow-pulse {
0%, 100% { opacity: 0.7; }
50% { opacity: 1; }
}
/* Footer - always at bottom */
.wow-footer {
background: var(--wow-bg-panel);
border-top: 2px solid var(--wow-border);
margin-top: auto; /* This pushes footer to bottom */
flex-shrink: 0; /* Prevent footer from shrinking */
position: relative;
z-index: 10;
}
.wow-footer::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--wotlk-ice-blue), transparent);
box-shadow: 0 0 10px rgba(79, 195, 247, 0.4);
}
.wow-footer-text {
color: var(--wow-text-muted);
font-size: 0.9rem;
}
/* Frost/ice particle effects */
.wow-bg::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(2px 2px at 20px 30px, rgba(79, 195, 247, 0.4), transparent),
radial-gradient(1px 1px at 40px 70px, rgba(129, 212, 250, 0.3), transparent),
radial-gradient(3px 3px at 90px 40px, rgba(179, 229, 252, 0.3), transparent),
radial-gradient(1px 1px at 130px 80px, rgba(79, 195, 247, 0.4), transparent),
radial-gradient(2px 2px at 160px 30px, rgba(129, 212, 250, 0.25), transparent),
radial-gradient(1px 1px at 180px 60px, rgba(26, 35, 126, 0.3), transparent);
background-repeat: repeat;
background-size: 250px 120px;
animation: wow-frost-particles 25s linear infinite;
pointer-events: none;
z-index: -1;
opacity: 0.7;
}
@keyframes wow-frost-particles {
0% { transform: translateY(0) translateX(0) rotate(0deg); }
100% { transform: translateY(-120px) translateX(30px) rotate(180deg); }
}
/* Enhanced section titles with ice decorative elements */
.wow-section-title::before {
content: '❖';
margin-right: 0.5rem;
color: var(--wotlk-ice-light);
text-shadow: 0 0 15px rgba(129, 212, 250, 0.9);
animation: wow-ice-gem-glow 3s ease-in-out infinite alternate;
}
.wow-section-title::after {
content: '❖';
margin-left: 0.5rem;
color: var(--wotlk-ice-light);
text-shadow: 0 0 15px rgba(129, 212, 250, 0.9);
animation: wow-ice-gem-glow 3s ease-in-out infinite alternate-reverse;
}
@keyframes wow-ice-gem-glow {
0% { opacity: 0.6; transform: scale(1) rotate(0deg); }
100% { opacity: 1; transform: scale(1.3) rotate(45deg); }
}
/* Responsive Design */
@media (max-width: 768px) {
.wow-item-comparison {
grid-template-columns: 1fr;
gap: 1rem;
}
.wow-item-arrow {
transform: rotate(90deg);
font-size: 1.5rem;
}
.wow-tab-container {
flex-direction: column;
}
.wow-tab {
margin-right: 0;
margin-bottom: 2px;
}
.wow-title::before,
.wow-title::after {
display: none; /* Hide sword decorations on mobile */
}
}

View File

@@ -0,0 +1,34 @@
# Background Images for WoW Item Scaler
This directory should contain WoW frost/ice themed background images to enhance the visual appearance of the application.
## Required Images:
1. **wotlk-bg1.jpg** - Wrath of the Lich King landscape (recommended: 1920x1080)
2. **wotlk-bg2.jpg** - Northrend/frost themed scenery (recommended: 1920x1080)
3. **icecrown-bg.jpg** - Icecrown Citadel or similar WotLK location (recommended: 1920x1080)
## Recommended Sources:
- Official Blizzard press kit images
- WoW community screenshots (with proper licensing)
- Fan art (with permission)
- Generated AI art in WoW style
## Image Specifications:
- **Format**: JPG or PNG
- **Size**: 1920x1080 recommended for best quality
- **Theme**: Frost, ice, snow, Wrath of the Lich King landscapes
- **Colors**: Cool blues, whites, grays to match Araxia theme
## CSS Integration:
The images are automatically integrated via `css/wow-theme.css` with:
- Araxia color overlay blending
- Ice particle effects on top
- Multiple layer composition for depth
## Fallback:
If images are not present, the CSS will fall back to pure CSS-generated frost patterns and gradients that simulate the WoW aesthetic.

View File

@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WoW Item Scaler</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://wow.zamimg.com/widgets/power.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Uncial+Antiqua&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/wow-theme.css">
</head>
<body class="min-h-screen wow-bg">
<!-- Header -->
<header class="wow-header border-b-4 shadow-2xl" style="border-color: #4fc3f7;">
<div class="container mx-auto px-6 py-4">
<div class="flex items-center justify-center">
<div class="wow-logo-container">
<div class="flex items-center justify-center mb-2">
<img src="https://araxiaonline.github.io/docs/_media/logo.png" alt="Araxia Online" class="araxia-logo" onerror="this.style.display='none'">
</div>
<h1 class="wow-title text-4xl font-bold">Item Scaler</h1>
<p class="wow-subtitle text-lg">Wrath of the Lich King</p>
<p class="wow-server-name">Araxia Online</p>
</div>
</div>
</div>
</header>
<main class="container mx-auto px-6 py-8">
<!-- Scaling Parameters Panel -->
<div class="wow-panel mb-8">
<div class="wow-panel-header">
<h2 class="wow-section-title text-2xl font-semibold">Scaling Parameters</h2>
</div>
<div class="wow-panel-content">
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div class="wow-input-group">
<label for="itemLevel" class="wow-label">Item Level</label>
<input type="number" id="itemLevel" class="wow-input" value="325" min="1" max="500">
</div>
<div class="wow-input-group">
<label for="phase" class="wow-label">Phase</label>
<select id="phase" class="wow-select">
<option value="0">No Tier Bonus</option>
<option value="1">Phase 1</option>
<option value="2">Phase 2</option>
<option value="3">Phase 3</option>
<option value="4">Phase 4</option>
<option value="5">Phase 5</option>
</select>
</div>
<div class="wow-input-group">
<label for="quality" class="wow-label">Quality</label>
<select id="quality" class="wow-select">
<option value="4" class="text-purple-400">Epic</option>
<option value="5" class="text-orange-400">Legendary</option>
</select>
</div>
<div class="wow-input-group">
<label for="catchup" class="wow-label">Catchup Bonus</label>
<input type="number" id="catchup" class="wow-input" value="1.0" min="0.1" max="3.0" step="0.1">
</div>
</div>
</div>
</div>
<!-- Item Input Panel -->
<div class="wow-panel mb-8">
<div class="wow-panel-header">
<h2 class="wow-section-title text-2xl font-semibold">Item Selection</h2>
</div>
<div class="wow-panel-content">
<div class="space-y-6">
<!-- Tab Navigation -->
<div class="wow-tab-container">
<button id="tabEntries" class="wow-tab wow-tab-active">Entry IDs</button>
<button id="tabSearch" class="wow-tab">Search by Name</button>
</div>
<!-- Entry IDs Input -->
<div id="entriesPanel" class="wow-tab-panel">
<div class="wow-input-group">
<label for="entryIds" class="wow-label">Entry IDs (comma-separated)</label>
<input type="text" id="entryIds" class="wow-input" placeholder="19019, 19103, 17076">
<p class="wow-help-text">Enter item entry IDs separated by commas</p>
</div>
</div>
<!-- Search Input -->
<div id="searchPanel" class="wow-tab-panel hidden">
<div class="wow-input-group">
<label for="itemSearch" class="wow-label">Item Name</label>
<input type="text" id="itemSearch" class="wow-input" placeholder="Thunderfury">
<div id="searchResults" class="wow-search-results hidden"></div>
</div>
</div>
<!-- Action Buttons -->
<div class="flex gap-4">
<button id="scaleButton" class="wow-button wow-button-primary">
<span class="wow-button-text">Scale Items</span>
</button>
<button id="clearButton" class="wow-button wow-button-secondary">
<span class="wow-button-text">Clear Results</span>
</button>
</div>
</div>
</div>
</div>
<!-- Loading Spinner -->
<div id="loadingSpinner" class="wow-loading hidden">
<div class="wow-spinner"></div>
<p class="wow-loading-text">Forging your items...</p>
</div>
<!-- Results Panel -->
<div id="resultsPanel" class="wow-panel hidden">
<div class="wow-panel-header">
<h2 class="wow-section-title text-2xl font-semibold">Scaled Items</h2>
<div class="flex gap-2">
<button id="exportSqlButton" class="wow-button-small wow-button-secondary">Export SQL</button>
<button id="copyResultsButton" class="wow-button-small wow-button-secondary">Copy Results</button>
</div>
</div>
<div id="resultsContainer" class="wow-panel-content">
<!-- Results will be populated here -->
</div>
</div>
</main>
<!-- Footer -->
<footer class="wow-footer mt-12">
<div class="container mx-auto px-6 py-4 text-center">
<p class="wow-footer-text">Araxia Online - Wrath of the Lich King Item Scaler</p>
<p class="text-xs mt-2" style="color: #7986cb;">Forge your destiny in the frozen wastes of Northrend</p>
</div>
</footer>
<script src="js/wow-theme.js"></script>
<script src="js/item-scaler.js"></script>
</body>
</html>

View File

@@ -0,0 +1,342 @@
// Main Item Scaler Application Logic
class ItemScaler {
constructor() {
this.backendUrl = 'http://localhost:8080'; // Update this to match your backend
this.searchTimeout = null;
this.init();
}
init() {
this.setupEventListeners();
this.loadDefaultValues();
}
setupEventListeners() {
// Scale button
document.getElementById('scaleButton').addEventListener('click', () => {
this.scaleItems();
});
// Clear button
document.getElementById('clearButton').addEventListener('click', () => {
WowTheme.clearResults();
});
// Export SQL button
document.getElementById('exportSqlButton').addEventListener('click', () => {
this.exportSQL();
});
// Copy results button
document.getElementById('copyResultsButton').addEventListener('click', () => {
this.copyResults();
});
// Search input
document.getElementById('itemSearch').addEventListener('input', (e) => {
this.handleSearchInput(e.target.value);
});
// Hide search results when clicking outside
document.addEventListener('click', (e) => {
const searchContainer = document.getElementById('searchPanel');
if (!searchContainer.contains(e.target)) {
document.getElementById('searchResults').classList.add('hidden');
}
});
}
loadDefaultValues() {
// Load any saved preferences from localStorage
const savedValues = this.getSavedValues();
if (savedValues) {
document.getElementById('itemLevel').value = savedValues.itemLevel || 325;
document.getElementById('phase').value = savedValues.phase || 0;
document.getElementById('quality').value = savedValues.quality || 4;
document.getElementById('catchup').value = savedValues.catchup || 1.0;
}
}
getSavedValues() {
try {
return JSON.parse(localStorage.getItem('wowItemScalerSettings'));
} catch {
return null;
}
}
saveValues() {
const values = {
itemLevel: parseInt(document.getElementById('itemLevel').value),
phase: parseInt(document.getElementById('phase').value),
quality: parseInt(document.getElementById('quality').value),
catchup: parseFloat(document.getElementById('catchup').value)
};
localStorage.setItem('wowItemScalerSettings', JSON.stringify(values));
}
async scaleItems() {
try {
WowTheme.showLoading();
this.saveValues();
const scalingParams = this.getScalingParameters();
const itemEntries = this.getItemEntries();
if (itemEntries.length === 0) {
WowTheme.hideLoading();
WowTheme.showNotification('Please provide item entries or search for items', 'error');
return;
}
const results = await this.callBackendAPI(scalingParams, itemEntries);
this.displayResults(results);
} catch (error) {
console.error('Error scaling items:', error);
WowTheme.hideLoading();
WowTheme.showNotification(`Error: ${error.message}`, 'error');
}
}
getScalingParameters() {
return {
itemLevel: parseInt(document.getElementById('itemLevel').value),
phase: parseInt(document.getElementById('phase').value),
quality: parseInt(document.getElementById('quality').value),
catchup: parseFloat(document.getElementById('catchup').value)
};
}
getItemEntries() {
const entriesText = document.getElementById('entryIds').value.trim();
if (!entriesText) {
return [];
}
return entriesText
.split(',')
.map(entry => entry.trim())
.filter(entry => entry && !isNaN(entry))
.map(entry => parseInt(entry));
}
async callBackendAPI(scalingParams, itemEntries) {
const requestData = {
...scalingParams,
entries: itemEntries
};
console.log('Sending request:', requestData);
const response = await fetch(`${this.backendUrl}/api/scale-items`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestData)
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
}
displayResults(results) {
const container = document.getElementById('resultsContainer');
container.innerHTML = '';
if (!results || !results.items || results.items.length === 0) {
container.innerHTML = `
<div class="text-center text-gray-400 py-8">
<p>No items were successfully scaled.</p>
<p class="text-sm mt-2">Check the console for detailed error information.</p>
</div>
`;
WowTheme.showResults();
return;
}
// Display summary
const summary = `
<div class="wow-panel mb-6">
<div class="wow-panel-content">
<div class="text-center">
<h3 class="text-xl font-semibold text-yellow-400 mb-4">Scaling Summary</h3>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
<div>
<div class="text-gray-400">Total Items</div>
<div class="text-white font-semibold">${results.summary?.total || results.items.length}</div>
</div>
<div>
<div class="text-gray-400">Successful</div>
<div class="text-green-400 font-semibold">${results.summary?.successful || results.items.length}</div>
</div>
<div>
<div class="text-gray-400">Failed</div>
<div class="text-red-400 font-semibold">${results.summary?.failed || 0}</div>
</div>
<div>
<div class="text-gray-400">Success Rate</div>
<div class="text-yellow-400 font-semibold">${results.summary?.successRate || '100.0'}%</div>
</div>
</div>
</div>
</div>
</div>
`;
container.innerHTML = summary;
// Display individual item results
results.items.forEach(result => {
if (result.success) {
const itemCard = WowTheme.createItemCard(
result.original,
result.scaled,
result.referenceItem
);
container.innerHTML += itemCard;
} else {
// Display error for failed item
container.innerHTML += `
<div class="wow-item-card border-red-600">
<div class="text-center">
<h3 class="text-red-400 font-semibold mb-2">Failed to scale item</h3>
<p class="text-gray-300 mb-2">Entry ID: ${result.entryId}</p>
<div class="text-sm text-gray-400">
${result.errors ? result.errors.map(error => `<p>${error}</p>`).join('') : 'Unknown error occurred'}
</div>
</div>
</div>
`;
}
});
WowTheme.showResults();
this.reinitializeTooltips();
}
reinitializeTooltips() {
// Reinitialize Wowhead tooltips for newly added content
if (window.$WowheadPower) {
window.$WowheadPower.refreshLinks();
}
}
async handleSearchInput(query) {
clearTimeout(this.searchTimeout);
if (!query || query.length < 2) {
document.getElementById('searchResults').classList.add('hidden');
return;
}
this.searchTimeout = setTimeout(async () => {
try {
const results = await this.searchItems(query);
this.displaySearchResults(results);
} catch (error) {
console.error('Search error:', error);
WowTheme.showNotification('Search failed', 'error');
}
}, 300);
}
async searchItems(query) {
const response = await fetch(`${this.backendUrl}/api/search-items?q=${encodeURIComponent(query)}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
if (!response.ok) {
throw new Error(`Search failed: ${response.status}`);
}
return await response.json();
}
displaySearchResults(results) {
const container = document.getElementById('searchResults');
if (!results || results.length === 0) {
container.innerHTML = `
<div class="wow-search-item">
<div class="text-gray-400">No items found</div>
</div>
`;
} else {
container.innerHTML = results
.slice(0, 10) // Limit to 10 results
.map(item => WowTheme.createSearchResultItem(item))
.join('');
}
container.classList.remove('hidden');
}
async exportSQL() {
try {
const scalingParams = this.getScalingParameters();
const itemEntries = this.getItemEntries();
if (itemEntries.length === 0) {
WowTheme.showNotification('No items to export', 'error');
return;
}
const response = await fetch(`${this.backendUrl}/api/export-sql`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...scalingParams,
entries: itemEntries
})
});
if (!response.ok) {
throw new Error(`Export failed: ${response.status}`);
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `scaled_items_${Date.now()}.sql`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
WowTheme.showNotification('SQL exported successfully!', 'success');
} catch (error) {
console.error('Export error:', error);
WowTheme.showNotification('Export failed', 'error');
}
}
async copyResults() {
try {
const container = document.getElementById('resultsContainer');
const textContent = container.innerText;
await navigator.clipboard.writeText(textContent);
WowTheme.showNotification('Results copied to clipboard!', 'success');
} catch (error) {
console.error('Copy error:', error);
WowTheme.showNotification('Failed to copy results', 'error');
}
}
}
// Initialize the application
document.addEventListener('DOMContentLoaded', () => {
new ItemScaler();
});

View File

@@ -0,0 +1,250 @@
// WoW Theme JavaScript utilities
class WowTheme {
static init() {
this.setupTabNavigation();
this.setupTooltips();
this.initializeWowheadPower();
}
static setupTabNavigation() {
const tabButtons = document.querySelectorAll('.wow-tab');
const tabPanels = document.querySelectorAll('.wow-tab-panel');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const isEntries = button.id === 'tabEntries';
// Update tab states
tabButtons.forEach(btn => btn.classList.remove('wow-tab-active'));
button.classList.add('wow-tab-active');
// Show/hide panels
document.getElementById('entriesPanel').classList.toggle('hidden', !isEntries);
document.getElementById('searchPanel').classList.toggle('hidden', isEntries);
});
});
}
static setupTooltips() {
// Initialize Wowhead tooltips when they become available
if (window.$WowheadPower) {
window.$WowheadPower.init();
}
}
static initializeWowheadPower() {
// Configure Wowhead Power settings
if (window.$WH) {
window.$WH.Tooltip.config = {
colorLinks: true,
iconizeLinks: true,
renameLinks: true
};
}
}
static showLoading() {
document.getElementById('loadingSpinner').classList.remove('hidden');
document.getElementById('resultsPanel').classList.add('hidden');
}
static hideLoading() {
document.getElementById('loadingSpinner').classList.add('hidden');
}
static showResults() {
document.getElementById('resultsPanel').classList.remove('hidden');
this.hideLoading();
}
static clearResults() {
const container = document.getElementById('resultsContainer');
container.innerHTML = '';
document.getElementById('resultsPanel').classList.add('hidden');
}
static getQualityClass(quality) {
const qualityMap = {
0: 'quality-poor',
1: 'quality-common',
2: 'quality-uncommon',
3: 'quality-rare',
4: 'quality-epic',
5: 'quality-legendary'
};
return qualityMap[quality] || 'quality-common';
}
static getQualityName(quality) {
const qualityNames = {
0: 'Poor',
1: 'Common',
2: 'Uncommon',
3: 'Rare',
4: 'Epic',
5: 'Legendary'
};
return qualityNames[quality] || 'Common';
}
static createItemCard(original, scaled, referenceItem = null) {
const qualityClass = this.getQualityClass(scaled.quality);
const cardClass = scaled.quality === 4 ? 'wow-item-epic' : 'wow-item-legendary';
return `
<div class="wow-item-card ${cardClass}">
<div class="wow-item-comparison">
<div class="wow-item-before">
<h3 class="wow-item-name ${this.getQualityClass(original.quality)}">
${original.name}
</h3>
<div class="wow-item-image">
<img src="https://wow.zamimg.com/images/wow/icons/medium/${this.getItemIcon(original.entry)}.jpg"
alt="${original.name}"
onerror="this.src='https://wow.zamimg.com/images/wow/icons/medium/inv_misc_questionmark.jpg'">
</div>
<div class="wow-item-stats">
<div class="wow-stat-line">Item Level: ${original.itemLevel || 'Unknown'}</div>
<div class="wow-stat-line">Quality: ${this.getQualityName(original.quality)}</div>
${this.formatStats(original.stats)}
</div>
</div>
<div class="wow-item-arrow">➤</div>
<div class="wow-item-after">
<h3 class="wow-item-name ${qualityClass}">
${scaled.name}
</h3>
<div class="wow-item-image">
<img src="https://wow.zamimg.com/images/wow/icons/medium/${this.getItemIcon(scaled.entry)}.jpg"
alt="${scaled.name}"
onerror="this.src='https://wow.zamimg.com/images/wow/icons/medium/inv_misc_questionmark.jpg'">
</div>
<div class="wow-item-stats">
<div class="wow-stat-line">Item Level: ${scaled.itemLevel}</div>
<div class="wow-stat-line">Quality: ${this.getQualityName(scaled.quality)}</div>
${this.formatStats(scaled.stats, original.stats)}
</div>
</div>
</div>
${referenceItem ? `
<div class="mt-4 pt-4 border-t border-gray-600">
<p class="text-sm text-gray-400 mb-2">Reference Item Used:</p>
<p class="text-yellow-400">${referenceItem.name}</p>
</div>
` : ''}
<div class="mt-4 pt-4 border-t border-gray-600 flex justify-between">
<span class="text-sm text-gray-400">Entry ID: ${scaled.entry}</span>
<button class="wow-button-small wow-button-secondary" onclick="WowTheme.copyToClipboard('${scaled.entry}')">
Copy Entry ID
</button>
</div>
</div>
`;
}
static formatStats(stats, originalStats = null) {
if (!stats || Object.keys(stats).length === 0) {
return '<div class="wow-stat-line text-gray-500">No stats available</div>';
}
return Object.entries(stats).map(([statName, value]) => {
let className = 'wow-stat-line';
let prefix = '';
if (originalStats && originalStats[statName] !== undefined) {
const originalValue = originalStats[statName];
if (value > originalValue) {
className += ' wow-stat-improved';
prefix = '+';
} else if (value < originalValue) {
className += ' wow-stat-decreased';
}
}
return `<div class="${className}">${prefix}${value} ${statName}</div>`;
}).join('');
}
static getItemIcon(entryId) {
// This would ideally come from your backend or a lookup table
// For now, return a default or use the entry ID as a fallback
return `inv_misc_questionmark`;
}
static async copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
this.showNotification('Copied to clipboard!', 'success');
} catch (err) {
console.error('Failed to copy: ', err);
this.showNotification('Failed to copy', 'error');
}
}
static showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 p-4 rounded-lg shadow-lg z-50 transition-all duration-300 ${
type === 'success' ? 'bg-green-600' :
type === 'error' ? 'bg-red-600' :
'bg-blue-600'
} text-white`;
notification.textContent = message;
document.body.appendChild(notification);
// Animate in
setTimeout(() => notification.classList.add('opacity-100'), 10);
// Remove after 3 seconds
setTimeout(() => {
notification.classList.add('opacity-0');
setTimeout(() => notification.remove(), 300);
}, 3000);
}
static createSearchResultItem(item) {
return `
<div class="wow-search-item" data-entry="${item.entry}" onclick="WowTheme.selectSearchItem(${item.entry}, '${item.name.replace(/'/g, "\\'")}')">
<div class="wow-item-image" style="width: 32px; height: 32px;">
<img src="https://wow.zamimg.com/images/wow/icons/small/${this.getItemIcon(item.entry)}.jpg"
alt="${item.name}"
style="width: 100%; height: 100%;"
onerror="this.src='https://wow.zamimg.com/images/wow/icons/small/inv_misc_questionmark.jpg'">
</div>
<div>
<div class="font-medium ${this.getQualityClass(item.quality)}">${item.name}</div>
<div class="text-xs text-gray-400">Entry ID: ${item.entry}</div>
</div>
</div>
`;
}
static selectSearchItem(entryId, itemName) {
const entriesInput = document.getElementById('entryIds');
const currentEntries = entriesInput.value.trim();
if (currentEntries) {
entriesInput.value = `${currentEntries}, ${entryId}`;
} else {
entriesInput.value = entryId.toString();
}
// Clear search
document.getElementById('itemSearch').value = '';
document.getElementById('searchResults').classList.add('hidden');
// Switch to entries tab
document.getElementById('tabEntries').click();
this.showNotification(`Added ${itemName} to selection`, 'success');
}
}
// Initialize theme when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
WowTheme.init();
});