Adds a floating action button to display content from a specific div in a draggable window.
// ==UserScript==
// @name Ironwood RPG Floating Menu
// @namespace http://tampermonkey.net/
// @version 1.2
// @description Adds a floating action button to display content from a specific div in a draggable window.
// @author Your Name
// @match https://ironwoodrpg.com/*
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// --- Configuration ---
// Selector for the element whose content you want to clone and display
const TARGET_SELECTOR = 'div.scroll.custom-scrollbar.scroll-margin';
// --- End Configuration ---
// 1. Inject CSS for the FAB and the floating window
GM_addStyle(`
/* Floating Action Button (FAB) */
#fab-button {
position: fixed;
bottom: 20px;
right: 20px;
width: 50px; /* Slightly smaller FAB */
height: 50px;
border-radius: 50%;
background-color: #007bff;
color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25);
border: none;
cursor: pointer;
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
transition: background-color 0.2s;
}
#fab-button:hover {
background-color: #0056b3;
}
/* Floating Window (Modal/Panel) */
#floating-window {
position: fixed;
bottom: 80px; /* Position above the FAB */
right: 20px;
top: auto; /* Remove top centering */
left: auto; /* Remove left centering */
transform: none; /* Remove centering transform */
/* --- CHANGES FOR ONE-HAND USAGE & STYLING --- */
width: 200px; /* Narrow width for one-hand use */
max-width: 90vw;
height: 400px; /* Set a fixed height for a tall mobile-like panel */
max-height: 80vh;
/* Remove custom background/border to better adopt cloned content's styling */
background-color: transparent;
border: none;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); /* Keep shadow for visual separation */
z-index: 9999;
display: none;
resize: vertical; /* Allow resizing, but keep it constrained */
overflow: hidden; /* Manage scrolling via the content area */
}
/* The cloned content area will manage the visuals */
#floating-window-content {
height: 100%; /* Make content fill the window */
width: 100%;
padding: 0; /* Remove padding to avoid offsets */
overflow-y: auto; /* Allow scrolling inside the content area */
}
#floating-window-content button {
/* Enable Flexbox layout for the button */
display: flex !important;
/* Vertically center the items (img and div) */
align-items: center !important;
/* Add a little space between the image and the text */
gap: 8px;
/* Ensure the content is centered horizontally if the button is wider */
justify-content: flex-start !important;
/* Ensure the button takes up the full width of the cloned content */
width: 100%;
}
`);
// 2. Create the FAB button
const fabButton = document.createElement('button');
fabButton.id = 'fab-button';
// Use a standard hamburger icon (Unicode character)
fabButton.innerHTML = '☰';
fabButton.title = 'Open Floating Content';
document.body.appendChild(fabButton);
// 3. Create the Floating Window structure (NO HEADER)
const floatingWindow = document.createElement('div');
floatingWindow.id = 'floating-window';
// Content area
const contentArea = document.createElement('div');
contentArea.id = 'floating-window-content';
// Since the header is gone, make the whole content area draggable
contentArea.style.cursor = 'move';
floatingWindow.appendChild(contentArea);
document.body.appendChild(floatingWindow);
// 4. FAB Button Click Handler (Toggle Visibility and Update Content)
fabButton.addEventListener('click', () => {
if (floatingWindow.style.display === 'block') {
floatingWindow.style.display = 'none'; // Hide
} else {
// Find the original target element
const targetElement = document.querySelector(TARGET_SELECTOR);
if (targetElement) {
// Clone the content
const clonedContent = targetElement.cloneNode(true);
// --- Styling Fixes ---
clonedContent.style.backgroundColor = '#061a2e';
clonedContent.style.color = 'white';
clonedContent.style.border = '1px solid #263849';
clonedContent.style.padding = '8px'
clonedContent.style.borderRadius = '10px';
contentArea.innerHTML = '';
contentArea.appendChild(clonedContent);
clonedContent.addEventListener('click', (e) => {
// Find the index of the clicked element within its parent
let node = e.target;
// We need to trace the click back to the original element
// by mapping the DOM path from the cloned element.
// 1. Get the path of elements from the clicked node up to the cloned root
const path = [];
let tempNode = node;
while (tempNode !== clonedContent && tempNode) {
path.unshift(tempNode);
tempNode = tempNode.parentNode;
}
// 2. Traverse the path on the original target element
let originalNode = targetElement;
for (const element of path) {
if (originalNode) {
// Find the corresponding child node in the original element
let childIndex = Array.from(element.parentNode.children).indexOf(element);
originalNode = originalNode.children[childIndex];
}
}
// 3. Trigger the click on the original element
if (originalNode) {
originalNode.click();
floatingWindow.style.display = 'none';
} else {
console.warn('Could not map click to original element.');
}
});
floatingWindow.style.display = 'block'; // Show
} else {
console.error('Target element not found with selector:', TARGET_SELECTOR);
contentArea.innerHTML = '<div style="padding:10px; color:red; background:white;">Error: Target element not found.</div>';
floatingWindow.style.display = 'block';
}
}
});
// 5. Drag Functionality (Now using the content area)
let isDragging = false;
let offsetX, offsetY;
const draggableArea = contentArea; // Use contentArea for dragging
draggableArea.addEventListener('mousedown', (e) => {
isDragging = true;
// Calculate the offset from the mouse click to the window's top-left corner
offsetX = e.clientX - floatingWindow.offsetLeft;
offsetY = e.clientY - floatingWindow.offsetTop;
// Prevent text selection while dragging
document.body.style.userSelect = 'none';
e.preventDefault(); // Prevents default dragging/selection
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
// Calculate new position
let newLeft = e.clientX - offsetX;
let newTop = e.clientY - offsetY;
// Keep it anchored to the right side if dragged
floatingWindow.style.right = `${document.documentElement.clientWidth - newLeft - floatingWindow.offsetWidth}px`;
floatingWindow.style.bottom = `${document.documentElement.clientHeight - newTop - floatingWindow.offsetHeight}px`;
floatingWindow.style.left = 'auto';
floatingWindow.style.top = 'auto';
});
document.addEventListener('mouseup', () => {
isDragging = false;
document.body.style.userSelect = 'auto';
});
})();