diff --git a/src/systems/dashboard/promptDialog.js b/src/systems/dashboard/promptDialog.js
index 9647d4c..6cdfc6f 100644
--- a/src/systems/dashboard/promptDialog.js
+++ b/src/systems/dashboard/promptDialog.js
@@ -1,7 +1,7 @@
/**
* Prompt Dialog System
*
- * Provides styled prompt dialogs for text input, similar to confirmDialog.
+ * Provides styled prompt dialogs for text input, matching extension theming.
* Used for tab renaming, creation, etc.
*/
@@ -29,98 +29,55 @@ export function showPromptDialog(options) {
validator = null
} = options;
- // Create modal container
+ // Create modal container (uses .rpg-modal class for theming)
const modal = document.createElement('div');
modal.className = 'rpg-modal rpg-prompt-modal';
modal.style.display = 'flex';
- modal.style.position = 'fixed';
- modal.style.inset = '0';
- modal.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
- modal.style.zIndex = '10001';
- modal.style.alignItems = 'center';
- modal.style.justifyContent = 'center';
- // Create modal content
+ // Create modal content (uses .rpg-modal-content class for theming)
const modalContent = document.createElement('div');
modalContent.className = 'rpg-modal-content rpg-prompt-content';
modalContent.style.cssText = `
- background: #16213e;
- border-radius: 8px;
- padding: 0;
min-width: 400px;
max-width: 90vw;
- box-shadow: 0 4px 20px rgba(0,0,0,0.5);
`;
- // Header
+ // Header (uses .rpg-modal-header class)
const header = document.createElement('div');
header.className = 'rpg-modal-header';
- header.style.cssText = `
- padding: 20px;
- border-bottom: 1px solid #0f3460;
- display: flex;
- align-items: center;
- justify-content: space-between;
- `;
const headerContent = document.createElement('div');
headerContent.style.display = 'flex';
headerContent.style.alignItems = 'center';
- headerContent.style.gap = '10px';
+ headerContent.style.gap = '0.5rem';
const icon = document.createElement('i');
icon.className = 'fa-solid fa-pencil';
- icon.style.color = '#4ecca3';
- icon.style.fontSize = '20px';
+ icon.style.color = 'var(--rpg-highlight)';
const titleEl = document.createElement('h3');
titleEl.textContent = title;
- titleEl.style.cssText = `
- margin: 0;
- font-size: 18px;
- color: #eeeeee;
- `;
+ titleEl.style.margin = '0';
const closeBtn = document.createElement('button');
closeBtn.className = 'rpg-modal-close';
closeBtn.innerHTML = '';
- closeBtn.style.cssText = `
- background: transparent;
- border: none;
- color: #eeeeee;
- font-size: 20px;
- cursor: pointer;
- padding: 4px;
- width: 28px;
- height: 28px;
- border-radius: 4px;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: background 0.2s;
- `;
- closeBtn.onmouseenter = () => closeBtn.style.background = '#e94560';
- closeBtn.onmouseleave = () => closeBtn.style.background = 'transparent';
headerContent.appendChild(icon);
headerContent.appendChild(titleEl);
header.appendChild(headerContent);
header.appendChild(closeBtn);
- // Body
+ // Body (uses .rpg-modal-body class)
const body = document.createElement('div');
body.className = 'rpg-modal-body';
- body.style.cssText = `
- padding: 20px;
- `;
if (message) {
const messageEl = document.createElement('p');
messageEl.textContent = message;
messageEl.style.cssText = `
- margin: 0 0 15px 0;
- color: #eeeeee;
- font-size: 14px;
+ margin: 0 0 1rem 0;
+ color: var(--rpg-text);
`;
body.appendChild(messageEl);
}
@@ -131,12 +88,12 @@ export function showPromptDialog(options) {
input.placeholder = placeholder;
input.style.cssText = `
width: 100%;
- padding: 10px;
- background: #0f3460;
- border: 1px solid #1a4d7a;
+ padding: 0.5rem;
+ background: var(--rpg-accent);
+ border: 1px solid var(--rpg-border);
border-radius: 4px;
- color: #eeeeee;
- font-size: 14px;
+ color: var(--rpg-text);
+ font-size: 1rem;
font-family: inherit;
box-sizing: border-box;
`;
@@ -144,58 +101,26 @@ export function showPromptDialog(options) {
const errorEl = document.createElement('div');
errorEl.className = 'rpg-prompt-error';
errorEl.style.cssText = `
- margin-top: 8px;
- color: #e94560;
- font-size: 12px;
- min-height: 18px;
+ margin-top: 0.5rem;
+ color: var(--rpg-highlight);
+ font-size: 0.875rem;
+ min-height: 1.25rem;
`;
body.appendChild(input);
body.appendChild(errorEl);
- // Footer
+ // Footer (uses .rpg-modal-footer class)
const footer = document.createElement('div');
footer.className = 'rpg-modal-footer';
- footer.style.cssText = `
- padding: 20px;
- border-top: 1px solid #0f3460;
- display: flex;
- gap: 10px;
- justify-content: flex-end;
- `;
const cancelBtn = document.createElement('button');
cancelBtn.className = 'rpg-btn-secondary';
- cancelBtn.textContent = cancelText;
- cancelBtn.style.cssText = `
- padding: 10px 20px;
- background: #0f3460;
- border: none;
- border-radius: 6px;
- color: #eeeeee;
- font-size: 14px;
- cursor: pointer;
- transition: background 0.2s;
- `;
- cancelBtn.onmouseenter = () => cancelBtn.style.background = '#1a4d7a';
- cancelBtn.onmouseleave = () => cancelBtn.style.background = '#0f3460';
+ cancelBtn.innerHTML = ` ${cancelText}`;
const confirmBtn = document.createElement('button');
confirmBtn.className = 'rpg-btn-primary';
- confirmBtn.textContent = confirmText;
- confirmBtn.style.cssText = `
- padding: 10px 20px;
- background: #4ecca3;
- border: none;
- border-radius: 6px;
- color: #16213e;
- font-size: 14px;
- font-weight: bold;
- cursor: pointer;
- transition: background 0.2s;
- `;
- confirmBtn.onmouseenter = () => confirmBtn.style.background = '#45b993';
- confirmBtn.onmouseleave = () => confirmBtn.style.background = '#4ecca3';
+ confirmBtn.innerHTML = ` ${confirmText}`;
footer.appendChild(cancelBtn);
footer.appendChild(confirmBtn);
diff --git a/src/systems/dashboard/tabContextMenu.js b/src/systems/dashboard/tabContextMenu.js
index a182f4a..6c19e47 100644
--- a/src/systems/dashboard/tabContextMenu.js
+++ b/src/systems/dashboard/tabContextMenu.js
@@ -45,7 +45,12 @@ export class TabContextMenu {
attachHandlers() {
if (!this.tabsContainer) return;
- // Use event delegation for dynamically added tabs
+ // Long press support for mobile
+ let longPressTimer = null;
+ let longPressTarget = null;
+ let touchStartPos = { x: 0, y: 0 };
+
+ // Desktop: Right-click context menu
this.tabsContainer.addEventListener('contextmenu', (e) => {
// Find closest tab element
const tabElement = e.target.closest('.rpg-dashboard-tab');
@@ -60,8 +65,77 @@ export class TabContextMenu {
this.showMenu(e.pageX, e.pageY, tabId);
});
- // Close menu on any click outside
+ // Mobile: Long press support (touch and hold)
+ this.tabsContainer.addEventListener('touchstart', (e) => {
+ const tabElement = e.target.closest('.rpg-dashboard-tab');
+ if (!tabElement) return;
+
+ const tabId = tabElement.dataset.tabId;
+ if (!tabId) return;
+
+ // Store touch position
+ const touch = e.touches[0];
+ touchStartPos = { x: touch.pageX, y: touch.pageY };
+ longPressTarget = { tabId, x: touch.pageX, y: touch.pageY };
+
+ // Start long press timer (500ms)
+ longPressTimer = setTimeout(() => {
+ if (longPressTarget) {
+ // Prevent default touch behavior
+ e.preventDefault();
+ // Show context menu at touch position
+ this.showMenu(longPressTarget.x, longPressTarget.y, longPressTarget.tabId);
+ // Provide haptic feedback if available
+ if (navigator.vibrate) {
+ navigator.vibrate(50);
+ }
+ longPressTarget = null;
+ }
+ }, 500);
+ }, { passive: false });
+
+ // Cancel long press on touch move (if moved too far)
+ this.tabsContainer.addEventListener('touchmove', (e) => {
+ if (!longPressTimer) return;
+
+ const touch = e.touches[0];
+ const deltaX = Math.abs(touch.pageX - touchStartPos.x);
+ const deltaY = Math.abs(touch.pageY - touchStartPos.y);
+
+ // Cancel if moved more than 10px
+ if (deltaX > 10 || deltaY > 10) {
+ clearTimeout(longPressTimer);
+ longPressTimer = null;
+ longPressTarget = null;
+ }
+ });
+
+ // Cancel long press on touch end (if timer still running)
+ this.tabsContainer.addEventListener('touchend', () => {
+ if (longPressTimer) {
+ clearTimeout(longPressTimer);
+ longPressTimer = null;
+ longPressTarget = null;
+ }
+ });
+
+ // Cancel long press on touch cancel
+ this.tabsContainer.addEventListener('touchcancel', () => {
+ if (longPressTimer) {
+ clearTimeout(longPressTimer);
+ longPressTimer = null;
+ longPressTarget = null;
+ }
+ });
+
+ // Close menu on any click/touch outside
document.addEventListener('click', () => this.hideMenu());
+ document.addEventListener('touchstart', (e) => {
+ // Close menu if touching outside context menu
+ if (this.menu && !this.menu.contains(e.target)) {
+ this.hideMenu();
+ }
+ });
document.addEventListener('contextmenu', (e) => {
// Only hide if right-clicking outside tabs
if (!e.target.closest('.rpg-dashboard-tab')) {
@@ -83,17 +157,17 @@ export class TabContextMenu {
const tab = this.tabManager.getTab(tabId);
if (!tab) return;
- // Create menu container
+ // Create menu container (matches widget styling with solid background)
this.menu = document.createElement('div');
this.menu.className = 'rpg-tab-context-menu';
this.menu.style.cssText = `
position: fixed;
left: ${x}px;
top: ${y}px;
- background: #16213e;
- border: 1px solid #0f3460;
+ background: linear-gradient(135deg, rgba(22, 33, 62, 1) 0%, rgba(26, 26, 46, 1) 100%);
+ border: 2px solid var(--rpg-border);
border-radius: 6px;
- box-shadow: 0 4px 20px rgba(0,0,0,0.5);
+ box-shadow: 0 4px 18px var(--rpg-shadow), inset 0 0 12px rgba(0, 0, 0, 0.3);
z-index: 10002;
min-width: 180px;
padding: 6px 0;
@@ -115,7 +189,7 @@ export class TabContextMenu {
const separator = document.createElement('div');
separator.style.cssText = `
height: 1px;
- background: #0f3460;
+ background: var(--rpg-border);
margin: 6px 0;
`;
this.menu.appendChild(separator);
@@ -142,8 +216,8 @@ export class TabContextMenu {
const menuItem = document.createElement('div');
menuItem.className = 'rpg-tab-context-menu-item';
- const baseColor = item.danger ? '#e94560' : '#eeeeee';
- const hoverBg = item.danger ? '#8b2a3a' : '#0f3460';
+ const baseColor = item.danger ? 'var(--rpg-highlight)' : 'var(--rpg-text)';
+ const hoverBg = item.danger ? 'rgba(233, 69, 96, 0.2)' : 'var(--rpg-accent)';
menuItem.style.cssText = `
padding: 10px 16px;
@@ -172,7 +246,7 @@ export class TabContextMenu {
icon.style.cssText = `
width: 16px;
text-align: center;
- color: ${item.danger ? '#e94560' : '#4ecca3'};
+ color: ${item.danger ? 'var(--rpg-highlight)' : 'var(--rpg-border)'};
`;
const label = document.createElement('span');
@@ -338,44 +412,31 @@ export class TabContextMenu {
*/
showIconPicker(iconOptions, currentIcon) {
return new Promise((resolve) => {
- // Create modal
+ // Create modal (uses .rpg-modal class for theming)
const modal = document.createElement('div');
modal.className = 'rpg-modal';
- modal.style.cssText = `
- position: fixed;
- inset: 0;
- background: rgba(0,0,0,0.7);
- z-index: 10001;
- display: flex;
- align-items: center;
- justify-content: center;
- `;
+ modal.style.display = 'flex';
+ // Modal content (uses .rpg-modal-content class for theming)
const content = document.createElement('div');
content.className = 'rpg-modal-content';
- content.style.cssText = `
- background: #16213e;
- border-radius: 8px;
- padding: 24px;
- max-width: 500px;
- max-height: 80vh;
- overflow-y: auto;
- `;
+ content.style.padding = '1.5rem';
+ content.style.maxWidth = '500px';
const title = document.createElement('h3');
title.textContent = 'Choose Icon';
title.style.cssText = `
- margin: 0 0 20px 0;
- color: #eeeeee;
- font-size: 18px;
+ margin: 0 0 1.25rem 0;
+ color: var(--rpg-text);
+ font-size: 1.25rem;
`;
const grid = document.createElement('div');
grid.style.cssText = `
display: grid;
grid-template-columns: repeat(5, 1fr);
- gap: 12px;
- margin-bottom: 20px;
+ gap: 0.75rem;
+ margin-bottom: 1.25rem;
`;
// Extract icon name without fa-solid prefix for comparison
@@ -386,12 +447,12 @@ export class TabContextMenu {
const isSelected = option.icon === currentIconName;
iconBtn.style.cssText = `
- padding: 16px;
- background: ${isSelected ? '#4ecca3' : '#0f3460'};
- border: 2px solid ${isSelected ? '#4ecca3' : '#1a4d7a'};
+ padding: 1rem;
+ background: ${isSelected ? 'var(--rpg-highlight)' : 'var(--rpg-accent)'};
+ border: 2px solid ${isSelected ? 'var(--rpg-highlight)' : 'var(--rpg-border)'};
border-radius: 6px;
- color: ${isSelected ? '#16213e' : '#eeeeee'};
- font-size: 24px;
+ color: ${isSelected ? 'white' : 'var(--rpg-text)'};
+ font-size: 1.5rem;
cursor: pointer;
transition: all 0.2s;
display: flex;
@@ -404,14 +465,14 @@ export class TabContextMenu {
iconBtn.onmouseenter = () => {
if (!isSelected) {
- iconBtn.style.background = '#1a4d7a';
- iconBtn.style.borderColor = '#4ecca3';
+ iconBtn.style.borderColor = 'var(--rpg-highlight)';
+ iconBtn.style.transform = 'scale(1.05)';
}
};
iconBtn.onmouseleave = () => {
if (!isSelected) {
- iconBtn.style.background = '#0f3460';
- iconBtn.style.borderColor = '#1a4d7a';
+ iconBtn.style.borderColor = 'var(--rpg-border)';
+ iconBtn.style.transform = 'scale(1)';
}
};
@@ -424,17 +485,9 @@ export class TabContextMenu {
});
const cancelBtn = document.createElement('button');
- cancelBtn.textContent = 'Cancel';
- cancelBtn.style.cssText = `
- padding: 10px 20px;
- background: #0f3460;
- border: none;
- border-radius: 6px;
- color: #eeeeee;
- font-size: 14px;
- cursor: pointer;
- width: 100%;
- `;
+ cancelBtn.className = 'rpg-btn-secondary';
+ cancelBtn.innerHTML = ' Cancel';
+ cancelBtn.style.width = '100%';
cancelBtn.onclick = () => {
modal.remove();
resolve(null);
diff --git a/style.css b/style.css
index 3c79d11..1982636 100644
--- a/style.css
+++ b/style.css
@@ -1479,13 +1479,13 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
/* Modal Content Container */
.rpg-modal-content {
- background: var(--rpg-bg);
+ background: linear-gradient(135deg, rgba(22, 33, 62, 1) 0%, rgba(26, 26, 46, 1) 100%);
border: 2px solid var(--rpg-border);
border-radius: 8px;
max-width: 600px;
max-height: 80vh;
overflow: auto;
- box-shadow: 0 4px 20px var(--rpg-shadow);
+ box-shadow: 0 4px 18px var(--rpg-shadow), inset 0 0 12px rgba(0, 0, 0, 0.3);
}
/* Modal Header */