3dd7b017a6
- Add resetWidgetSizesToDefault() to reset all widgets to default sizes before auto-arrange/reset - Implement continuous expansion algorithm that fills available space up to maxAutoSize limits - Add visible height detection to prevent widgets expanding beyond viewport (no forced scroll) - Update all widget defaultSize and maxAutoSize for optimal 1x1 compact layouts - Info widgets (calendar, weather, temp, clock): 1x1 default, 1x2 max - Location: 2x2 max (was 3x3) - Characters: 3x5 max, moved to 'scene' category (eliminates Social tab) - User Info: 2x1 max (prevents expansion) - User Mood: 1x1 default and max (compact top-right placement) - User Attributes: 3x5 max (fills bottom space) - User Stats: 3x3 max - Fix CSS scaling for 1x1 widgets - Replace viewport-based units with fixed rem values - Reduce icon/graphic sizes to fit with visible text - Add explicit gaps and padding for consistent spacing - Set line-height: 1 to prevent text overflow - Reorganize default layout - Status tab: User Info (2x1) + Mood (1x1 top right) + Stats + Attributes - Scene tab: Info widgets (1x1) + Location + Characters (all on one tab) - Inventory tab: Full inventory widget Auto-arrange and reset now properly size widgets to defaults and expand to fill available space without exceeding visible area.
212 lines
7.0 KiB
JavaScript
212 lines
7.0 KiB
JavaScript
/**
|
|
* User Mood Widget
|
|
*
|
|
* Displays user's current mood emoji and active conditions.
|
|
* Compact widget showing emotional state and status effects.
|
|
*
|
|
* Features:
|
|
* - Large mood emoji (editable)
|
|
* - Conditions/status effects text (editable)
|
|
* - Responsive layout
|
|
*/
|
|
|
|
/**
|
|
* Register User Mood Widget
|
|
* @param {WidgetRegistry} registry - Widget registry instance
|
|
* @param {Object} dependencies - External dependencies
|
|
* @param {Function} dependencies.getExtensionSettings - Get extension settings
|
|
* @param {Function} dependencies.onStatsChange - Callback when stats change
|
|
*/
|
|
export function registerUserMoodWidget(registry, dependencies) {
|
|
const {
|
|
getExtensionSettings,
|
|
onStatsChange
|
|
} = dependencies;
|
|
|
|
registry.register('userMood', {
|
|
name: 'User Mood',
|
|
icon: '😊',
|
|
description: 'Mood emoji and active conditions',
|
|
category: 'user',
|
|
minSize: { w: 1, h: 1 },
|
|
defaultSize: { w: 1, h: 1 },
|
|
maxAutoSize: { w: 1, h: 1 }, // Max size for auto-arrange expansion - stays compact in top right
|
|
requiresSchema: false,
|
|
|
|
/**
|
|
* Render widget content
|
|
* @param {HTMLElement} container - Widget container
|
|
* @param {Object} config - Widget configuration
|
|
*/
|
|
render(container, config = {}) {
|
|
const settings = getExtensionSettings();
|
|
const stats = settings.userStats;
|
|
|
|
// Merge default config
|
|
const finalConfig = {
|
|
showMoodEmoji: true,
|
|
showConditions: true,
|
|
...config
|
|
};
|
|
|
|
// Build HTML
|
|
const html = `
|
|
<div class="rpg-mood">
|
|
${finalConfig.showMoodEmoji ? `<div class="rpg-mood-emoji rpg-editable" contenteditable="true" data-field="mood" title="Click to edit emoji">${stats.mood}</div>` : ''}
|
|
${finalConfig.showConditions ? `<div class="rpg-mood-conditions rpg-editable" contenteditable="true" data-field="conditions" title="Click to edit conditions">${stats.conditions}</div>` : ''}
|
|
</div>
|
|
`;
|
|
|
|
container.innerHTML = html;
|
|
|
|
// Attach event handlers
|
|
attachEventHandlers(container, settings, onStatsChange);
|
|
},
|
|
|
|
/**
|
|
* Get configuration options
|
|
* @returns {Object} Configuration schema
|
|
*/
|
|
getConfig() {
|
|
return {
|
|
showMoodEmoji: {
|
|
type: 'boolean',
|
|
label: 'Show Mood Emoji',
|
|
default: true
|
|
},
|
|
showConditions: {
|
|
type: 'boolean',
|
|
label: 'Show Conditions',
|
|
default: true
|
|
}
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Handle configuration changes
|
|
* @param {HTMLElement} container - Widget container
|
|
* @param {Object} newConfig - New configuration
|
|
*/
|
|
onConfigChange(container, newConfig) {
|
|
this.render(container, newConfig);
|
|
},
|
|
|
|
/**
|
|
* Handle widget resize
|
|
* @param {HTMLElement} container - Widget container
|
|
* @param {number} newW - New width
|
|
* @param {number} newH - New height
|
|
*/
|
|
onResize(container, newW, newH) {
|
|
// Responsive adjustments if needed
|
|
const mood = container.querySelector('.rpg-mood');
|
|
if (!mood) return;
|
|
|
|
// Adjust layout for narrow widgets
|
|
if (newW < 2) {
|
|
mood.style.flexDirection = 'column';
|
|
} else {
|
|
mood.style.flexDirection = 'row';
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Attach event handlers to widget
|
|
* @private
|
|
*/
|
|
function attachEventHandlers(container, settings, onStatsChange) {
|
|
// Handle mood emoji editing
|
|
const moodEmoji = container.querySelector('.rpg-mood-emoji.rpg-editable');
|
|
if (moodEmoji) {
|
|
let originalMood = moodEmoji.textContent.trim();
|
|
|
|
moodEmoji.addEventListener('focus', () => {
|
|
originalMood = moodEmoji.textContent.trim();
|
|
const range = document.createRange();
|
|
range.selectNodeContents(moodEmoji);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
});
|
|
|
|
moodEmoji.addEventListener('blur', () => {
|
|
const value = moodEmoji.textContent.trim() || '😐';
|
|
moodEmoji.textContent = value;
|
|
|
|
if (value !== originalMood) {
|
|
settings.userStats.mood = value;
|
|
if (onStatsChange) {
|
|
onStatsChange('userStats', 'mood', value);
|
|
}
|
|
}
|
|
});
|
|
|
|
moodEmoji.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
moodEmoji.blur();
|
|
}
|
|
if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
moodEmoji.textContent = originalMood;
|
|
moodEmoji.blur();
|
|
}
|
|
});
|
|
|
|
// Prevent paste with formatting
|
|
moodEmoji.addEventListener('paste', (e) => {
|
|
e.preventDefault();
|
|
const text = (e.clipboardData || window.clipboardData).getData('text/plain');
|
|
document.execCommand('insertText', false, text);
|
|
});
|
|
}
|
|
|
|
// Handle conditions editing
|
|
const moodConditions = container.querySelector('.rpg-mood-conditions.rpg-editable');
|
|
if (moodConditions) {
|
|
let originalConditions = moodConditions.textContent.trim();
|
|
|
|
moodConditions.addEventListener('focus', () => {
|
|
originalConditions = moodConditions.textContent.trim();
|
|
const range = document.createRange();
|
|
range.selectNodeContents(moodConditions);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
});
|
|
|
|
moodConditions.addEventListener('blur', () => {
|
|
const value = moodConditions.textContent.trim() || 'None';
|
|
moodConditions.textContent = value;
|
|
|
|
if (value !== originalConditions) {
|
|
settings.userStats.conditions = value;
|
|
if (onStatsChange) {
|
|
onStatsChange('userStats', 'conditions', value);
|
|
}
|
|
}
|
|
});
|
|
|
|
moodConditions.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
moodConditions.blur();
|
|
}
|
|
if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
moodConditions.textContent = originalConditions;
|
|
moodConditions.blur();
|
|
}
|
|
});
|
|
|
|
// Prevent paste with formatting
|
|
moodConditions.addEventListener('paste', (e) => {
|
|
e.preventDefault();
|
|
const text = (e.clipboardData || window.clipboardData).getData('text/plain');
|
|
document.execCommand('insertText', false, text);
|
|
});
|
|
}
|
|
}
|