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 + ` 元素
+ 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');