diff --git a/README.md b/README.md index 52e0340..87295d6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # 华谊题库助手 -支持在普通 HTTP / HTTPS 网页及获准访问的本地 HTML 文件中通过快捷键打开的本地题库油猴脚本:文字搜索、划词检索、自动选择已确认答案,并保留华谊练习平台的按库采集、CSV 导出与多题库导入。当前版本 **1.2.9**。 +支持在普通 HTTP / HTTPS 网页及获准访问的本地 HTML 文件中通过快捷键打开的本地题库油猴脚本:文字搜索、划词检索、自动选择已确认答案,并保留华谊练习平台的按库采集、CSV 导出与多题库导入。当前版本 **1.2.10**。 -1.2.9 明确匹配全部 HTTP、HTTPS 和本地 `file:` 页面。焦点在内嵌 iframe 时,**Shift + Alt + Q** 也能控制顶层的同一个面板;iframe 中不另建面板、不采集或保存数据。主页面在 window 捕获阶段处理快捷键,避免页面在 document 层拦截后失效。保持每页默认隐藏、自动答题默认关闭,保留1.2.8的重置题库功能。 +1.2.10 将默认显示/隐藏快捷键简化为 **Alt + Q**,原来的 **Shift + Alt + Q** 仍可使用。普通网页、本地 HTML 和内嵌 iframe 的快捷键行为一致。每页默认隐藏及自动答题默认关闭的行为保留。 + +1.2.9 明确匹配全部 HTTP、HTTPS 和本地 `file:` 页面。焦点在内嵌 iframe 时,显示/隐藏快捷键也能控制顶层的同一个面板;iframe 中不另建面板、不采集或保存数据。主页面在 window 捕获阶段处理快捷键,避免页面在 document 层拦截后失效。保持每页默认隐藏、自动答题默认关闭,保留1.2.8的重置题库功能。 1.2.8 新增“重置全部题库”,用于清除旧版或错误导入的数据。确认后清空本地全部题库、答案、冲突和四类历史修复备份,取消正在进行的导入及待保存任务,停止自动选择和判断题补齐,并暂停采集。新的题库存储与重置前隔离,旧标签页或旧版脚本再次写入的数据不会混回。升级本身保留已有资料,需要清空时再手动重置。 @@ -36,7 +38,7 @@ 1. 在浏览器中安装并启用 Tampermonkey(油猴)。 2. 打开油猴管理面板,选择“新建脚本”,将安装文件的全部内容粘贴进去并保存。 -3. 打开或刷新普通网页,按 **Shift + Alt + Q** 显示题库助手,再按一次隐藏。每次加载页面都默认完全隐藏,无悬浮按钮;旧版保存的打开状态也会重置。 +3. 打开或刷新普通网页,按 **Alt + Q** 显示题库助手,再按一次隐藏。每次加载页面都默认完全隐藏,无悬浮按钮;旧版保存的打开状态也会重置。 4. 在华谊练习页面,正常加载的题目会自动保存;只有取得可信提交记录或标准反馈时才保存已确认答案。安装后应重新进入练习,以便观察首次题目响应。普通网页只使用本地题库检索,不观察其网络请求或自动采集表单。 5. 在“文字搜索”中输入或粘贴至少两个字符,按回车或点“搜索”。也可以在页面划选题干后松开鼠标自动查询。 6. 在“题库 / 导出”中检查实际题库名、范围内题数及文件名预览,再导出 CSV 或备份 JSON。已识别的练习页默认当前题库;普通网页默认全部已存题库。 @@ -80,7 +82,7 @@ 启动后从第1题逐题核对题库、位置、题目ID、完整题干和选项。已有可信标准答案的题目会跳过作答;待补题通过正常选项控件选择“正确”一次,以触发网站返回标准反馈。**这个选择只是取得反馈的练习作答,不能当作正确答案保存**;网站给出的标准可能是“正确”或“错误”。只有当前网页标准反馈与同题可信入库记录一致后,才点击正常“下一题”。身份、来源或答案不一致,或者反馈迟迟不到时停止,保留当前结果供核对。 -可随时点击“停止补齐”。暂停采集、切换题库、离开页面、导入题库或发生存储异常时也会停止。隐藏或收起面板后仍会继续;隐藏后按 **Shift + Alt + Q** 恢复面板,收起后点击“展开”,再点击“停止补齐”。刷新后保持关闭,不会自动恢复。启动补齐会先停止普通自动选择,启用连续自动选择或点击“答当前题”也会先停止补齐,避免两种模式同时操作。 +可随时点击“停止补齐”。暂停采集、切换题库、离开页面、导入题库或发生存储异常时也会停止。隐藏或收起面板后仍会继续;隐藏后按 **Alt + Q** 恢复面板,收起后点击“展开”,再点击“停止补齐”。刷新后保持关闭,不会自动恢复。启动补齐会先停止普通自动选择,启用连续自动选择或点击“答当前题”也会先停止补齐,避免两种模式同时操作。 补齐模式会按上述核验结果点击“下一题”,但不会点击结束、交卷或末题提交按钮。只有全部标准题目位置均有可信答案时才显示完成;已有完整发布题库时仍核对当前题目身份、答案映射及可信来源,网页已有标准反馈时还会比较是否一致。 @@ -112,8 +114,8 @@ ## 清除旧数据并重新导入 -1. 将油猴中的原脚本完整更新为 [`dist/huayi-question-bank.user.js`](dist/huayi-question-bank.user.js)(1.2.9),保存并刷新网页。 -2. 按 **Shift + Alt + Q** 显示面板。在“题库 / 导出”的“已保存题库”下方,或“设置”顶部,点击 **重置全部题库**;油猴菜单也有同名入口。 +1. 将油猴中的原脚本完整更新为 [`dist/huayi-question-bank.user.js`](dist/huayi-question-bank.user.js)(1.2.10),保存并刷新网页。 +2. 按 **Alt + Q** 显示面板。在“题库 / 导出”的“已保存题库”下方,或“设置”顶部,点击 **重置全部题库**;油猴菜单也有同名入口。 3. 确认提示会说明清除范围。如需保留旧资料,先取消并点击“备份 JSON”。确认重置后,本地题数归零,四类历史修复备份一起清除,正在读取的导入文件也不会在稍后写回。 4. 点击“导入多个题库”,选择仓库中已核验的 [`合并题库JSON`](题库/华谊集团化工总控工(高级)-合并题库.json) 或 [`合并CSV`](题库/华谊集团化工总控工(高级)-合并题库.csv),导入后应为 **1884题**。只需选择其中一种;也可导入所需的分库文件。 5. 重置后采集保持暂停,刷新后也不会自动恢复。需要重新采集时,再点击“继续采集”;连续自动选择和判断题补齐仍须手动开启。 @@ -127,9 +129,9 @@ | 操作 | 效果 | 恢复方式 | | --- | --- | --- | | 右上角“收起” | 只显示面板标题栏 | 点击“展开” | -| 打开或刷新网页 | 默认完全隐藏,无悬浮按钮 | **Shift + Alt + Q** | -| 右上角“隐藏” | 面板完全隐藏 | **Shift + Alt + Q** | -| **Shift + Alt + Q** | 在显示与隐藏之间切换 | 再按一次 | +| 打开或刷新网页 | 默认完全隐藏,无悬浮按钮 | **Alt + Q** | +| 右上角“隐藏” | 面板完全隐藏 | **Alt + Q** | +| **Alt + Q** | 在显示与隐藏之间切换 | 再按一次 | | 油猴菜单“显示题库助手” | 手动显示面板 | 右上角隐藏或快捷键 | 复制开关等设置会保留;面板在每次加载后均重置为隐藏,连续自动选择和判断题补齐均关闭。隐藏时划选题干仍会更新查询,但不会重新弹出面板。隐藏面板不会停止当前页已开启的连续选择或判断题补齐;请恢复面板后关闭连续选择开关,或点击“停止补齐”。暂停采集会停止判断题补齐。 diff --git a/build.cjs b/build.cjs index 039bd38..404ae6b 100644 --- a/build.cjs +++ b/build.cjs @@ -4,7 +4,7 @@ const root = __dirname; const header = `// ==UserScript== // @name 华谊题库助手 · CSV导出与划词检索 // @namespace local.huayi.question-bank -// @version 1.2.9 +// @version 1.2.10 // @description 全网页快捷键唤出、本地题库文字检索与自动选择答案,按库导入导出;每页默认完全隐藏。 // @match http://*/* // @match https://*/* diff --git a/dist/huayi-question-bank.user.js b/dist/huayi-question-bank.user.js index ef1e944..7183d2d 100644 --- a/dist/huayi-question-bank.user.js +++ b/dist/huayi-question-bank.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name 华谊题库助手 · CSV导出与划词检索 // @namespace local.huayi.question-bank -// @version 1.2.9 +// @version 1.2.10 // @description 全网页快捷键唤出、本地题库文字检索与自动选择答案,按库导入导出;每页默认完全隐藏。 // @match http://*/* // @match https://*/* @@ -2377,7 +2377,7 @@ if (typeof module !== 'undefined' && module.exports) module.exports = QBCollecto // UI and persistence stay inside the userscript sandbox. Page data is rendered as text. (() => { const FRAME_SHORTCUT = 'huayi-qb.frame-shortcut.v1'; - const isToggleShortcut = event => event.altKey && event.shiftKey && !event.ctrlKey && !event.metaKey && + const isToggleShortcut = event => event.altKey && !event.ctrlKey && !event.metaKey && (event.code === 'KeyQ' || (!event.code && String(event.key).toLowerCase() === 'q')) && !event.repeat; // Frames only forward the explicit shortcut. Keep one panel and one set of // storage/network handlers in the top page; never relay frame text or answers. @@ -3244,7 +3244,7 @@ if (typeof module !== 'undefined' && module.exports) module.exports = QBCollecto const ui = node('div'); ui.innerHTML = `
-
题库助手
HUAYI · 本地练习资料
+
题库助手
HUAYI · 本地练习资料
正在识别题库…
@@ -3261,7 +3261,7 @@ if (typeof module !== 'undefined' && module.exports) module.exports = QBCollecto
-
`; + `; shadow.append(...ui.childNodes); (document.body || document.documentElement).append(root); panel = shadow.getElementById('qb-panel'); launcher = shadow.getElementById('qb-launcher'); @@ -3385,7 +3385,7 @@ if (typeof module !== 'undefined' && module.exports) module.exports = QBCollecto window.addEventListener('focus', synchronizeReset); document.addEventListener('visibilitychange', () => { if (!document.hidden) synchronizeReset(); }); if (typeof GM_registerMenuCommand === 'function') { - GM_registerMenuCommand('显示题库助手(Alt+Shift+Q)', () => setMode('open')); + GM_registerMenuCommand('显示题库助手(Alt+Q)', () => setMode('open')); GM_registerMenuCommand('隐藏题库助手', () => setMode('hidden')); GM_registerMenuCommand('重置全部本地题库', resetLibrary); GM_registerMenuCommand('导出整理前原始题库备份', exportCleanupBackup); diff --git a/package.json b/package.json index e1b0c5b..3b84a67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "huayi-question-bank", - "version": "1.2.9", + "version": "1.2.10", "private": true, "description": "华谊练习题库本地采集、CSV 导出与划词检索油猴脚本", "scripts": { diff --git a/src/app.js b/src/app.js index c95a6cd..97f3d46 100644 --- a/src/app.js +++ b/src/app.js @@ -1,7 +1,7 @@ // UI and persistence stay inside the userscript sandbox. Page data is rendered as text. (() => { const FRAME_SHORTCUT = 'huayi-qb.frame-shortcut.v1'; - const isToggleShortcut = event => event.altKey && event.shiftKey && !event.ctrlKey && !event.metaKey && + const isToggleShortcut = event => event.altKey && !event.ctrlKey && !event.metaKey && (event.code === 'KeyQ' || (!event.code && String(event.key).toLowerCase() === 'q')) && !event.repeat; // Frames only forward the explicit shortcut. Keep one panel and one set of // storage/network handlers in the top page; never relay frame text or answers. @@ -868,7 +868,7 @@ const ui = node('div'); ui.innerHTML = `
-
题库助手
HUAYI · 本地练习资料
+
题库助手
HUAYI · 本地练习资料
正在识别题库…
@@ -885,7 +885,7 @@
-
`; + `; shadow.append(...ui.childNodes); (document.body || document.documentElement).append(root); panel = shadow.getElementById('qb-panel'); launcher = shadow.getElementById('qb-launcher'); @@ -1009,7 +1009,7 @@ window.addEventListener('focus', synchronizeReset); document.addEventListener('visibilitychange', () => { if (!document.hidden) synchronizeReset(); }); if (typeof GM_registerMenuCommand === 'function') { - GM_registerMenuCommand('显示题库助手(Alt+Shift+Q)', () => setMode('open')); + GM_registerMenuCommand('显示题库助手(Alt+Q)', () => setMode('open')); GM_registerMenuCommand('隐藏题库助手', () => setMode('hidden')); GM_registerMenuCommand('重置全部本地题库', resetLibrary); GM_registerMenuCommand('导出整理前原始题库备份', exportCleanupBackup); diff --git a/tests/global-browser.test.cjs b/tests/global-browser.test.cjs index e9a8c84..dbcadee 100644 --- a/tests/global-browser.test.cjs +++ b/tests/global-browser.test.cjs @@ -63,13 +63,13 @@ test('全网址:HTTP、HTTPS、内网地址、本地文件及iframe快捷键', await page.goto(url); await page.locator('#huayi-qb-root').waitFor({ state: 'attached' }); assert.equal(await page.locator('#qb-panel').isVisible(), false); - await page.keyboard.press('Alt+Shift+Q'); + await page.keyboard.press('Alt+Q'); assert.equal(await page.locator('#qb-panel').isVisible(), true); await page.locator('#qb-search').fill('全网页核验题'); await page.locator('#qb-search').press('Enter'); assert.match(await page.locator('#qb-results').innerText(), /正确内容/); assert.equal(await page.evaluate(() => window.fetch === window.__originalFetch), true, '普通页面不观察业务请求'); - await page.keyboard.press('Alt+Shift+Q'); + await page.keyboard.press('Alt+Q'); assert.equal(await page.locator('#qb-panel').isVisible(), false); } finally { await page.close(); } }); @@ -79,10 +79,12 @@ test('全网址:HTTP、HTTPS、内网地址、本地文件及iframe快捷键', try { await page.goto('https://public.example.test/blocks-document'); await page.locator('#huayi-qb-root').waitFor({ state: 'attached' }); - await page.keyboard.press('Alt+Shift+Q'); + await page.keyboard.press('Alt+Q'); assert.equal(await page.locator('#qb-panel').isVisible(), true); - await page.evaluate(() => window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Q', altKey: true, shiftKey: true }))); + await page.evaluate(() => window.dispatchEvent(new KeyboardEvent('keydown', { key: 'q', altKey: true }))); assert.equal(await page.locator('#qb-panel').isVisible(), false); + await page.keyboard.press('Alt+Shift+Q'); + assert.equal(await page.locator('#qb-panel').isVisible(), true, '旧快捷键仍可使用'); } finally { await page.close(); } }); await t.test('跨域及嵌套iframe中按快捷键,控制顶层唯一面板且子页不启动存储或采集', async () => { @@ -98,9 +100,9 @@ test('全网址:HTTP、HTTPS、内网地址、本地文件及iframe快捷键', assert.equal(await frame.evaluate(() => window.__globalMenus.length), 0); assert.equal(await frame.evaluate(() => window.fetch === window.__originalFetch), true); } - await child.locator('#frame-input').press('Alt+Shift+Q'); + await child.locator('#frame-input').press('Alt+Q'); await page.locator('#qb-panel').waitFor({ state: 'visible' }); - await nested.locator('#frame-input').press('Alt+Shift+Q'); + await nested.locator('#frame-input').press('Alt+Q'); await page.locator('#qb-panel').waitFor({ state: 'hidden' }); assert.equal(await page.locator('#huayi-qb-root').count(), 1); } finally { await page.close(); }