Fix session close button with non-blocking confirmation modal
Replace blocking confirm() dialog with custom non-blocking modal to prevent browser warning issues when users have "don't show warnings" enabled. Changes: - Add showConfirmModal() method with Promise-based async modal - Update closeSession() to use non-blocking modal - Update deleteSession() to use non-blocking modal - Add complete CSS styling for confirmation modal - Support keyboard (Escape key) and click-outside to close - Responsive design for mobile devices - Dark theme matching existing UI Fixes issue where close button completely stopped working after browser blocked confirm() dialog and user selected "don't show warnings". Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -170,7 +170,7 @@ class SessionTabs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close a session (with confirmation)
|
* Close a session (with non-blocking confirmation modal)
|
||||||
*/
|
*/
|
||||||
async closeSession(sessionId) {
|
async closeSession(sessionId) {
|
||||||
const session = this.sessions.find(s => s.id === sessionId);
|
const session = this.sessions.find(s => s.id === sessionId);
|
||||||
@@ -178,8 +178,14 @@ class SessionTabs {
|
|||||||
|
|
||||||
const sessionName = this.getSessionName(session);
|
const sessionName = this.getSessionName(session);
|
||||||
|
|
||||||
// Confirm before closing
|
// Show non-blocking confirmation modal
|
||||||
if (!confirm(`Close session "${sessionName}"?`)) {
|
const confirmed = await this.showConfirmModal(
|
||||||
|
'Close Session',
|
||||||
|
`Are you sure you want to close "${escapeHtml(sessionName)}"?`,
|
||||||
|
'Close Session'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +293,93 @@ class SessionTabs {
|
|||||||
document.getElementById('session-context-menu')?.remove();
|
document.getElementById('session-context-menu')?.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a non-blocking confirmation modal
|
||||||
|
* @param {string} title - Modal title
|
||||||
|
* @param {string} message - Confirmation message
|
||||||
|
* @param {string} confirmText - Text for confirm button (default: "Confirm")
|
||||||
|
* @returns {Promise<boolean>} - True if confirmed, false otherwise
|
||||||
|
*/
|
||||||
|
showConfirmModal(title, message, confirmText = 'Confirm') {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
// Remove existing modal if present
|
||||||
|
const existingModal = document.getElementById('confirm-modal-overlay');
|
||||||
|
if (existingModal) existingModal.remove();
|
||||||
|
|
||||||
|
// Create overlay
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.id = 'confirm-modal-overlay';
|
||||||
|
overlay.className = 'confirm-modal-overlay';
|
||||||
|
|
||||||
|
// Create modal
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.className = 'confirm-modal';
|
||||||
|
modal.innerHTML = `
|
||||||
|
<div class="confirm-modal-header">
|
||||||
|
<h3 class="confirm-modal-title">${escapeHtml(title)}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="confirm-modal-body">
|
||||||
|
<p class="confirm-modal-message">${message}</p>
|
||||||
|
</div>
|
||||||
|
<div class="confirm-modal-footer">
|
||||||
|
<button class="btn-confirm-cancel" id="confirm-modal-cancel">Cancel</button>
|
||||||
|
<button class="btn-confirm-ok" id="confirm-modal-ok">${escapeHtml(confirmText)}</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
overlay.appendChild(modal);
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
|
// Prevent body scroll
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
|
||||||
|
// Trigger animation
|
||||||
|
setTimeout(() => {
|
||||||
|
overlay.classList.add('visible');
|
||||||
|
modal.classList.add('visible');
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
// Handle confirm button
|
||||||
|
document.getElementById('confirm-modal-ok').addEventListener('click', () => {
|
||||||
|
closeModal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle cancel button
|
||||||
|
document.getElementById('confirm-modal-cancel').addEventListener('click', () => {
|
||||||
|
closeModal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close on overlay click
|
||||||
|
overlay.addEventListener('click', (e) => {
|
||||||
|
if (e.target === overlay) {
|
||||||
|
closeModal(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close on Escape key
|
||||||
|
const escapeHandler = (e) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
closeModal(false);
|
||||||
|
document.removeEventListener('keydown', escapeHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', escapeHandler);
|
||||||
|
|
||||||
|
// Function to close modal
|
||||||
|
function closeModal(result) {
|
||||||
|
overlay.classList.remove('visible');
|
||||||
|
modal.classList.remove('visible');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
document.removeEventListener('keydown', escapeHandler);
|
||||||
|
overlay.remove();
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
resolve(result);
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rename a session
|
* Rename a session
|
||||||
*/
|
*/
|
||||||
@@ -362,7 +455,13 @@ class SessionTabs {
|
|||||||
async deleteSession(session) {
|
async deleteSession(session) {
|
||||||
const sessionName = this.getSessionName(session);
|
const sessionName = this.getSessionName(session);
|
||||||
|
|
||||||
if (!confirm(`Permanently delete "${sessionName}"? This cannot be undone.`)) {
|
const confirmed = await this.showConfirmModal(
|
||||||
|
'Delete Session',
|
||||||
|
`Are you sure you want to permanently delete "${escapeHtml(sessionName)}"? This action cannot be undone.`,
|
||||||
|
'Delete'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,4 +523,145 @@ if (document.readyState === 'loading') {
|
|||||||
window.sessionTabs.initialize();
|
window.sessionTabs.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Add CSS Styles
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.textContent = `
|
||||||
|
/* Confirm Modal Overlay */
|
||||||
|
.confirm-modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
z-index: 10000;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-modal-overlay.visible {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Confirm Modal */
|
||||||
|
.confirm-modal {
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 1px solid #333;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
overflow: hidden;
|
||||||
|
transform: scale(0.95);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-modal.visible {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
.confirm-modal-header {
|
||||||
|
padding: 20px 20px 12px 20px;
|
||||||
|
border-bottom: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-modal-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #e0e0e0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Body */
|
||||||
|
.confirm-modal-body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-modal-message {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #b0b0b0;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
.confirm-modal-footer {
|
||||||
|
padding: 12px 20px 20px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
.btn-confirm-cancel,
|
||||||
|
.btn-confirm-ok {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm-cancel {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #333;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm-cancel:hover {
|
||||||
|
background: #252525;
|
||||||
|
border-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm-ok {
|
||||||
|
background: linear-gradient(135deg, #4a9eff 0%, #a78bfa 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm-ok:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba(74, 158, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm-ok:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.confirm-modal {
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-modal-header,
|
||||||
|
.confirm-modal-body,
|
||||||
|
.confirm-modal-footer {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-modal-footer {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm-cancel,
|
||||||
|
.btn-confirm-ok {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
})();
|
||||||
|
|
||||||
console.log('[SessionTabs] Module loaded');
|
console.log('[SessionTabs] Module loaded');
|
||||||
|
|||||||
Reference in New Issue
Block a user