From 60a8c2f21f2caef0860662d5b33814cffe3fc0b2 Mon Sep 17 00:00:00 2001 From: IDeathByte <41571062+IDeathByte@users.noreply.github.com> Date: Mon, 20 Oct 2025 23:18:06 +0500 Subject: [PATCH 1/6] Fix user icon/fonts scaling scaling fix again :) --- style.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/style.css b/style.css index 8b906b1..1a3eb59 100644 --- a/style.css +++ b/style.css @@ -576,7 +576,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { } .rpg-user-portrait { - width: clamp(24px, 4vh, 32px); + width: clamp(1.7vw, 1.8vw, 1.9vw); height: clamp(24px, 4vh, 32px); border-radius: 50%; border: 2px solid var(--rpg-highlight); @@ -726,7 +726,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { /* User name and level - inline with portrait */ .rpg-user-name { font-weight: 600; - font-size: 1em; + font-size: 0.7vw; color: var(--rpg-text-color); white-space: nowrap; overflow: hidden; @@ -734,14 +734,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld { } .rpg-level-label { - font-size: 1em; + font-size: 0.7vw; font-weight: 600; color: var(--rpg-text-color); opacity: 0.7; } .rpg-level-value { - font-size: 1em; + font-size: 0.7vw; font-weight: 700; color: var(--rpg-highlight-color); padding: clamp(1px, 0.2vh, 2px) 0.375em; From 8fd08bfc28395211ca8a37b42d66e5df2a2100c2 Mon Sep 17 00:00:00 2001 From: IDeathByte <41571062+IDeathByte@users.noreply.github.com> Date: Mon, 20 Oct 2025 23:31:44 +0500 Subject: [PATCH 2/6] Fix fir fix miss height =\ --- style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style.css b/style.css index 1a3eb59..e9e266d 100644 --- a/style.css +++ b/style.css @@ -577,7 +577,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { .rpg-user-portrait { width: clamp(1.7vw, 1.8vw, 1.9vw); - height: clamp(24px, 4vh, 32px); + height: clamp(1.7vw, 1.8vw, 1.9vw); border-radius: 50%; border: 2px solid var(--rpg-highlight); box-shadow: 0 0 8px var(--rpg-highlight); From 55cef9bee2689e334a2b2d1d6fd1a31794700f14 Mon Sep 17 00:00:00 2001 From: Spicy_Marinara Date: Tue, 21 Oct 2025 15:02:49 +0200 Subject: [PATCH 3/6] Fix duplicate tracker entries by prioritizing text format over emoji format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added parsedFields tracking to prevent parsing the same field twice - Split combined if conditions into separate checks for text vs emoji format - Text format (Temperature:, Time:, etc.) is now parsed first and preferred - Emoji format (��️:, πŸ•’:, etc.) only parsed if text format not found - Prevents duplicate entries when AI generates both formats in output - Fixes duplicate Temperature, Time, Location lines in tracker data --- src/systems/rendering/infoBox.js | 227 ++++++++++++++++++++++--------- 1 file changed, 159 insertions(+), 68 deletions(-) diff --git a/src/systems/rendering/infoBox.js b/src/systems/rendering/infoBox.js index 5cf3276..ad3172c 100644 --- a/src/systems/rendering/infoBox.js +++ b/src/systems/rendering/infoBox.js @@ -63,82 +63,146 @@ export function renderInfoBox() { characters: [] }; + // Track which fields we've already parsed to avoid duplicates from mixed formats + const parsedFields = { + date: false, + temperature: false, + time: false, + location: false, + weather: false + }; + for (const line of lines) { // console.log('[RPG Companion] Processing line:', line); // Support both new text format (Date:) and legacy emoji format (πŸ—“οΈ:) - if (line.startsWith('Date:') || line.includes('πŸ—“οΈ:')) { - // console.log('[RPG Companion] β†’ Matched DATE'); - const dateStr = line.replace('Date:', '').replace('πŸ—“οΈ:', '').trim(); - // Parse format: "Weekday, Month Day, Year" or "Weekday, Month, Year" - const dateParts = dateStr.split(',').map(p => p.trim()); - data.weekday = dateParts[0] || ''; - data.month = dateParts[1] || ''; - data.year = dateParts[2] || ''; - data.date = dateStr; - } else if (line.startsWith('Temperature:') || line.includes('🌑️:')) { - // console.log('[RPG Companion] β†’ Matched TEMPERATURE'); - const tempStr = line.replace('Temperature:', '').replace('🌑️:', '').trim(); - data.temperature = tempStr; - // Extract numeric value - const tempMatch = tempStr.match(/(-?\d+)/); - if (tempMatch) { - data.tempValue = parseInt(tempMatch[1]); + // Prioritize text format over emoji format + if (line.startsWith('Date:')) { + if (!parsedFields.date) { + // console.log('[RPG Companion] β†’ Matched DATE (text format)'); + const dateStr = line.replace('Date:', '').trim(); + const dateParts = dateStr.split(',').map(p => p.trim()); + data.weekday = dateParts[0] || ''; + data.month = dateParts[1] || ''; + data.year = dateParts[2] || ''; + data.date = dateStr; + parsedFields.date = true; + } + } else if (line.includes('πŸ—“οΈ:')) { + if (!parsedFields.date) { + // console.log('[RPG Companion] β†’ Matched DATE (emoji format)'); + const dateStr = line.replace('πŸ—“οΈ:', '').trim(); + const dateParts = dateStr.split(',').map(p => p.trim()); + data.weekday = dateParts[0] || ''; + data.month = dateParts[1] || ''; + data.year = dateParts[2] || ''; + data.date = dateStr; + parsedFields.date = true; + } + } else if (line.startsWith('Temperature:')) { + if (!parsedFields.temperature) { + // console.log('[RPG Companion] β†’ Matched TEMPERATURE (text format)'); + const tempStr = line.replace('Temperature:', '').trim(); + data.temperature = tempStr; + const tempMatch = tempStr.match(/(-?\d+)/); + if (tempMatch) { + data.tempValue = parseInt(tempMatch[1]); + } + parsedFields.temperature = true; + } + } else if (line.includes('🌑️:')) { + if (!parsedFields.temperature) { + // console.log('[RPG Companion] β†’ Matched TEMPERATURE (emoji format)'); + const tempStr = line.replace('🌑️:', '').trim(); + data.temperature = tempStr; + const tempMatch = tempStr.match(/(-?\d+)/); + if (tempMatch) { + data.tempValue = parseInt(tempMatch[1]); + } + parsedFields.temperature = true; + } + } else if (line.startsWith('Time:')) { + if (!parsedFields.time) { + // console.log('[RPG Companion] β†’ Matched TIME (text format)'); + const timeStr = line.replace('Time:', '').trim(); + data.time = timeStr; + const timeParts = timeStr.split('β†’').map(t => t.trim()); + data.timeStart = timeParts[0] || ''; + data.timeEnd = timeParts[1] || ''; + parsedFields.time = true; + } + } else if (line.includes('πŸ•’:')) { + if (!parsedFields.time) { + // console.log('[RPG Companion] β†’ Matched TIME (emoji format)'); + const timeStr = line.replace('πŸ•’:', '').trim(); + data.time = timeStr; + const timeParts = timeStr.split('β†’').map(t => t.trim()); + data.timeStart = timeParts[0] || ''; + data.timeEnd = timeParts[1] || ''; + parsedFields.time = true; + } + } else if (line.startsWith('Location:')) { + if (!parsedFields.location) { + // console.log('[RPG Companion] β†’ Matched LOCATION (text format)'); + data.location = line.replace('Location:', '').trim(); + parsedFields.location = true; + } + } else if (line.includes('πŸ—ΊοΈ:')) { + if (!parsedFields.location) { + // console.log('[RPG Companion] β†’ Matched LOCATION (emoji format)'); + data.location = line.replace('πŸ—ΊοΈ:', '').trim(); + parsedFields.location = true; } - } else if (line.startsWith('Time:') || line.includes('πŸ•’:')) { - // console.log('[RPG Companion] β†’ Matched TIME'); - const timeStr = line.replace('Time:', '').replace('πŸ•’:', '').trim(); - data.time = timeStr; - // Parse "HH:MM β†’ HH:MM" format - const timeParts = timeStr.split('β†’').map(t => t.trim()); - data.timeStart = timeParts[0] || ''; - data.timeEnd = timeParts[1] || ''; - } else if (line.startsWith('Location:') || line.includes('πŸ—ΊοΈ:')) { - // console.log('[RPG Companion] β†’ Matched LOCATION'); - data.location = line.replace('Location:', '').replace('πŸ—ΊοΈ:', '').trim(); } else if (line.startsWith('Weather:')) { - // New text format: Weather: [Emoji], [Forecast] - const weatherStr = line.replace('Weather:', '').trim(); - const weatherParts = weatherStr.split(',').map(p => p.trim()); - data.weatherEmoji = weatherParts[0] || ''; - data.weatherForecast = weatherParts[1] || ''; + if (!parsedFields.weather) { + // New text format: Weather: [Emoji], [Forecast] + const weatherStr = line.replace('Weather:', '').trim(); + const weatherParts = weatherStr.split(',').map(p => p.trim()); + data.weatherEmoji = weatherParts[0] || ''; + data.weatherForecast = weatherParts[1] || ''; + parsedFields.weather = true; + } } else { - // Check if it's a weather line - // Since \p{Emoji} doesn't work reliably, use a simpler approach - const hasColon = line.includes(':'); - const notInfoBox = !line.includes('Info Box'); - const notDivider = !line.includes('---'); - const notCodeFence = !line.trim().startsWith('```'); + // Check if it's a legacy weather line (emoji format) + // Only parse if we haven't already found weather in text format + if (!parsedFields.weather) { + // Since \p{Emoji} doesn't work reliably, use a simpler approach + const hasColon = line.includes(':'); + const notInfoBox = !line.includes('Info Box'); + const notDivider = !line.includes('---'); + const notCodeFence = !line.trim().startsWith('```'); - // console.log('[RPG Companion] β†’ Checking weather conditions:', { - // line: line, - // hasColon: hasColon, - // notInfoBox: notInfoBox, - // notDivider: notDivider - // }); + // console.log('[RPG Companion] β†’ Checking weather conditions:', { + // line: line, + // hasColon: hasColon, + // notInfoBox: notInfoBox, + // notDivider: notDivider + // }); - if (hasColon && notInfoBox && notDivider && notCodeFence && line.trim().length > 0) { - // Match format: [Weather Emoji]: [Forecast] - // Capture everything before colon as emoji, everything after as forecast - // console.log('[RPG Companion] β†’ Testing WEATHER match for:', line); - const weatherMatch = line.match(/^\s*([^:]+):\s*(.+)$/); - if (weatherMatch) { - const potentialEmoji = weatherMatch[1].trim(); - const forecast = weatherMatch[2].trim(); + if (hasColon && notInfoBox && notDivider && notCodeFence && line.trim().length > 0) { + // Match format: [Weather Emoji]: [Forecast] + // Capture everything before colon as emoji, everything after as forecast + // console.log('[RPG Companion] β†’ Testing WEATHER match for:', line); + const weatherMatch = line.match(/^\s*([^:]+):\s*(.+)$/); + if (weatherMatch) { + const potentialEmoji = weatherMatch[1].trim(); + const forecast = weatherMatch[2].trim(); - // If the first part is short (likely emoji), treat as weather - if (potentialEmoji.length <= 5) { - data.weatherEmoji = potentialEmoji; - data.weatherForecast = forecast; - // console.log('[RPG Companion] βœ“ Weather parsed:', data.weatherEmoji, data.weatherForecast); + // If the first part is short (likely emoji), treat as weather + if (potentialEmoji.length <= 5) { + data.weatherEmoji = potentialEmoji; + data.weatherForecast = forecast; + parsedFields.weather = true; + // console.log('[RPG Companion] βœ“ Weather parsed:', data.weatherEmoji, data.weatherForecast); + } else { + // console.log('[RPG Companion] βœ— First part too long for emoji:', potentialEmoji); + } } else { - // console.log('[RPG Companion] βœ— First part too long for emoji:', potentialEmoji); + // console.log('[RPG Companion] βœ— Weather regex did not match'); } } else { - // console.log('[RPG Companion] βœ— Weather regex did not match'); + // console.log('[RPG Companion] β†’ No match for this line'); } - } else { - // console.log('[RPG Companion] β†’ No match for this line'); } } } @@ -157,14 +221,15 @@ export function renderInfoBox() { let html = '
'; // Calendar widget - always show (editable even if empty) + // Display abbreviated version but allow editing full value const monthShort = data.month ? data.month.substring(0, 3).toUpperCase() : 'MON'; const weekdayShort = data.weekday ? data.weekday.substring(0, 3).toUpperCase() : 'DAY'; const yearDisplay = data.year || 'YEAR'; html += `
-
${monthShort}
-
${weekdayShort}
-
${yearDisplay}
+
${monthShort}
+
${weekdayShort}
+
${yearDisplay}
`; @@ -239,11 +304,32 @@ export function renderInfoBox() { // Add event handlers for editable Info Box fields $infoBoxContainer.find('.rpg-editable').on('blur', function() { - const field = $(this).data('field'); - const value = $(this).text().trim(); + const $this = $(this); + const field = $this.data('field'); + const value = $this.text().trim(); + + // For date fields, update the data-full-value immediately + if (field === 'month' || field === 'weekday' || field === 'year') { + $this.data('full-value', value); + // Update the display to show abbreviated version + if (field === 'month' || field === 'weekday') { + $this.text(value.substring(0, 3).toUpperCase()); + } else { + $this.text(value); + } + } + updateInfoBoxField(field, value); }); + // For date fields, show full value on focus + $infoBoxContainer.find('[data-field="month"], [data-field="weekday"], [data-field="year"]').on('focus', function() { + const fullValue = $(this).data('full-value'); + if (fullValue) { + $(this).text(fullValue); + } + }); + // Remove updating class after animation if (extensionSettings.enableAnimations) { setTimeout(() => $infoBoxContainer.removeClass('rpg-content-updating'), 500); @@ -512,5 +598,10 @@ export function updateInfoBoxField(field, value) { } saveChatData(); - renderInfoBox(); + + // Only re-render if NOT editing date fields + // Date fields will update on next tracker generation to avoid losing user input + if (field !== 'month' && field !== 'weekday' && field !== 'year') { + renderInfoBox(); + } } From 5b7928b4430d53da65427a03edc1274193a634e5 Mon Sep 17 00:00:00 2001 From: Spicy_Marinara Date: Wed, 22 Oct 2025 00:38:35 +0200 Subject: [PATCH 4/6] Display end time instead of start time in Info Box clock widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Changed time display to show timeEnd (second time in range) instead of timeStart - Clock now displays 14:22 instead of 14:07 when time format is '14:07 β†’ 14:22' - Falls back to timeStart if timeEnd not available, then to '12:00' default --- src/systems/rendering/infoBox.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/systems/rendering/infoBox.js b/src/systems/rendering/infoBox.js index ad3172c..6aebea6 100644 --- a/src/systems/rendering/infoBox.js +++ b/src/systems/rendering/infoBox.js @@ -261,7 +261,8 @@ export function renderInfoBox() { `; // Time widget - always show (editable even if empty) - const timeDisplay = data.timeStart || '12:00'; + // Display the end time (second time in range) if available, otherwise start time + const timeDisplay = data.timeEnd || data.timeStart || '12:00'; // Parse time for clock hands const timeMatch = timeDisplay.match(/(\d+):(\d+)/); let hourAngle = 0; From 83576a9073b816a02e294efa1dd98f6f99e64141 Mon Sep 17 00:00:00 2001 From: Spicy_Marinara Date: Wed, 22 Oct 2025 00:52:43 +0200 Subject: [PATCH 5/6] Revert "Merge pull request #16 from paperboygold/main" This reverts commit c1b2520fa11ac2ea36fe8d474b9de56d780f9d8a, reversing changes made to c6a1352aae680cc7fe1b59a61f3df482ffe8f729. --- index.js | 38 +----- src/core/config.js | 4 - src/core/state.js | 4 - src/systems/ui/layout.js | 13 +- src/systems/ui/mobile.js | 255 ++------------------------------------- style.css | 112 +---------------- 6 files changed, 21 insertions(+), 405 deletions(-) diff --git a/index.js b/index.js index 037592d..160c515 100644 --- a/index.js +++ b/index.js @@ -98,8 +98,7 @@ import { setupMobileTabs, removeMobileTabs, setupMobileKeyboardHandling, - setupContentEditableScrolling, - setupRefreshButtonDrag + setupContentEditableScrolling } from './src/systems/ui/mobile.js'; import { setupDesktopTabs, @@ -210,14 +209,6 @@ async function initUI() { `; $('body').append(mobileToggleHtml); - // Add mobile refresh button (same pattern as toggle button) - const mobileRefreshHtml = ` - - `; - $('body').append(mobileRefreshHtml); - // Cache UI elements using state setters setPanelContainer($('#rpg-companion-panel')); setUserStatsContainer($('#rpg-user-stats')); @@ -306,34 +297,12 @@ async function initUI() { toggleAnimations(); }); - // Bind to both desktop and mobile refresh buttons - $('#rpg-manual-update, #rpg-manual-update-mobile').on('click', async function() { - // Get mobile button reference - const $mobileBtn = $('#rpg-manual-update-mobile'); - - // Skip if we just finished dragging the mobile button - if ($mobileBtn.data('just-dragged')) { - console.log('[RPG Companion] Click blocked - just finished dragging refresh button'); - return; - } - + $('#rpg-manual-update').on('click', async function() { if (!extensionSettings.enabled) { // console.log('[RPG Companion] Extension is disabled. Please enable it in the Extensions tab.'); return; } - - // Remove focus to prevent sticky black state on mobile - $(this).blur(); - - // Add spinning animation to mobile button - $mobileBtn.addClass('spinning'); - - try { - await updateRPGData(renderUserStats, renderInfoBox, renderThoughts, renderInventory); - } finally { - // Remove spinning animation when done - $mobileBtn.removeClass('spinning'); - } + await updateRPGData(renderUserStats, renderInfoBox, renderThoughts, renderInventory); }); $('#rpg-stat-bar-color-low').on('change', function() { @@ -453,7 +422,6 @@ async function initUI() { setupPlotButtons(sendPlotProgression); setupMobileKeyboardHandling(); setupContentEditableScrolling(); - setupRefreshButtonDrag(); initInventoryEventListeners(); } diff --git a/src/core/config.js b/src/core/config.js index 2fe9030..93d2495 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -49,10 +49,6 @@ export const defaultSettings = { top: 'calc(var(--topBarBlockSize) + 60px)', right: '12px' }, // Saved position for mobile FAB button - mobileRefreshPosition: { - bottom: '80px', - right: '20px' - }, // Saved position for mobile refresh button userStats: { health: 100, satiety: 100, diff --git a/src/core/state.js b/src/core/state.js index c837bba..4e86ae3 100644 --- a/src/core/state.js +++ b/src/core/state.js @@ -37,10 +37,6 @@ export let extensionSettings = { top: 'calc(var(--topBarBlockSize) + 60px)', right: '12px' }, // Saved position for mobile FAB button - mobileRefreshPosition: { - bottom: '80px', - right: '20px' - }, // Saved position for mobile refresh button userStats: { health: 100, satiety: 100, diff --git a/src/systems/ui/layout.js b/src/systems/ui/layout.js index b88b5f0..9993154 100644 --- a/src/systems/ui/layout.js +++ b/src/systems/ui/layout.js @@ -34,9 +34,6 @@ export function closeMobilePanelWithAnimation() { $panel.removeClass('rpg-mobile-open').addClass('rpg-mobile-closing'); $mobileToggle.removeClass('active'); - // Trigger event for other components (like refresh button) - $(document).trigger('rpg-panel-toggled', { isOpen: false }); - // Wait for animation to complete before hiding $panel.one('animationend', function() { $panel.removeClass('rpg-mobile-closing'); @@ -130,9 +127,6 @@ export function setupCollapseToggle() { const $overlay = $('
'); $('body').append($overlay); - // Trigger event for other components (like refresh button) - $(document).trigger('rpg-panel-toggled', { isOpen: true }); - // Debug: Check state after animation should complete setTimeout(() => { console.log('[RPG Mobile] 500ms after opening:', { @@ -273,13 +267,10 @@ export function applyPanelPosition() { */ export function updateGenerationModeUI() { if (extensionSettings.generationMode === 'together') { - // In "together" mode, hide both update buttons + // In "together" mode, manual update button is hidden $('#rpg-manual-update').hide(); - $('#rpg-manual-update-mobile').hide(); } else { - // In "separate" mode, show both buttons - // (CSS media queries control which one is visible based on viewport) + // In "separate" mode, manual update button is visible $('#rpg-manual-update').show(); - $('#rpg-manual-update-mobile').show(); } } diff --git a/src/systems/ui/mobile.js b/src/systems/ui/mobile.js index b3f7b95..f2648b3 100644 --- a/src/systems/ui/mobile.js +++ b/src/systems/ui/mobile.js @@ -278,9 +278,6 @@ export function setupMobileToggle() { $('body').append($overlay); $mobileToggle.addClass('active'); - // Trigger event for other components (like refresh button) - $(document).trigger('rpg-panel-toggled', { isOpen: true }); - // Close when clicking overlay $overlay.on('click', function() { closeMobilePanelWithAnimation(); @@ -313,9 +310,6 @@ export function setupMobileToggle() { $('body').append($overlay); $mobileToggle.addClass('active'); - // Trigger event for other components (like refresh button) - $(document).trigger('rpg-panel-toggled', { isOpen: true }); - $overlay.on('click', function() { console.log('[RPG Mobile] Overlay clicked - closing panel'); closeMobilePanelWithAnimation(); @@ -440,41 +434,32 @@ export function setupMobileToggle() { * Constrains the mobile FAB button to viewport bounds with top-bar awareness. * Only runs when button is in user-controlled state (mobileFabPosition exists). * Ensures button never goes behind the top bar or outside viewport edges. - * @param {jQuery} $button - Optional button element (defaults to mobile toggle) */ -export function constrainFabToViewport($button = null) { - // Default to mobile toggle if no button specified - if (!$button) { - $button = $('#rpg-mobile-toggle'); - } - - if ($button.length === 0) return; - - // Determine which position setting to check based on button ID - const isRefreshButton = $button.attr('id') === 'rpg-manual-update-mobile'; - const positionSetting = isRefreshButton ? 'mobileRefreshPosition' : 'mobileFabPosition'; - +export function constrainFabToViewport() { // Only constrain if user has set a custom position - if (!extensionSettings[positionSetting]) { + if (!extensionSettings.mobileFabPosition) { console.log('[RPG Mobile] Skipping viewport constraint - using CSS defaults'); return; } + const $mobileToggle = $('#rpg-mobile-toggle'); + if ($mobileToggle.length === 0) return; + // Skip if button is not visible - if (!$button.is(':visible')) { + if (!$mobileToggle.is(':visible')) { console.log('[RPG Mobile] Skipping viewport constraint - button not visible'); return; } // Get current position - const offset = $button.offset(); + const offset = $mobileToggle.offset(); if (!offset) return; let currentX = offset.left; let currentY = offset.top; - const buttonWidth = $button.outerWidth(); - const buttonHeight = $button.outerHeight(); + const buttonWidth = $mobileToggle.outerWidth(); + const buttonHeight = $mobileToggle.outerHeight(); // Get top bar height from CSS variable (fallback to 50px if not set) const topBarHeight = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--topBarBlockSize')) || 50; @@ -500,15 +485,15 @@ export function constrainFabToViewport($button = null) { }); // Apply new position - $button.css({ + $mobileToggle.css({ left: newX + 'px', top: newY + 'px', right: 'auto', bottom: 'auto' }); - // Save corrected position to appropriate setting - extensionSettings[positionSetting] = { + // Save corrected position + extensionSettings.mobileFabPosition = { left: newX + 'px', top: newY + 'px' }; @@ -731,219 +716,3 @@ export function setupContentEditableScrolling() { }, 300); }); } - -/** - * Sets up the mobile refresh button with drag functionality. - * Same pattern as mobile toggle button. - * Tap = refresh, drag = reposition - */ -export function setupRefreshButtonDrag() { - const $refreshBtn = $('#rpg-manual-update-mobile'); - - if ($refreshBtn.length === 0) { - console.warn('[RPG Mobile] Refresh button not found in DOM'); - return; - } - - console.log('[RPG Mobile] setupRefreshButtonDrag called'); - - // Load and apply saved position - if (extensionSettings.mobileRefreshPosition) { - const pos = extensionSettings.mobileRefreshPosition; - console.log('[RPG Mobile] Loading saved refresh button position:', pos); - - // Apply saved position - if (pos.top) $refreshBtn.css('top', pos.top); - if (pos.right) $refreshBtn.css('right', pos.right); - if (pos.bottom) $refreshBtn.css('bottom', pos.bottom); - if (pos.left) $refreshBtn.css('left', pos.left); - - // Constrain to viewport after position is applied - requestAnimationFrame(() => constrainFabToViewport($refreshBtn)); - } - - // Touch/drag state - let isDragging = false; - let touchStartTime = 0; - let touchStartX = 0; - let touchStartY = 0; - let buttonStartX = 0; - let buttonStartY = 0; - const LONG_PRESS_DURATION = 200; - const MOVE_THRESHOLD = 10; - let rafId = null; - let pendingX = null; - let pendingY = null; - - // Update position using requestAnimationFrame - function updatePosition() { - if (pendingX !== null && pendingY !== null) { - $refreshBtn.css({ - left: pendingX + 'px', - top: pendingY + 'px', - right: 'auto', - bottom: 'auto' - }); - pendingX = null; - pendingY = null; - } - rafId = null; - } - - // Touch start - $refreshBtn.on('touchstart', function(e) { - const touch = e.originalEvent.touches[0]; - touchStartTime = Date.now(); - touchStartX = touch.clientX; - touchStartY = touch.clientY; - - const offset = $refreshBtn.offset(); - buttonStartX = offset.left; - buttonStartY = offset.top; - - isDragging = false; - }); - - // Touch move - $refreshBtn.on('touchmove', function(e) { - const touch = e.originalEvent.touches[0]; - const deltaX = touch.clientX - touchStartX; - const deltaY = touch.clientY - touchStartY; - const timeSinceStart = Date.now() - touchStartTime; - const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); - - if (!isDragging && (timeSinceStart > LONG_PRESS_DURATION || distance > MOVE_THRESHOLD)) { - isDragging = true; - $refreshBtn.addClass('dragging'); - } - - if (isDragging) { - e.preventDefault(); - - let newX = buttonStartX + deltaX; - let newY = buttonStartY + deltaY; - - const buttonWidth = $refreshBtn.outerWidth(); - const buttonHeight = $refreshBtn.outerHeight(); - - const minX = 10; - const maxX = window.innerWidth - buttonWidth - 10; - const minY = 10; - const maxY = window.innerHeight - buttonHeight - 10; - - newX = Math.max(minX, Math.min(maxX, newX)); - newY = Math.max(minY, Math.min(maxY, newY)); - - pendingX = newX; - pendingY = newY; - if (!rafId) { - rafId = requestAnimationFrame(updatePosition); - } - } - }); - - // Touch end - $refreshBtn.on('touchend', function(e) { - if (isDragging) { - // Save new position - const offset = $refreshBtn.offset(); - const newPosition = { - left: offset.left + 'px', - top: offset.top + 'px' - }; - - extensionSettings.mobileRefreshPosition = newPosition; - saveSettings(); - - setTimeout(() => { - $refreshBtn.removeClass('dragging'); - }, 50); - - // Set flag to prevent click handler from firing - $refreshBtn.data('just-dragged', true); - setTimeout(() => { - $refreshBtn.data('just-dragged', false); - }, 100); - - isDragging = false; - } - }); - - // Mouse support for desktop - let mouseDown = false; - - $refreshBtn.on('mousedown', function(e) { - e.preventDefault(); - touchStartTime = Date.now(); - touchStartX = e.clientX; - touchStartY = e.clientY; - - const offset = $refreshBtn.offset(); - buttonStartX = offset.left; - buttonStartY = offset.top; - - mouseDown = true; - isDragging = false; - }); - - $(document).on('mousemove', function(e) { - if (!mouseDown) return; - - const deltaX = e.clientX - touchStartX; - const deltaY = e.clientY - touchStartY; - const timeSinceStart = Date.now() - touchStartTime; - const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); - - if (!isDragging && (timeSinceStart > LONG_PRESS_DURATION || distance > MOVE_THRESHOLD)) { - isDragging = true; - $refreshBtn.addClass('dragging'); - } - - if (isDragging) { - let newX = buttonStartX + deltaX; - let newY = buttonStartY + deltaY; - - const buttonWidth = $refreshBtn.outerWidth(); - const buttonHeight = $refreshBtn.outerHeight(); - - const minX = 10; - const maxX = window.innerWidth - buttonWidth - 10; - const minY = 10; - const maxY = window.innerHeight - buttonHeight - 10; - - newX = Math.max(minX, Math.min(maxX, newX)); - newY = Math.max(minY, Math.min(maxY, newY)); - - pendingX = newX; - pendingY = newY; - if (!rafId) { - rafId = requestAnimationFrame(updatePosition); - } - } - }); - - $(document).on('mouseup', function(e) { - if (mouseDown && isDragging) { - const offset = $refreshBtn.offset(); - const newPosition = { - left: offset.left + 'px', - top: offset.top + 'px' - }; - - extensionSettings.mobileRefreshPosition = newPosition; - saveSettings(); - - setTimeout(() => { - $refreshBtn.removeClass('dragging'); - }, 50); - - $refreshBtn.data('just-dragged', true); - setTimeout(() => { - $refreshBtn.data('just-dragged', false); - }, 100); - } - - mouseDown = false; - isDragging = false; - }); -} diff --git a/style.css b/style.css index 8ecf0b0..e9e266d 100644 --- a/style.css +++ b/style.css @@ -744,7 +744,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { font-size: 0.7vw; font-weight: 700; color: var(--rpg-highlight-color); - padding: 0 0.375em; + padding: clamp(1px, 0.2vh, 2px) 0.375em; background: var(--rpg-accent-color); border-radius: clamp(2px, 0.3vh, 3px); border: 1px solid var(--rpg-highlight-color); @@ -2670,7 +2670,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { } /* ============================================ - MANUAL UPDATE BUTTON (Desktop) + MANUAL UPDATE BUTTON ============================================ */ .rpg-manual-update-btn { width: 100%; @@ -2707,64 +2707,6 @@ body:has(.rpg-panel.rpg-position-left) #sheld { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); } -/* ============================================ - MOBILE REFRESH BUTTON (FAB - Same pattern as toggle) - ============================================ */ -.rpg-mobile-refresh { - display: none; - align-items: center; - justify-content: center; - position: fixed; - /* Position set by JavaScript based on saved settings */ - width: 44px; - height: 44px; - border-radius: 50%; - background: var(--SmartThemeBlurTintColor); - border: 2px solid var(--SmartThemeBorderColor); - color: var(--rpg-text, #ecf0f1); - font-size: 1.85vw; - cursor: grab; - z-index: 1001; /* Above panel (1000) but below mobile toggle (10002) */ - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - transition: opacity 0.3s ease, transform 0.2s ease, top 0.3s ease, left 0.3s ease, right 0.3s ease, bottom 0.3s ease; - user-select: none; - -webkit-user-select: none; - will-change: top, left; -} - -/* Disable transitions while actively dragging */ -.rpg-mobile-refresh.dragging { - transition: none; - cursor: grabbing; -} - -.rpg-mobile-refresh:hover { - transform: scale(1.1); - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4); -} - -.rpg-mobile-refresh:active { - transform: scale(0.95); -} - -/* Spinning animation when refreshing */ -.rpg-mobile-refresh.spinning i { - animation: rpg-spin 0.8s linear infinite; -} - -.rpg-mobile-refresh i { - pointer-events: none; -} - -@keyframes rpg-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - /* ============================================ SETTINGS BUTTON ============================================ */ @@ -3350,31 +3292,6 @@ body:has(.rpg-panel.rpg-position-left) #sheld { /* Mobile-specific panel behavior - matches SillyTavern's 1000px breakpoint */ /* CACHE BUST v2025-01-16 */ -/* Mobile refresh button visibility - opposite of toggle */ -@media (max-width: 1000px) { - /* Show mobile refresh button */ - .rpg-mobile-refresh { - display: flex; - } - - /* Hide desktop refresh button */ - .rpg-manual-update-btn { - display: none !important; - } - - /* Show refresh button when panel is open (opposite of toggle) */ - body:has(.rpg-panel.rpg-mobile-open) .rpg-mobile-refresh { - opacity: 1; - pointer-events: auto; - } - - /* Hide refresh button when panel is closed */ - .rpg-mobile-refresh { - opacity: 0; - pointer-events: none; - } -} - @media (max-width: 1000px) { /* ======================================== MOBILE PANEL FOUNDATION @@ -3699,9 +3616,6 @@ body:has(.rpg-panel.rpg-position-left) #sheld { .rpg-calendar-day { font-size: clamp(11px, 2.9vw, 14px) !important; - min-height: 3em !important; /* Ensure enough height for content to center */ - padding: 0.75em 0.5em !important; /* More vertical padding on mobile */ - line-height: 1.2 !important; /* Tighter line height */ } .rpg-calendar-year { @@ -3839,20 +3753,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld { grid-row: 3; display: flex; flex-direction: column; - gap: 3px !important; /* Reduced from 6px for more compact display */ + gap: 6px; min-width: 0; - padding: 4px 0.375em !important; /* Reduced vertical padding */ } /* Make mood text readable on mobile */ .rpg-mood-conditions { font-size: clamp(11px, 2.8vw, 14px); - line-height: 1.2 !important; /* Tighter line height */ - } - - /* Smaller emoji on mobile */ - .rpg-mood-emoji { - font-size: clamp(14px, 3.5vw, 18px) !important; /* Slightly smaller */ + line-height: 1.3; } /* Attributes - right side, rows 4-6 aligned with mood */ @@ -4052,11 +3960,6 @@ body:has(.rpg-panel.rpg-position-left) #sheld { font-size: clamp(20px, 5.1vw, 26px) !important; } - /* Larger mobile refresh icon (same as toggle) */ - .rpg-mobile-refresh { - font-size: clamp(20px, 5.1vw, 26px) !important; - } - /* ======================================== MOBILE SETTINGS POPUP ======================================== */ @@ -4156,13 +4059,6 @@ body:has(.rpg-panel.rpg-position-left) #sheld { min-height: 2.75rem; } - /* Exception: Level value should stay compact */ - .rpg-level-value.rpg-editable { - padding: 0 0.375em; - min-height: auto; - line-height: 1.2; - } - /* Larger close buttons */ .rpg-thought-close { min-width: 2.75rem; From 8ad349c1d1bca8820f73e34cf11d9329ac09939d Mon Sep 17 00:00:00 2001 From: Spicy_Marinara Date: Wed, 22 Oct 2025 00:53:32 +0200 Subject: [PATCH 6/6] Bump version to 1.1.0 --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 0ed9fec..19e8b1f 100644 --- a/manifest.json +++ b/manifest.json @@ -6,6 +6,6 @@ "js": "index.js", "css": "style.css", "author": "Marysia", - "version": "1.0.0", + "version": "1.1.0", "homePage": "https://github.com/SpicyMarinara/rpg-companion-sillytavern" }