From fc76581337801c7251eddf379d04b2fd30da5518 Mon Sep 17 00:00:00 2001 From: uroma Date: Thu, 22 Jan 2026 12:35:52 +0000 Subject: [PATCH] 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 --- public/claude-ide/session-tabs.js | 248 +++++++++++++++++++++++++++++- 1 file changed, 244 insertions(+), 4 deletions(-) diff --git a/public/claude-ide/session-tabs.js b/public/claude-ide/session-tabs.js index 6f7e5dac..37fd0f29 100644 --- a/public/claude-ide/session-tabs.js +++ b/public/claude-ide/session-tabs.js @@ -170,7 +170,7 @@ class SessionTabs { } /** - * Close a session (with confirmation) + * Close a session (with non-blocking confirmation modal) */ async closeSession(sessionId) { const session = this.sessions.find(s => s.id === sessionId); @@ -178,8 +178,14 @@ class SessionTabs { const sessionName = this.getSessionName(session); - // Confirm before closing - if (!confirm(`Close session "${sessionName}"?`)) { + // Show non-blocking confirmation modal + const confirmed = await this.showConfirmModal( + 'Close Session', + `Are you sure you want to close "${escapeHtml(sessionName)}"?`, + 'Close Session' + ); + + if (!confirmed) { return; } @@ -287,6 +293,93 @@ class SessionTabs { 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} - 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 = ` +
+

${escapeHtml(title)}

+
+
+

${message}

+
+ + `; + + 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 */ @@ -362,7 +455,13 @@ class SessionTabs { async deleteSession(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; } @@ -424,4 +523,145 @@ if (document.readyState === 'loading') { 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');