fix: copy mobile toggle pattern for refresh button

- Moved refresh button creation from template.html to index.js (appended to body)
- Created new CSS class .rpg-mobile-refresh (exact copy of .rpg-mobile-toggle pattern)
- Uses opacity for show/hide instead of display (CSS controls visibility based on panel state)
- Show when panel open (body:has(.rpg-panel.rpg-mobile-open))
- Hide when panel closed (opacity: 0, pointer-events: none)
- Updated constrainFabToViewport() to accept optional button parameter
- Automatically detects which button and uses correct settings (mobileFabPosition or mobileRefreshPosition)
- Simplified updateGenerationModeUI() - CSS handles visibility
- Kept full drag functionality with touch and mouse support
- Button positioned via JavaScript with saved position
- z-index: 1001 (above panel, below toggle at 10002)
This commit is contained in:
Lucas 'Paperboy' Rose-Winters
2025-10-21 21:57:37 +11:00
parent 711f3feb00
commit 1855085d2c
5 changed files with 88 additions and 77 deletions
+8
View File
@@ -210,6 +210,14 @@ async function initUI() {
`; `;
$('body').append(mobileToggleHtml); $('body').append(mobileToggleHtml);
// Add mobile refresh button (same pattern as toggle button)
const mobileRefreshHtml = `
<button id="rpg-manual-update-mobile" class="rpg-mobile-refresh" title="Refresh RPG Info">
<i class="fa-solid fa-sync"></i>
</button>
`;
$('body').append(mobileRefreshHtml);
// Cache UI elements using state setters // Cache UI elements using state setters
setPanelContainer($('#rpg-companion-panel')); setPanelContainer($('#rpg-companion-panel'));
setUserStatsContainer($('#rpg-user-stats')); setUserStatsContainer($('#rpg-user-stats'));
+3 -3
View File
@@ -273,12 +273,12 @@ export function applyPanelPosition() {
*/ */
export function updateGenerationModeUI() { export function updateGenerationModeUI() {
if (extensionSettings.generationMode === 'together') { if (extensionSettings.generationMode === 'together') {
// In "together" mode, both update buttons are hidden // In "together" mode, hide both update buttons
$('#rpg-manual-update').hide(); $('#rpg-manual-update').hide();
$('#rpg-manual-update-mobile').hide(); $('#rpg-manual-update-mobile').hide();
} else { } else {
// In "separate" mode, both update buttons are visible // In "separate" mode, show both buttons
// (CSS media queries will control which one is displayed) // (CSS media queries control which one is visible based on viewport)
$('#rpg-manual-update').show(); $('#rpg-manual-update').show();
$('#rpg-manual-update-mobile').show(); $('#rpg-manual-update-mobile').show();
} }
+31 -31
View File
@@ -440,32 +440,41 @@ export function setupMobileToggle() {
* Constrains the mobile FAB button to viewport bounds with top-bar awareness. * Constrains the mobile FAB button to viewport bounds with top-bar awareness.
* Only runs when button is in user-controlled state (mobileFabPosition exists). * Only runs when button is in user-controlled state (mobileFabPosition exists).
* Ensures button never goes behind the top bar or outside viewport edges. * 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() { 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';
// Only constrain if user has set a custom position // Only constrain if user has set a custom position
if (!extensionSettings.mobileFabPosition) { if (!extensionSettings[positionSetting]) {
console.log('[RPG Mobile] Skipping viewport constraint - using CSS defaults'); console.log('[RPG Mobile] Skipping viewport constraint - using CSS defaults');
return; return;
} }
const $mobileToggle = $('#rpg-mobile-toggle');
if ($mobileToggle.length === 0) return;
// Skip if button is not visible // Skip if button is not visible
if (!$mobileToggle.is(':visible')) { if (!$button.is(':visible')) {
console.log('[RPG Mobile] Skipping viewport constraint - button not visible'); console.log('[RPG Mobile] Skipping viewport constraint - button not visible');
return; return;
} }
// Get current position // Get current position
const offset = $mobileToggle.offset(); const offset = $button.offset();
if (!offset) return; if (!offset) return;
let currentX = offset.left; let currentX = offset.left;
let currentY = offset.top; let currentY = offset.top;
const buttonWidth = $mobileToggle.outerWidth(); const buttonWidth = $button.outerWidth();
const buttonHeight = $mobileToggle.outerHeight(); const buttonHeight = $button.outerHeight();
// Get top bar height from CSS variable (fallback to 50px if not set) // Get top bar height from CSS variable (fallback to 50px if not set)
const topBarHeight = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--topBarBlockSize')) || 50; const topBarHeight = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--topBarBlockSize')) || 50;
@@ -491,15 +500,15 @@ export function constrainFabToViewport() {
}); });
// Apply new position // Apply new position
$mobileToggle.css({ $button.css({
left: newX + 'px', left: newX + 'px',
top: newY + 'px', top: newY + 'px',
right: 'auto', right: 'auto',
bottom: 'auto' bottom: 'auto'
}); });
// Save corrected position // Save corrected position to appropriate setting
extensionSettings.mobileFabPosition = { extensionSettings[positionSetting] = {
left: newX + 'px', left: newX + 'px',
top: newY + 'px' top: newY + 'px'
}; };
@@ -725,43 +734,34 @@ export function setupContentEditableScrolling() {
/** /**
* Sets up the mobile refresh button with drag functionality. * Sets up the mobile refresh button with drag functionality.
* Button is only visible when panel is open, and can be dragged to reposition. * Same pattern as mobile toggle button.
* Tap = refresh, drag = reposition * Tap = refresh, drag = reposition
*/ */
export function setupRefreshButtonDrag() { export function setupRefreshButtonDrag() {
const $refreshBtn = $('#rpg-manual-update-mobile'); const $refreshBtn = $('#rpg-manual-update-mobile');
const $panel = $('#rpg-companion-panel');
if ($refreshBtn.length === 0) { if ($refreshBtn.length === 0) {
console.warn('[RPG Mobile] Refresh button not found in DOM'); console.warn('[RPG Mobile] Refresh button not found in DOM');
return; return;
} }
console.log('[RPG Mobile] setupRefreshButtonDrag called');
// Load and apply saved position // Load and apply saved position
if (extensionSettings.mobileRefreshPosition) { if (extensionSettings.mobileRefreshPosition) {
const pos = extensionSettings.mobileRefreshPosition; const pos = extensionSettings.mobileRefreshPosition;
if (pos.left) $refreshBtn.css('left', pos.left); console.log('[RPG Mobile] Loading saved refresh button position:', pos);
// Apply saved position
if (pos.top) $refreshBtn.css('top', pos.top); if (pos.top) $refreshBtn.css('top', pos.top);
if (pos.right) $refreshBtn.css('right', pos.right); if (pos.right) $refreshBtn.css('right', pos.right);
if (pos.bottom) $refreshBtn.css('bottom', pos.bottom); 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));
} }
// Show/hide button based on panel state
const updateButtonVisibility = () => {
if ($panel.hasClass('rpg-mobile-open')) {
$refreshBtn.show();
} else {
$refreshBtn.hide();
}
};
// Initial visibility check
updateButtonVisibility();
// Listen for panel state changes (attach to panel toggle events)
// This will be triggered by setupMobileToggle
$(document).on('rpg-panel-toggled', updateButtonVisibility);
// Touch/drag state // Touch/drag state
let isDragging = false; let isDragging = false;
let touchStartTime = 0; let touchStartTime = 0;
+46 -38
View File
@@ -2708,62 +2708,51 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
} }
/* ============================================ /* ============================================
REFRESH ICON BUTTON (Mobile - Floating & Draggable) MOBILE REFRESH BUTTON (FAB - Same pattern as toggle)
============================================ */ ============================================ */
.rpg-refresh-icon-btn { .rpg-mobile-refresh {
display: none; /* Hidden by default, shown on mobile when panel open */ display: none;
position: fixed; /* Fixed for dragging anywhere on viewport */
bottom: 80px; /* Above FAB toggle, below screen edge */
right: 20px;
width: 44px; /* Touch-friendly size */
height: 44px;
padding: 0;
background: var(--rpg-highlight);
border: 2px solid var(--rpg-highlight);
border-radius: 50%;
color: var(--rpg-text);
font-size: 1rem;
cursor: grab;
transition: transform 0.3s ease, box-shadow 0.3s ease, opacity 0.3s ease;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); position: fixed;
z-index: 1001; /* Above panel (1000) but below mobile FAB (10002) */ /* Position set by JavaScript based on saved settings */
width: 44px;
/* Fix sticky tap highlight */ height: 44px;
-webkit-tap-highlight-color: transparent; border-radius: 50%;
-webkit-touch-callout: none; 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; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
touch-action: none; will-change: top, left;
}
/* Remove focus outline (prevents black state) */
.rpg-refresh-icon-btn:focus {
outline: none;
} }
/* Disable transitions while actively dragging */ /* Disable transitions while actively dragging */
.rpg-refresh-icon-btn.dragging { .rpg-mobile-refresh.dragging {
transition: none; transition: none;
cursor: grabbing; cursor: grabbing;
} }
.rpg-refresh-icon-btn:hover { .rpg-mobile-refresh:hover {
transform: scale(1.1); transform: scale(1.1);
box-shadow: 0 4px 12px var(--rpg-highlight); box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
} }
.rpg-refresh-icon-btn:active { .rpg-mobile-refresh:active {
transform: scale(0.95); transform: scale(0.95);
} }
/* Spinning animation when refreshing */ /* Spinning animation when refreshing */
.rpg-refresh-icon-btn.spinning i { .rpg-mobile-refresh.spinning i {
animation: rpg-spin 0.8s linear infinite; animation: rpg-spin 0.8s linear infinite;
} }
.rpg-refresh-icon-btn i { .rpg-mobile-refresh i {
pointer-events: none; pointer-events: none;
} }
@@ -3361,14 +3350,28 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
/* Mobile-specific panel behavior - matches SillyTavern's 1000px breakpoint */ /* Mobile-specific panel behavior - matches SillyTavern's 1000px breakpoint */
/* CACHE BUST v2025-01-16 */ /* CACHE BUST v2025-01-16 */
/* Mobile: Show icon button, hide desktop button */ /* Mobile refresh button visibility - opposite of toggle */
@media (max-width: 1000px) { @media (max-width: 1000px) {
.rpg-refresh-icon-btn { /* Show mobile refresh button */
display: flex !important; /* Show mobile icon button */ .rpg-mobile-refresh {
display: flex;
} }
/* Hide desktop refresh button */
.rpg-manual-update-btn { .rpg-manual-update-btn {
display: none !important; /* Hide desktop button */ 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;
} }
} }
@@ -4040,6 +4043,11 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
font-size: clamp(20px, 5.1vw, 26px) !important; 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 MOBILE SETTINGS POPUP
======================================== */ ======================================== */
-5
View File
@@ -54,11 +54,6 @@
</div> </div>
</div> </div>
<!-- Mobile Floating Refresh Button (shown on all tabs) -->
<button id="rpg-manual-update-mobile" class="rpg-refresh-icon-btn" title="Refresh RPG Info">
<i class="fa-solid fa-sync"></i>
</button>
<!-- HTML Prompt Toggle --> <!-- HTML Prompt Toggle -->
<div class="rpg-toggle-container"> <div class="rpg-toggle-container">
<label class="rpg-toggle-label"> <label class="rpg-toggle-label">