From f0f04297f7125e541c5a97a0f9990a528fdfed5d Mon Sep 17 00:00:00 2001 From: Lucas 'Paperboy' Rose-Winters Date: Thu, 23 Oct 2025 22:18:02 +1100 Subject: [PATCH] fix(userInfo): fix avatar 404 errors and improve layout/scaling **Avatar 404 Fix:** - Add fallback to getUserAvatar() call to use FALLBACK_AVATAR_DATA_URI when user avatar is missing - Prevents 404 errors on app startup or when no character is selected **Layout & Space Utilization Improvements:** - Implement flexible hybrid layout system: - 1 column (1x1): Centered large avatar (3rem) with text below - 2+ columns (2x1+): Side-by-side (avatar 2.5rem left, text right) - Replace rigid horizontal layout with adaptive container - Add layout classes: rpg-layout-vertical, rpg-layout-horizontal - Trigger onResize on initial render for correct layout **Better Styling:** - Increase avatar size: 2.5rem-3rem (was 1.2rem) - Increase font sizes: 0.9rem name, 0.85rem level (was 0.75rem) - Improve text hierarchy with proper containers - Add proper spacing and alignment for both layouts - Remove awkward vertical stacking of "Name | LVL 1" - Text now stacks cleanly: "Name" on one line, "LVL X" on another The widget now uses space efficiently, displays a prominent avatar, and adapts intelligently to different widget sizes. --- .../dashboard/widgets/userInfoWidget.js | 59 ++++++---- style.css | 105 ++++++++++++++++-- 2 files changed, 135 insertions(+), 29 deletions(-) diff --git a/src/systems/dashboard/widgets/userInfoWidget.js b/src/systems/dashboard/widgets/userInfoWidget.js index 8fbbb48..c4dfb0f 100644 --- a/src/systems/dashboard/widgets/userInfoWidget.js +++ b/src/systems/dashboard/widgets/userInfoWidget.js @@ -26,6 +26,7 @@ export function registerUserInfoWidget(registry, dependencies) { const { getContext, getUserAvatar, + getFallbackAvatar, getExtensionSettings, onStatsChange } = dependencies; @@ -49,7 +50,7 @@ export function registerUserInfoWidget(registry, dependencies) { const settings = getExtensionSettings(); const context = getContext(); const userName = context.name1; - const userPortrait = getUserAvatar(); + const userPortrait = getUserAvatar() || getFallbackAvatar(); // Merge default config const finalConfig = { @@ -59,16 +60,19 @@ export function registerUserInfoWidget(registry, dependencies) { ...config }; - // Build HTML + // Build HTML with flexible layout structure const html = ` -
+ `; @@ -76,6 +80,11 @@ export function registerUserInfoWidget(registry, dependencies) { // Attach event handlers attachEventHandlers(container, settings, onStatsChange); + + // Set initial layout based on current config size + if (config.w !== undefined && config.h !== undefined) { + this.onResize(container, config.w, config.h); + } }, /** @@ -114,21 +123,33 @@ export function registerUserInfoWidget(registry, dependencies) { /** * Handle widget resize * @param {HTMLElement} container - Widget container - * @param {number} newW - New width - * @param {number} newH - New height + * @param {number} newW - New width (grid columns) + * @param {number} newH - New height (grid rows) */ onResize(container, newW, newH) { - // Responsive adjustments if needed - const infoRow = container.querySelector('.rpg-user-info-row'); - if (!infoRow) return; + const infoContainer = container.querySelector('.rpg-user-info-container'); + const portrait = container.querySelector('.rpg-user-portrait'); + if (!infoContainer) return; - // Stack vertically on very narrow widgets + // Flexible hybrid layout based on width: + // - 1 column (1x1, 1x2): Centered avatar with text below + // - 2+ columns: Side-by-side (avatar left, text right) if (newW < 2) { - infoRow.style.flexDirection = 'column'; - infoRow.style.alignItems = 'center'; + // Compact vertical layout: centered large avatar with text below + infoContainer.classList.add('rpg-layout-vertical'); + infoContainer.classList.remove('rpg-layout-horizontal'); + if (portrait) { + portrait.style.width = '3rem'; + portrait.style.height = '3rem'; + } } else { - infoRow.style.flexDirection = 'row'; - infoRow.style.alignItems = 'center'; + // Horizontal layout: avatar left, text right + infoContainer.classList.add('rpg-layout-horizontal'); + infoContainer.classList.remove('rpg-layout-vertical'); + if (portrait) { + portrait.style.width = '2.5rem'; + portrait.style.height = '2.5rem'; + } } } }); diff --git a/style.css b/style.css index aa4118f..06258c8 100644 --- a/style.css +++ b/style.css @@ -1243,21 +1243,106 @@ body:has(.rpg-panel.rpg-position-left) #sheld { font-size: 0.7rem; } -/* User info - rem for typography */ -.rpg-widget .rpg-user-info-row { - font-size: 0.75rem; - gap: 0.4rem; +/* User info widget - responsive layout */ +.rpg-user-info-container { + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; + height: 100%; + width: 100%; + padding: 0.5rem; } -.rpg-widget .rpg-user-name, -.rpg-widget .rpg-level-label, -.rpg-widget .rpg-level-value { - font-size: 0.75rem; +/* Vertical layout (1 column): centered avatar with text below */ +.rpg-user-info-container.rpg-layout-vertical { + flex-direction: column; + gap: 0.5rem; } +/* Horizontal layout (2+ columns): avatar left, text right */ +.rpg-user-info-container.rpg-layout-horizontal { + flex-direction: row; + justify-content: flex-start; +} + +/* User portrait/avatar */ .rpg-widget .rpg-user-portrait { - width: 1.2rem; - height: 1.2rem; + width: 2.5rem; + height: 2.5rem; + border-radius: 50%; + border: 2px solid var(--rpg-highlight); + box-shadow: 0 0 8px var(--rpg-highlight); + object-fit: cover; + transition: transform 0.3s ease; + flex-shrink: 0; +} + +.rpg-widget .rpg-user-portrait:hover { + transform: scale(1.1) rotate(5deg); +} + +/* Text container */ +.rpg-user-info-text { + display: flex; + flex-direction: column; + gap: 0.2rem; + align-items: flex-start; +} + +.rpg-layout-vertical .rpg-user-info-text { + align-items: center; + text-align: center; +} + +/* User name */ +.rpg-user-name { + font-weight: 600; + font-size: 0.9rem; + color: var(--rpg-text); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; +} + +/* Level container */ +.rpg-user-level { + display: flex; + align-items: center; + gap: 0.3rem; +} + +/* Level label and value */ +.rpg-level-label { + font-size: 0.75rem; + font-weight: 600; + color: var(--rpg-text); + opacity: 0.7; +} + +.rpg-level-value { + font-size: 0.85rem; + font-weight: 700; + color: var(--rpg-highlight); + padding: 0.15rem 0.4rem; + background: rgba(0, 0, 0, 0.2); + border-radius: 0.25rem; + min-width: 1.5rem; + text-align: center; + cursor: text; + transition: all 0.2s ease; +} + +.rpg-level-value:hover { + background: var(--rpg-highlight); + color: var(--rpg-bg); +} + +.rpg-level-value:focus { + outline: 2px solid var(--rpg-highlight); + outline-offset: 1px; + background: var(--rpg-bg); } /* Stat bars - rem for text, vh for bar height */