diff --git a/AutoHaiTun.js b/AutoHaiTun.js index 73f0ef4..cbe85ab 100644 --- a/AutoHaiTun.js +++ b/AutoHaiTun.js @@ -8,7 +8,6 @@ // @grant none // @run-at document-end // ==/UserScript== - (function() { 'use strict'; @@ -17,47 +16,28 @@ notifications: [], baseTop: 70, // 初始顶部位置 gap: 10, // 通知间距 - - // 添加通知到管理器 add: function(element) { - // 计算新通知的顶部位置 let topPosition = this.baseTop; - - // 为已有的每个通知增加高度和间隔 this.notifications.forEach(notification => { - if (notification.isConnected) { // 确保通知仍在DOM中 + if (notification.isConnected) { const height = notification.offsetHeight; topPosition += height + this.gap; } }); - - // 过滤掉已移除的通知 this.notifications = this.notifications.filter(n => n.isConnected); - - // 设置新通知位置 element.style.top = topPosition + 'px'; - - // 添加到管理器 this.notifications.push(element); - return element; }, - - // 从管理器移除通知 remove: function(element) { const index = this.notifications.indexOf(element); if (index > -1) { this.notifications.splice(index, 1); } - - // 重新计算剩余通知的位置 this.updatePositions(); }, - - // 更新所有通知的位置 updatePositions: function() { let topPosition = this.baseTop; - this.notifications.forEach(notification => { if (notification.isConnected) { notification.style.top = topPosition + 'px'; @@ -71,69 +51,17 @@ function addStyles() { const styleSheet = document.createElement('style'); styleSheet.textContent = ` - @keyframes slideInRight { - from { transform: translateX(100%); opacity: 0; } - to { transform: translateX(0); opacity: 1; } - } - - @keyframes pulse { - 0% { transform: scale(1); } - 50% { transform: scale(1.05); } - 100% { transform: scale(1); } - } - - @keyframes fadeIn { - from { opacity: 0; transform: translateY(-20px); } - to { opacity: 1; transform: translateY(0); } - } - - @keyframes fadeOut { - from { opacity: 1; transform: translateY(0); } - to { opacity: 0; transform: translateY(-20px); } - } - - @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } - } - - @keyframes ripple { - to { - transform: scale(3); - opacity: 0; - } - } - - #dolphin-auto-btn { - animation: slideInRight 0.5s ease-out; - } - - .dolphin-notification { - animation: fadeIn 0.3s ease-out; - display: flex; - align-items: center; - border-left: 4px solid; - } - - .dolphin-notification.hiding { - animation: fadeOut 0.5s ease-in forwards; - } - - .notification-icon { - margin-right: 12px; - font-size: 20px; - } - - .loader { - display: inline-block; - width: 15px; - height: 15px; - border: 2px solid rgba(255,255,255,0.3); - border-radius: 50%; - border-top-color: #fff; - animation: spin 0.8s linear infinite; - margin-right: 8px; - } + @keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } + @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } + @keyframes fadeIn { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } + @keyframes fadeOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-20px); } } + @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } + @keyframes ripple { to { transform: scale(3); opacity: 0; } } + #dolphin-auto-btn { animation: slideInRight 0.5s ease-out; } + .dolphin-notification { animation: fadeIn 0.3s ease-out; display: flex; align-items: center; border-left: 4px solid; } + .dolphin-notification.hiding { animation: fadeOut 0.5s ease-in forwards; } + .notification-icon { margin-right: 12px; font-size: 20px; } + .loader { display: inline-block; width: 15px; height: 15px; border: 2px solid rgba(255,255,255,0.3); border-radius: 50%; border-top-color: #fff; animation: spin 0.8s linear infinite; margin-right: 8px; } `; document.head.appendChild(styleSheet); } @@ -154,17 +82,13 @@ console.log('等待重启确认模态框...'); const checkModal = setInterval(() => { const modals = [...document.querySelectorAll('.ivu-modal')]; - const targetModal = modals.find(modal => - modal.textContent.includes('重新启动内核并重新运行整个笔记本')); - + const targetModal = modals.find(modal => modal.textContent.includes('重新启动内核并重新运行整个笔记本')); if (targetModal) { const confirmButton = targetModal.querySelector('.ivu-btn-primary'); if (confirmButton) { confirmButton.click(); console.log('已点击"确定"按钮'); clearInterval(checkModal); - - // 执行完成后通知用户 setTimeout(() => { showNotification('自动化执行完成:代码已复制、保存并重启', 'success'); }, 1000); @@ -175,20 +99,15 @@ // 提取代码文本 function extractCodeText(codeBlock) { - return codeBlock.innerText || - [...codeBlock.querySelectorAll('span')].reduce((text, span) => - text + span.textContent, ''); + return codeBlock.innerText || [...codeBlock.querySelectorAll('span')].reduce((text, span) => text + span.textContent, ''); } - + // 显示美化的通知 function showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.className = 'dolphin-notification'; - let iconHTML = ''; let borderColor = ''; - - // 根据类型设置不同的图标和颜色 if (type === 'success') { iconHTML = '
'; borderColor = '#4CAF50'; @@ -199,56 +118,30 @@ iconHTML = '
ℹ️
'; borderColor = '#2196F3'; } - notification.innerHTML = iconHTML + `
${message}
`; notification.style.cssText = ` - position: fixed; - right: 300px; - padding: 15px 20px; - background: white; - color: #333; - border-radius: 6px; - box-shadow: 0 4px 15px rgba(0,0,0,0.15); - z-index: 10000; - max-width: 350px; - font-size: 14px; - border-left-color: ${borderColor}; - display: flex; - align-items: center; - transition: top 0.3s ease; + position: fixed; right: 300px; padding: 15px 20px; background: white; color: #333; border-radius: 6px; + box-shadow: 0 4px 15px rgba(0,0,0,0.15); z-index: 10000; max-width: 350px; font-size: 14px; border-left-color: ${borderColor}; + display: flex; align-items: center; transition: top 0.3s ease; `; - document.body.appendChild(notification); - - // 通过通知管理器添加并定位通知 NotificationManager.add(notification); - - // 鼠标悬停时停止自动隐藏 + let timeoutId; - - notification.addEventListener('mouseenter', () => { - clearTimeout(timeoutId); - }); - - notification.addEventListener('mouseleave', () => { - startHideTimeout(); - }); - + notification.addEventListener('mouseenter', () => clearTimeout(timeoutId)); + notification.addEventListener('mouseleave', () => startHideTimeout()); + function startHideTimeout() { timeoutId = setTimeout(() => { notification.classList.add('hiding'); setTimeout(() => { notification.remove(); - // 从管理器中移除并重新排列其他通知 NotificationManager.remove(notification); }, 500); }, 3000); } - - // 开始隐藏倒计时 startHideTimeout(); - - // 添加点击关闭功能 + notification.addEventListener('click', () => { clearTimeout(timeoutId); notification.classList.add('hiding'); @@ -257,84 +150,104 @@ NotificationManager.remove(notification); }, 500); }); - return notification; } // 设置按钮状态 function setButtonState(button, state) { if (!button) return; - - // 定义不同状态的样式 const states = { - idle: { - text: '运行自动化', - backgroundColor: '#2196F3', - disabled: false, - html: '运行自动化' - }, - running: { - text: '执行中', - backgroundColor: '#FFA000', - disabled: true, - html: '
执行中' - }, - completed: { - text: '已完成', - backgroundColor: '#4CAF50', - disabled: true, - html: '✅ 已完成' - }, - failed: { - text: '执行失败', - backgroundColor: '#F44336', - disabled: false, - html: '❌ 重试' - } + idle: { text: '运行自动化', backgroundColor: '#2196F3', disabled: false, html: '运行自动化' }, + running: { text: '执行中', backgroundColor: '#FFA000', disabled: true, html: '
执行中' }, + completed: { text: '已完成', backgroundColor: '#4CAF50', disabled: true, html: '✅ 已完成' }, + failed: { text: '执行失败', backgroundColor: '#F44336', disabled: false, html: '❌ 重试' } }; - const newState = states[state]; - - // 应用过渡效果 button.style.transition = 'all 0.3s ease'; button.disabled = newState.disabled; button.innerHTML = newState.html; button.style.backgroundColor = newState.backgroundColor; - - // 如果是完成状态,添加脉冲动画 if (state === 'completed') { button.style.animation = 'pulse 0.5s ease-in-out'; - // 3秒后恢复到初始状态 setTimeout(() => setButtonState(button, 'idle'), 3000); } } + // 查找所有目标代码块(包括“代码示例”和练习答案) + function findAllCodeBlocks() { + // 找到所有包含“代码示例”的父容器 + const textCellWraps = Array.from(document.querySelectorAll('.text-cell-wrap')) + .filter(cellWrap => cellWrap.querySelector('.alert.alert-success.alertsuccess')?.textContent.includes('代码示例')); + + // 收集紧跟在“代码示例”后面的
 元素
+        const exampleCodeBlocks = [];
+
+        // 辅助函数:递归查找 

+        function findCodeBlock(container) {
+            if (!container) return null;
+            const codeBlock = container.querySelector('pre code');
+            return codeBlock || findCodeBlock(container.nextElementSibling);
+        }
+
+        textCellWraps.forEach((cellWrap, index) => {
+            console.log(`处理代码示例 ${index + 1} 的父容器:`, cellWrap);
+            const codeBlock = findCodeBlock(cellWrap);
+            if (codeBlock) {
+                exampleCodeBlocks.push(codeBlock);
+                console.log(`找到“代码示例”代码块内容:`, codeBlock.textContent.trim());
+            } else {
+                console.warn(`未找到“代码示例”代码块!`);
+            }
+        });
+
+        // 找到所有练习答案区域中的 

+        const answerCodeBlocks = [];
+        const collapseDivs = Array.from(document.querySelectorAll('.collapse'));
+        console.log(`找到 ${collapseDivs.length} 个练习答案区域:`);
+        collapseDivs.forEach((collapseDiv, index) => {
+            console.log(`检查练习答案区域 ${index + 1}:`, collapseDiv);
+            const codeBlocks = collapseDiv.querySelectorAll('pre code');
+            if (codeBlocks.length > 0) {
+                codeBlocks.forEach((codeBlock) => {
+                    answerCodeBlocks.push(codeBlock);
+                    console.log(`找到练习答案代码块内容:`, codeBlock.textContent.trim());
+                });
+            } else {
+                console.warn(`练习答案区域 ${index + 1} 中未找到 
!`);
+            }
+        });
+
+        // 合并所有代码块
+        return [...exampleCodeBlocks, ...answerCodeBlocks];
+    }
+
     // 主函数
     function runAutomation() {
-        // 修改按钮状态
         const button = document.getElementById('dolphin-auto-btn');
         setButtonState(button, 'running');
-        
-        // 1. 查找代码块
-        let codeBlocks = document.querySelectorAll('pre > code.cm-s-ipython[class*="language-"]') ||
-                         document.querySelectorAll('pre > code[class*="language-"]') ||
-                         document.querySelectorAll('pre > code');
-                         
-        console.log(`找到 ${codeBlocks.length} 个代码块`);
-        
-        // 2. 查找编辑器
-        const codeMirrors = document.querySelectorAll('.CodeMirror');
-        console.log(`找到 ${codeMirrors.length} 个编辑器`);
-        
-        if (codeBlocks.length > 0 && codeMirrors.length > 0) {
-            showNotification(`正在填充 ${Math.min(codeBlocks.length, codeMirrors.length)} 个代码块...`, 'info');
-            
-            // 3. 将代码块内容复制到编辑器
-            codeBlocks.forEach((codeBlock, index) => {
+
+        // 查找所有目标代码块
+        const allCodeBlocks = findAllCodeBlocks();
+        console.log(`共找到 ${allCodeBlocks.length} 个有效代码块:`);
+        allCodeBlocks.forEach((codeBlock, index) => {
+            console.log(`代码块 ${index + 1}:`, codeBlock.textContent.trim());
+        });
+
+        // 查找所有 CodeMirror 编辑器实例
+        const codeMirrors = Array.from(document.querySelectorAll('.CodeMirror'))
+            .map(cmElement => cmElement.CodeMirror)
+            .filter(cmInstance => !!cmInstance);
+
+        console.log(`找到 ${codeMirrors.length} 个 CodeMirror 实例:`);
+
+        if (allCodeBlocks.length > 0 && codeMirrors.length > 0) {
+            showNotification(`正在填充 ${Math.min(allCodeBlocks.length, codeMirrors.length)} 个代码块...`, 'info');
+
+            // 将代码块内容复制到编辑器
+            allCodeBlocks.forEach((codeBlock, index) => {
                 if (index < codeMirrors.length) {
                     const codeText = extractCodeText(codeBlock);
-                    const cm = codeMirrors[index].CodeMirror;
-                    
+                    const cm = codeMirrors[index];
                     if (cm) {
                         cm.setValue(codeText);
                         console.log(`已填充代码块 ${index + 1}`);
@@ -351,33 +264,27 @@
                     }
                 }
             });
-            
-            // 4. 点击保存按钮
+
+            // 点击保存按钮
             console.log('准备点击保存按钮...');
             showNotification('正在保存笔记本...', 'info');
-            
             const saveButton = document.querySelector('.btn-wrap .btn-name') || 
-                            document.querySelector('[title="保存"]') || 
-                            [...document.querySelectorAll('button')].find(btn => 
-                                btn.textContent.includes('保存'));
-            
+                              document.querySelector('[title="保存"]') || 
+                              [...document.querySelectorAll('button')].find(btn => 
+                                  btn.textContent.includes('保存'));
             if (saveButton) {
                 saveButton.click();
                 console.log('已点击保存按钮');
-                
-                // 5. 等待保存成功后重启
+                // 等待保存成功后重启
                 waitForMessage(() => {
                     console.log('准备点击重启按钮...');
                     showNotification('正在重启笔记本...', 'info');
-                    
                     const menuItems = [...document.querySelectorAll('.ivu-dropdown-item, [role="menuitem"], .menu-item')];
                     const possibleTexts = ['重启 & 运行所有', '重启并运行所有', '重启&运行所有', '重启和运行所有'];
-                    
                     const restartButton = menuItems.find(item => {
                         const text = item.textContent.trim();
                         return possibleTexts.some(pt => text.includes(pt));
                     });
-                    
                     if (restartButton) {
                         console.log('找到重启按钮,点击中...');
                         restartButton.click();
@@ -387,7 +294,6 @@
                         console.error('未找到重启按钮!');
                         console.log('可用菜单项:');
                         menuItems.forEach((item, i) => console.log(`${i+1}. "${item.textContent.trim()}"`));
-                        
                         // 执行遇到错误,通知用户
                         showNotification('自动化执行失败:未找到重启按钮!', 'error');
                         setButtonState(button, 'failed');
@@ -432,14 +338,12 @@
             min-width: 140px;
             font-weight: 600;
         `;
-        
         // 鼠标悬停效果,缓慢放大并加深颜色
         button.addEventListener('mouseover', () => {
             button.style.transform = 'translateY(-2px)';
             button.style.boxShadow = '0 5px 15px rgba(33, 150, 243, 0.4)';
             button.style.backgroundColor = '#1976D2';
         });
-        
         button.addEventListener('mouseout', () => {
             button.style.transform = '';
             button.style.boxShadow = '0 3px 10px rgba(33, 150, 243, 0.3)';
@@ -447,7 +351,6 @@
                 button.style.backgroundColor = '#2196F3';
             }
         });
-        
         // 添加点击波纹效果
         button.addEventListener('mousedown', (e) => {
             const ripple = document.createElement('span');
@@ -455,7 +358,6 @@
             const size = Math.max(rect.width, rect.height);
             const x = e.clientX - rect.left - size / 2;
             const y = e.clientY - rect.top - size / 2;
-            
             ripple.style.cssText = `
                 position: absolute;
                 top: ${y}px;
@@ -468,21 +370,17 @@
                 animation: ripple 0.6s linear;
                 pointer-events: none;
             `;
-            
             button.appendChild(ripple);
-            
             setTimeout(() => {
                 ripple.remove();
             }, 600);
         });
-        
         // 点击事件
         button.addEventListener('click', () => {
             if (!button.disabled) {
                 runAutomation();
             }
         });
-        
         document.body.appendChild(button);
         return button;
     }
@@ -491,7 +389,6 @@
     function init() {
         addStyles();
         const button = createFloatingButton();
-        
         // 显示欢迎通知
         setTimeout(() => {
             showNotification('海豚自动化工具已加载,点击按钮开始自动化操作', 'info');