Extract createWindow helper to deduplicate window setup code
Refactor showStatsWindow, showInventoryWindow, and showChatWindow to use a shared createWindow() helper that handles existence checks, z-index management, header/close button creation, and makeDraggable setup. Each function now only contains its unique content creation logic. Added .window-content CSS class to style.css, style-ac.css, and the christmas theme to maintain flex layout through the new wrapper div. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a0698753c5
commit
a82e6f4856
3 changed files with 164 additions and 148 deletions
296
static/script.js
296
static/script.js
|
|
@ -735,37 +735,77 @@ function debounce(fn, ms) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show or create a stats window for a character
|
/**
|
||||||
function showStatsWindow(name) {
|
* Create or show a draggable window. Returns { win, content, isNew }.
|
||||||
debugLog('📊 showStatsWindow called for:', name);
|
* If window already exists, brings it to front and returns isNew: false.
|
||||||
if (statsWindows[name]) {
|
*/
|
||||||
const existing = statsWindows[name];
|
function createWindow(id, title, className, options = {}) {
|
||||||
debugLog('📊 Existing stats window found, showing it:', existing);
|
const { onClose } = options;
|
||||||
// Always show the window (no toggle)
|
|
||||||
existing.style.display = 'flex';
|
// Check if window already exists - bring to front
|
||||||
// Bring to front when opening
|
const existing = document.getElementById(id);
|
||||||
|
if (existing) {
|
||||||
|
existing.style.display = 'flex';
|
||||||
|
if (!window.__chatZ) window.__chatZ = 10000;
|
||||||
|
window.__chatZ += 1;
|
||||||
|
existing.style.zIndex = window.__chatZ;
|
||||||
|
return { win: existing, content: existing.querySelector('.window-content'), isNew: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new window
|
||||||
if (!window.__chatZ) window.__chatZ = 10000;
|
if (!window.__chatZ) window.__chatZ = 10000;
|
||||||
window.__chatZ += 1;
|
window.__chatZ += 1;
|
||||||
existing.style.zIndex = window.__chatZ;
|
|
||||||
debugLog('📊 Stats window shown with zIndex:', window.__chatZ);
|
const win = document.createElement('div');
|
||||||
|
win.id = id;
|
||||||
|
win.className = className;
|
||||||
|
win.style.display = 'flex';
|
||||||
|
win.style.zIndex = window.__chatZ;
|
||||||
|
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.className = 'chat-header';
|
||||||
|
|
||||||
|
const titleSpan = document.createElement('span');
|
||||||
|
titleSpan.textContent = title;
|
||||||
|
header.appendChild(titleSpan);
|
||||||
|
|
||||||
|
const closeBtn = document.createElement('button');
|
||||||
|
closeBtn.className = 'chat-close-btn';
|
||||||
|
closeBtn.textContent = '\u00D7';
|
||||||
|
closeBtn.addEventListener('click', () => {
|
||||||
|
win.style.display = 'none';
|
||||||
|
if (onClose) onClose();
|
||||||
|
});
|
||||||
|
header.appendChild(closeBtn);
|
||||||
|
|
||||||
|
const content = document.createElement('div');
|
||||||
|
content.className = 'window-content';
|
||||||
|
|
||||||
|
win.appendChild(header);
|
||||||
|
win.appendChild(content);
|
||||||
|
document.body.appendChild(win);
|
||||||
|
makeDraggable(win, header);
|
||||||
|
|
||||||
|
return { win, content, isNew: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show or create a stats window for a character
|
||||||
|
function showStatsWindow(name) {
|
||||||
|
debugLog('showStatsWindow called for:', name);
|
||||||
|
const windowId = `statsWindow-${name}`;
|
||||||
|
|
||||||
|
const { win, content, isNew } = createWindow(
|
||||||
|
windowId, `Stats: ${name}`, 'stats-window'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isNew) {
|
||||||
|
debugLog('Existing stats window found, showing it');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debugLog('📊 Creating new stats window for:', name);
|
|
||||||
const win = document.createElement('div');
|
|
||||||
win.className = 'stats-window';
|
|
||||||
win.dataset.character = name;
|
win.dataset.character = name;
|
||||||
// Header (reuses chat-header styling)
|
statsWindows[name] = win;
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'chat-header';
|
|
||||||
const title = document.createElement('span');
|
|
||||||
title.textContent = `Stats: ${name}`;
|
|
||||||
const closeBtn = document.createElement('button');
|
|
||||||
closeBtn.className = 'chat-close-btn';
|
|
||||||
closeBtn.textContent = '×';
|
|
||||||
closeBtn.addEventListener('click', () => { win.style.display = 'none'; });
|
|
||||||
header.appendChild(title);
|
|
||||||
header.appendChild(closeBtn);
|
|
||||||
win.appendChild(header);
|
|
||||||
// Time period controls
|
// Time period controls
|
||||||
const controls = document.createElement('div');
|
const controls = document.createElement('div');
|
||||||
controls.className = 'stats-controls';
|
controls.className = 'stats-controls';
|
||||||
|
|
@ -775,6 +815,12 @@ function showStatsWindow(name) {
|
||||||
{ label: '24H', value: 'now-24h' },
|
{ label: '24H', value: 'now-24h' },
|
||||||
{ label: '7D', value: 'now-7d' }
|
{ label: '7D', value: 'now-7d' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Stats content container (iframes grid)
|
||||||
|
const statsContent = document.createElement('div');
|
||||||
|
statsContent.className = 'chat-messages';
|
||||||
|
statsContent.textContent = 'Loading stats...';
|
||||||
|
|
||||||
timeRanges.forEach(range => {
|
timeRanges.forEach(range => {
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.className = 'time-range-btn';
|
btn.className = 'time-range-btn';
|
||||||
|
|
@ -783,25 +829,17 @@ function showStatsWindow(name) {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
controls.querySelectorAll('.time-range-btn').forEach(b => b.classList.remove('active'));
|
controls.querySelectorAll('.time-range-btn').forEach(b => b.classList.remove('active'));
|
||||||
btn.classList.add('active');
|
btn.classList.add('active');
|
||||||
updateStatsTimeRange(content, name, range.value);
|
updateStatsTimeRange(statsContent, name, range.value);
|
||||||
});
|
});
|
||||||
controls.appendChild(btn);
|
controls.appendChild(btn);
|
||||||
});
|
});
|
||||||
win.appendChild(controls);
|
|
||||||
|
content.appendChild(controls);
|
||||||
// Content container
|
content.appendChild(statsContent);
|
||||||
const content = document.createElement('div');
|
|
||||||
content.className = 'chat-messages';
|
debugLog('Stats window created for:', name);
|
||||||
content.textContent = 'Loading stats...';
|
|
||||||
win.appendChild(content);
|
|
||||||
debugLog('📊 Appending stats window to DOM:', win);
|
|
||||||
document.body.appendChild(win);
|
|
||||||
statsWindows[name] = win;
|
|
||||||
debugLog('📊 Stats window added to DOM, total children:', document.body.children.length);
|
|
||||||
// Load initial stats with default 24h range
|
// Load initial stats with default 24h range
|
||||||
updateStatsTimeRange(content, name, 'now-24h');
|
updateStatsTimeRange(statsContent, name, 'now-24h');
|
||||||
// Enable dragging using the global drag system
|
|
||||||
makeDraggable(win, header);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStatsTimeRange(content, name, timeRange) {
|
function updateStatsTimeRange(content, name, timeRange) {
|
||||||
|
|
@ -832,47 +870,33 @@ function updateStatsTimeRange(content, name, timeRange) {
|
||||||
|
|
||||||
// Show or create an inventory window for a character
|
// Show or create an inventory window for a character
|
||||||
function showInventoryWindow(name) {
|
function showInventoryWindow(name) {
|
||||||
debugLog('🎒 showInventoryWindow called for:', name);
|
debugLog('showInventoryWindow called for:', name);
|
||||||
if (inventoryWindows[name]) {
|
const windowId = `inventoryWindow-${name}`;
|
||||||
const existing = inventoryWindows[name];
|
|
||||||
debugLog('🎒 Existing inventory window found, showing it:', existing);
|
const { win, content, isNew } = createWindow(
|
||||||
// Always show the window (no toggle)
|
windowId, `Inventory: ${name}`, 'inventory-window'
|
||||||
existing.style.display = 'flex';
|
);
|
||||||
// Bring to front when opening
|
|
||||||
if (!window.__chatZ) window.__chatZ = 10000;
|
if (!isNew) {
|
||||||
window.__chatZ += 1;
|
debugLog('Existing inventory window found, showing it');
|
||||||
existing.style.zIndex = window.__chatZ;
|
|
||||||
debugLog('🎒 Inventory window shown with zIndex:', window.__chatZ);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debugLog('🎒 Creating new inventory window for:', name);
|
|
||||||
const win = document.createElement('div');
|
|
||||||
win.className = 'inventory-window';
|
|
||||||
win.dataset.character = name;
|
win.dataset.character = name;
|
||||||
// Header (reuses chat-header styling)
|
inventoryWindows[name] = win;
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'chat-header';
|
|
||||||
const title = document.createElement('span');
|
|
||||||
title.textContent = `Inventory: ${name}`;
|
|
||||||
const closeBtn = document.createElement('button');
|
|
||||||
closeBtn.className = 'chat-close-btn';
|
|
||||||
closeBtn.textContent = '×';
|
|
||||||
closeBtn.addEventListener('click', () => { win.style.display = 'none'; });
|
|
||||||
header.appendChild(title);
|
|
||||||
header.appendChild(closeBtn);
|
|
||||||
win.appendChild(header);
|
|
||||||
// Loading message
|
// Loading message
|
||||||
const loading = document.createElement('div');
|
const loading = document.createElement('div');
|
||||||
loading.className = 'inventory-loading';
|
loading.className = 'inventory-loading';
|
||||||
loading.textContent = 'Loading inventory...';
|
loading.textContent = 'Loading inventory...';
|
||||||
win.appendChild(loading);
|
content.appendChild(loading);
|
||||||
|
|
||||||
// Content container
|
// Inventory content container
|
||||||
const content = document.createElement('div');
|
const invContent = document.createElement('div');
|
||||||
content.className = 'inventory-content';
|
invContent.className = 'inventory-content';
|
||||||
content.style.display = 'none';
|
invContent.style.display = 'none';
|
||||||
win.appendChild(content);
|
content.appendChild(invContent);
|
||||||
|
|
||||||
// Fetch inventory data from main app (which will proxy to inventory service)
|
// Fetch inventory data from main app (which will proxy to inventory service)
|
||||||
fetch(`${API_BASE}/inventory/${encodeURIComponent(name)}?limit=1000`)
|
fetch(`${API_BASE}/inventory/${encodeURIComponent(name)}?limit=1000`)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
@ -881,38 +905,38 @@ function showInventoryWindow(name) {
|
||||||
})
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
loading.style.display = 'none';
|
loading.style.display = 'none';
|
||||||
content.style.display = 'block';
|
invContent.style.display = 'block';
|
||||||
|
|
||||||
// Create inventory grid
|
// Create inventory grid
|
||||||
const grid = document.createElement('div');
|
const grid = document.createElement('div');
|
||||||
grid.className = 'inventory-grid';
|
grid.className = 'inventory-grid';
|
||||||
|
|
||||||
// Render each item
|
// Render each item
|
||||||
data.items.forEach(item => {
|
data.items.forEach(item => {
|
||||||
|
|
||||||
const slot = document.createElement('div');
|
const slot = document.createElement('div');
|
||||||
slot.className = 'inventory-slot';
|
slot.className = 'inventory-slot';
|
||||||
|
|
||||||
// Create layered icon container
|
// Create layered icon container
|
||||||
const iconContainer = document.createElement('div');
|
const iconContainer = document.createElement('div');
|
||||||
iconContainer.className = 'item-icon-composite';
|
iconContainer.className = 'item-icon-composite';
|
||||||
|
|
||||||
// Get base icon ID with portal.dat offset
|
// Get base icon ID with portal.dat offset
|
||||||
const baseIconId = (item.icon + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
const baseIconId = (item.icon + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||||
|
|
||||||
// Check for overlay and underlay from enhanced format or legacy format
|
// Check for overlay and underlay from enhanced format or legacy format
|
||||||
let overlayIconId = null;
|
let overlayIconId = null;
|
||||||
let underlayIconId = null;
|
let underlayIconId = null;
|
||||||
|
|
||||||
// Enhanced format (inventory service) - check for proper icon overlay/underlay properties
|
// Enhanced format (inventory service) - check for proper icon overlay/underlay properties
|
||||||
if (item.icon_overlay_id && item.icon_overlay_id > 0) {
|
if (item.icon_overlay_id && item.icon_overlay_id > 0) {
|
||||||
overlayIconId = (item.icon_overlay_id + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
overlayIconId = (item.icon_overlay_id + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.icon_underlay_id && item.icon_underlay_id > 0) {
|
if (item.icon_underlay_id && item.icon_underlay_id > 0) {
|
||||||
underlayIconId = (item.icon_underlay_id + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
underlayIconId = (item.icon_underlay_id + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: Enhanced format (inventory service) - check spells object for decal info
|
// Fallback: Enhanced format (inventory service) - check spells object for decal info
|
||||||
if (!overlayIconId && !underlayIconId && item.spells && typeof item.spells === 'object') {
|
if (!overlayIconId && !underlayIconId && item.spells && typeof item.spells === 'object') {
|
||||||
// Icon overlay (using the actual property names from the data)
|
// Icon overlay (using the actual property names from the data)
|
||||||
|
|
@ -920,8 +944,8 @@ function showInventoryWindow(name) {
|
||||||
if (item.spells.spell_decal_218103838 && item.spells.spell_decal_218103838 > 100) {
|
if (item.spells.spell_decal_218103838 && item.spells.spell_decal_218103838 > 100) {
|
||||||
overlayIconId = (item.spells.spell_decal_218103838 + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
overlayIconId = (item.spells.spell_decal_218103838 + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Icon underlay
|
// Icon underlay
|
||||||
if (item.spells.spell_decal_218103848 && item.spells.spell_decal_218103848 > 100) {
|
if (item.spells.spell_decal_218103848 && item.spells.spell_decal_218103848 > 100) {
|
||||||
underlayIconId = (item.spells.spell_decal_218103848 + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
underlayIconId = (item.spells.spell_decal_218103848 + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||||
}
|
}
|
||||||
|
|
@ -929,13 +953,13 @@ function showInventoryWindow(name) {
|
||||||
// Legacy format - parse item_data
|
// Legacy format - parse item_data
|
||||||
try {
|
try {
|
||||||
const itemData = typeof item.item_data === 'string' ? JSON.parse(item.item_data) : item.item_data;
|
const itemData = typeof item.item_data === 'string' ? JSON.parse(item.item_data) : item.item_data;
|
||||||
|
|
||||||
if (itemData.IntValues) {
|
if (itemData.IntValues) {
|
||||||
// Icon overlay (ID 218103849) - only use valid icon IDs
|
// Icon overlay (ID 218103849) - only use valid icon IDs
|
||||||
if (itemData.IntValues['218103849'] && itemData.IntValues['218103849'] > 100) {
|
if (itemData.IntValues['218103849'] && itemData.IntValues['218103849'] > 100) {
|
||||||
overlayIconId = (itemData.IntValues['218103849'] + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
overlayIconId = (itemData.IntValues['218103849'] + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Icon underlay (ID 218103850) - only use valid icon IDs
|
// Icon underlay (ID 218103850) - only use valid icon IDs
|
||||||
if (itemData.IntValues['218103850'] && itemData.IntValues['218103850'] > 100) {
|
if (itemData.IntValues['218103850'] && itemData.IntValues['218103850'] > 100) {
|
||||||
underlayIconId = (itemData.IntValues['218103850'] + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
underlayIconId = (itemData.IntValues['218103850'] + 0x06000000).toString(16).toUpperCase().padStart(8, '0');
|
||||||
|
|
@ -945,7 +969,7 @@ function showInventoryWindow(name) {
|
||||||
console.warn('Failed to parse item data for', item.name);
|
console.warn('Failed to parse item data for', item.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create underlay (bottom layer)
|
// Create underlay (bottom layer)
|
||||||
if (underlayIconId) {
|
if (underlayIconId) {
|
||||||
const underlayImg = document.createElement('img');
|
const underlayImg = document.createElement('img');
|
||||||
|
|
@ -955,7 +979,7 @@ function showInventoryWindow(name) {
|
||||||
underlayImg.onerror = function() { this.style.display = 'none'; };
|
underlayImg.onerror = function() { this.style.display = 'none'; };
|
||||||
iconContainer.appendChild(underlayImg);
|
iconContainer.appendChild(underlayImg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create base icon (middle layer)
|
// Create base icon (middle layer)
|
||||||
const baseImg = document.createElement('img');
|
const baseImg = document.createElement('img');
|
||||||
baseImg.className = 'icon-base';
|
baseImg.className = 'icon-base';
|
||||||
|
|
@ -966,7 +990,7 @@ function showInventoryWindow(name) {
|
||||||
this.src = '/icons/06000133.png';
|
this.src = '/icons/06000133.png';
|
||||||
};
|
};
|
||||||
iconContainer.appendChild(baseImg);
|
iconContainer.appendChild(baseImg);
|
||||||
|
|
||||||
// Create overlay (top layer)
|
// Create overlay (top layer)
|
||||||
if (overlayIconId) {
|
if (overlayIconId) {
|
||||||
const overlayImg = document.createElement('img');
|
const overlayImg = document.createElement('img');
|
||||||
|
|
@ -976,74 +1000,68 @@ function showInventoryWindow(name) {
|
||||||
overlayImg.onerror = function() { this.style.display = 'none'; };
|
overlayImg.onerror = function() { this.style.display = 'none'; };
|
||||||
iconContainer.appendChild(overlayImg);
|
iconContainer.appendChild(overlayImg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create tooltip data
|
// Create tooltip data
|
||||||
slot.dataset.name = item.name || 'Unknown Item';
|
slot.dataset.name = item.name || 'Unknown Item';
|
||||||
slot.dataset.value = item.value || 0;
|
slot.dataset.value = item.value || 0;
|
||||||
slot.dataset.burden = item.burden || 0;
|
slot.dataset.burden = item.burden || 0;
|
||||||
|
|
||||||
// Store enhanced data for tooltips
|
// Store enhanced data for tooltips
|
||||||
// All data now comes from inventory service (no more local fallback)
|
// All data now comes from inventory service (no more local fallback)
|
||||||
if (item.max_damage !== undefined || item.object_class_name !== undefined || item.spells !== undefined) {
|
if (item.max_damage !== undefined || item.object_class_name !== undefined || item.spells !== undefined) {
|
||||||
// Inventory service provides clean, structured data with translations
|
// Inventory service provides clean, structured data with translations
|
||||||
// Only include properties that actually exist on the item
|
// Only include properties that actually exist on the item
|
||||||
const enhancedData = {};
|
const enhancedData = {};
|
||||||
|
|
||||||
// Check all possible enhanced properties from inventory service
|
// Check all possible enhanced properties from inventory service
|
||||||
const possibleProps = [
|
const possibleProps = [
|
||||||
'max_damage', 'armor_level', 'damage_bonus', 'attack_bonus',
|
'max_damage', 'armor_level', 'damage_bonus', 'attack_bonus',
|
||||||
'wield_level', 'skill_level', 'lore_requirement', 'equip_skill', 'equip_skill_name',
|
'wield_level', 'skill_level', 'lore_requirement', 'equip_skill', 'equip_skill_name',
|
||||||
'material', 'material_name', 'material_id', 'imbue', 'item_set', 'tinks',
|
'material', 'material_name', 'material_id', 'imbue', 'item_set', 'tinks',
|
||||||
'workmanship', 'workmanship_text', 'damage_rating', 'crit_rating',
|
'workmanship', 'workmanship_text', 'damage_rating', 'crit_rating',
|
||||||
'heal_boost_rating', 'has_id_data', 'object_class_name', 'spells',
|
'heal_boost_rating', 'has_id_data', 'object_class_name', 'spells',
|
||||||
'enhanced_properties', 'damage_range', 'damage_type', 'min_damage',
|
'enhanced_properties', 'damage_range', 'damage_type', 'min_damage',
|
||||||
'speed_text', 'speed_value', 'mana_display', 'spellcraft', 'current_mana', 'max_mana',
|
'speed_text', 'speed_value', 'mana_display', 'spellcraft', 'current_mana', 'max_mana',
|
||||||
'melee_defense_bonus', 'magic_defense_bonus', 'missile_defense_bonus',
|
'melee_defense_bonus', 'magic_defense_bonus', 'missile_defense_bonus',
|
||||||
'elemental_damage_vs_monsters', 'mana_conversion_bonus', 'icon_overlay_id', 'icon_underlay_id'
|
'elemental_damage_vs_monsters', 'mana_conversion_bonus', 'icon_overlay_id', 'icon_underlay_id'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Only add properties that exist and have meaningful values
|
// Only add properties that exist and have meaningful values
|
||||||
possibleProps.forEach(prop => {
|
possibleProps.forEach(prop => {
|
||||||
if (item.hasOwnProperty(prop) && item[prop] !== undefined && item[prop] !== null) {
|
if (item.hasOwnProperty(prop) && item[prop] !== undefined && item[prop] !== null) {
|
||||||
enhancedData[prop] = item[prop];
|
enhancedData[prop] = item[prop];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
slot.dataset.enhancedData = JSON.stringify(enhancedData);
|
slot.dataset.enhancedData = JSON.stringify(enhancedData);
|
||||||
} else {
|
} else {
|
||||||
// No enhanced data available
|
// No enhanced data available
|
||||||
slot.dataset.enhancedData = JSON.stringify({});
|
slot.dataset.enhancedData = JSON.stringify({});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tooltip on hover
|
// Add tooltip on hover
|
||||||
slot.addEventListener('mouseenter', e => showInventoryTooltip(e, slot));
|
slot.addEventListener('mouseenter', e => showInventoryTooltip(e, slot));
|
||||||
slot.addEventListener('mousemove', e => showInventoryTooltip(e, slot));
|
slot.addEventListener('mousemove', e => showInventoryTooltip(e, slot));
|
||||||
slot.addEventListener('mouseleave', hideInventoryTooltip);
|
slot.addEventListener('mouseleave', hideInventoryTooltip);
|
||||||
|
|
||||||
slot.appendChild(iconContainer);
|
slot.appendChild(iconContainer);
|
||||||
grid.appendChild(slot);
|
grid.appendChild(slot);
|
||||||
});
|
});
|
||||||
|
|
||||||
content.appendChild(grid);
|
invContent.appendChild(grid);
|
||||||
|
|
||||||
// Add item count
|
// Add item count
|
||||||
const count = document.createElement('div');
|
const count = document.createElement('div');
|
||||||
count.className = 'inventory-count';
|
count.className = 'inventory-count';
|
||||||
count.textContent = `${data.item_count} items`;
|
count.textContent = `${data.item_count} items`;
|
||||||
content.appendChild(count);
|
invContent.appendChild(count);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
loading.textContent = `Failed to load inventory: ${err.message}`;
|
loading.textContent = `Failed to load inventory: ${err.message}`;
|
||||||
console.error('Inventory fetch failed:', err);
|
console.error('Inventory fetch failed:', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
debugLog('🎒 Appending inventory window to DOM:', win);
|
debugLog('Inventory window created for:', name);
|
||||||
document.body.appendChild(win);
|
|
||||||
inventoryWindows[name] = win;
|
|
||||||
debugLog('🎒 Inventory window added to DOM, total children:', document.body.children.length);
|
|
||||||
|
|
||||||
// Enable dragging using the global drag system
|
|
||||||
makeDraggable(win, header);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inventory tooltip functions
|
// Inventory tooltip functions
|
||||||
|
|
@ -1812,39 +1830,26 @@ function initWebSocket() {
|
||||||
|
|
||||||
// Display or create a chat window for a character
|
// Display or create a chat window for a character
|
||||||
function showChatWindow(name) {
|
function showChatWindow(name) {
|
||||||
debugLog('💬 showChatWindow called for:', name);
|
debugLog('showChatWindow called for:', name);
|
||||||
if (chatWindows[name]) {
|
const windowId = `chatWindow-${name}`;
|
||||||
const existing = chatWindows[name];
|
|
||||||
debugLog('💬 Existing chat window found, showing it:', existing);
|
const { win, content, isNew } = createWindow(
|
||||||
// Always show the window (no toggle)
|
windowId, `Chat: ${name}`, 'chat-window'
|
||||||
existing.style.display = 'flex';
|
);
|
||||||
// Bring to front when opening
|
|
||||||
if (!window.__chatZ) window.__chatZ = 10000;
|
if (!isNew) {
|
||||||
window.__chatZ += 1;
|
debugLog('Existing chat window found, showing it');
|
||||||
existing.style.zIndex = window.__chatZ;
|
|
||||||
debugLog('💬 Chat window shown with zIndex:', window.__chatZ);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debugLog('💬 Creating new chat window for:', name);
|
|
||||||
const win = document.createElement('div');
|
|
||||||
win.className = 'chat-window';
|
|
||||||
win.dataset.character = name;
|
win.dataset.character = name;
|
||||||
// Header
|
chatWindows[name] = win;
|
||||||
const header = document.createElement('div');
|
|
||||||
header.className = 'chat-header';
|
|
||||||
const title = document.createElement('span');
|
|
||||||
title.textContent = `Chat: ${name}`;
|
|
||||||
const closeBtn = document.createElement('button');
|
|
||||||
closeBtn.className = 'chat-close-btn';
|
|
||||||
closeBtn.textContent = '×';
|
|
||||||
closeBtn.addEventListener('click', () => { win.style.display = 'none'; });
|
|
||||||
header.appendChild(title);
|
|
||||||
header.appendChild(closeBtn);
|
|
||||||
win.appendChild(header);
|
|
||||||
// Messages container
|
// Messages container
|
||||||
const msgs = document.createElement('div');
|
const msgs = document.createElement('div');
|
||||||
msgs.className = 'chat-messages';
|
msgs.className = 'chat-messages';
|
||||||
win.appendChild(msgs);
|
content.appendChild(msgs);
|
||||||
|
|
||||||
// Input form
|
// Input form
|
||||||
const form = document.createElement('form');
|
const form = document.createElement('form');
|
||||||
form.className = 'chat-form';
|
form.className = 'chat-form';
|
||||||
|
|
@ -1861,14 +1866,9 @@ function showChatWindow(name) {
|
||||||
socket.send(JSON.stringify({ player_name: name, command: text }));
|
socket.send(JSON.stringify({ player_name: name, command: text }));
|
||||||
input.value = '';
|
input.value = '';
|
||||||
});
|
});
|
||||||
win.appendChild(form);
|
content.appendChild(form);
|
||||||
debugLog('💬 Appending chat window to DOM:', win);
|
|
||||||
document.body.appendChild(win);
|
|
||||||
chatWindows[name] = win;
|
|
||||||
debugLog('💬 Chat window added to DOM, total children:', document.body.children.length);
|
|
||||||
|
|
||||||
// Enable dragging using the global drag system
|
debugLog('Chat window created for:', name);
|
||||||
makeDraggable(win, header);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append a chat message to the correct window
|
// Append a chat message to the correct window
|
||||||
|
|
|
||||||
|
|
@ -363,6 +363,14 @@ body {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.window-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-header {
|
.chat-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
|
||||||
|
|
@ -540,6 +540,14 @@ body {
|
||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.window-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-header {
|
.chat-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue