From dd8a4550c734c21045c20e09226d12d890ca8fdd Mon Sep 17 00:00:00 2001 From: PathGao <42336971+PathGao@users.noreply.github.com> Date: Tue, 27 Oct 2026 11:56:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=8D=E5=88=B6=E3=80=81=E4=B8=93=E6=B3=A8?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=EF=BC=9A=E6=B5=8F=E8=A7=88=E5=99=A8=E7=9A=84?= =?UTF-8?q?=E8=8B=B1=E6=96=87=E6=8A=A5=E9=94=99=E4=B8=8D=E5=86=8D=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 复制 Markdown 时剪贴板被拒(页面失去焦点等),提示「复制失败:无法访问剪贴板,点一下页面再试」,不再显示 Chrome 的英文原文。 专注模式启动时扩展刚更新,和视频页其他入口一样提示「扩展刚刚更新,请刷新网页」。 --- extension/content.js | 2 +- extension/errors.js | 5 +++-- extension/history/history.selftest.js | 2 +- extension/note-actions.js | 3 ++- extension/note-actions.selftest.js | 11 ++++++----- extension/sidepanel.selftest.js | 2 +- extension/triage/triage.selftest.js | 2 +- 7 files changed, 15 insertions(+), 12 deletions(-) diff --git a/extension/content.js b/extension/content.js index eb6237f7..b2df473b 100644 --- a/extension/content.js +++ b/extension/content.js @@ -1505,7 +1505,7 @@ function cleanupReaderFloatingArtifacts(playerHost = state.readingPlayerHost) { // Every entry into 专注模式 (the URL, a SPA video change, the popup): a failure is red on the page, the same sentence. function startReaderMode() { enterReaderMode().catch((error) => { - setReadingNotice(`${MdgErrors.TEXT.readerStartFailed}:${MdgErrors.messageOf(error)}`, { error: true }); + setReadingNotice(MdgErrors.reloaded(error) ? MdgErrors.TEXT.reloaded : `${MdgErrors.TEXT.readerStartFailed}:${MdgErrors.messageOf(error)}`, { error: true }); }); } diff --git a/extension/errors.js b/extension/errors.js index 378dd9fb..5187b291 100644 --- a/extension/errors.js +++ b/extension/errors.js @@ -101,11 +101,12 @@ nothingToCopy: "没有可复制的内容", nothingToDownload: "没有可下载的内容", nothingToWrite: "没有可写入 Obsidian 的内容", - // A copy to the clipboard failed (popup, side panel, 摘读记录, 分拣台); 「:原因」 follows, noClipboard without one. + // A copy to the clipboard failed (popup, side panel, 摘读记录, 分拣台); 「:原因」 follows (noClipboard when the clipboard refused). copyFailed: "复制失败", // 下载 .md failed (side panel, 摘读记录); 「:原因」 follows. downloadFailed: "下载失败", - noClipboard: "无法访问剪贴板", + // The page lost focus while the note was prepared, or the browser refused (Chrome's English NotAllowedError). + noClipboard: "无法访问剪贴板,点一下页面再试", // The side panel while the video page reads the subtitle for it. subtitleReading: "正在读取视频字幕…", // 分拣台's 收藏夹设置 / 关注设置 and their AI 参数; a failure is followed by 「:原因」. diff --git a/extension/history/history.selftest.js b/extension/history/history.selftest.js index 88125c30..aea6e4ce 100644 --- a/extension/history/history.selftest.js +++ b/extension/history/history.selftest.js @@ -343,7 +343,7 @@ assert.ok(/ { throw new Error("没有窗口"); } }; await act("ask"); assert.deepStrictEqual([els.status.textContent, red], ["打开侧边栏失败:没有窗口", true]); diff --git a/extension/note-actions.js b/extension/note-actions.js index ce571fa1..2c09aef0 100644 --- a/extension/note-actions.js +++ b/extension/note-actions.js @@ -28,7 +28,8 @@ try { const resp = note || (await resolve(kind, request, send, isStale, dialogs)); if (resp.stop) return { ok: true, done: false, text: resp.stop, remedies: [] }; - if (kind === "copy") await navigator.clipboard.writeText(resp.markdown); + // Any refusal says noClipboard, not Chrome's English ("Document is not focused"); no cause, its code is a DOMException's. + if (kind === "copy") await navigator.clipboard.writeText(resp.markdown).catch(() => Promise.reject(new Error(MdgErrors.TEXT.noClipboard))); if (kind === "download") MdgDownload.text(resp.filename, resp.markdown); return { ok: true, done: true, text: DONE[kind] || resp.text, remedies: [] }; } catch (error) { diff --git a/extension/note-actions.selftest.js b/extension/note-actions.selftest.js index 770fb8ce..958f85ca 100644 --- a/extension/note-actions.selftest.js +++ b/extension/note-actions.selftest.js @@ -106,11 +106,12 @@ const plain = (r) => JSON.parse(JSON.stringify(r)); const r = await run(kind, { request, send: fail({ ok: false, error: T.nothingToWrite, code: "NOTE_EMPTY" }) }); assert.deepStrictEqual([r.ok, r.text, [...r.remedies]], [false, want, []], kind); } - // The clipboard refused: 复制失败, 无法访问剪贴板 when it gave no words. - ctx.navigator.clipboard.writeText = async () => { throw new Error(""); }; - assert.strictEqual((await run("copy", { note: { markdown: "x" } })).text, `${T.copyFailed}:${T.noClipboard}`); - ctx.navigator.clipboard.writeText = async () => { throw new Error("没有权限"); }; - assert.strictEqual((await run("copy", { note: { markdown: "x" } })).text, `${T.copyFailed}:没有权限`); + // The clipboard refused: 复制失败:无法访问剪贴板, never Chrome's English, with or without words. + for (const message of ["", "Failed to execute 'writeText' on 'Clipboard': Document is not focused."]) { + ctx.navigator.clipboard.writeText = async () => { throw Object.assign(new Error(message), { name: "NotAllowedError", code: 0 }); }; + const r = await run("copy", { note: { markdown: "x" } }); + assert.deepStrictEqual([r.text, r.code], [`${T.copyFailed}:${T.noClipboard}`, undefined], message); + } // The extension reloaded under the video page: its own sentence. assert.strictEqual((await run("write", { request, send: async () => { throw new Error("Extension context invalidated."); } })).text, T.reloaded); diff --git a/extension/sidepanel.selftest.js b/extension/sidepanel.selftest.js index c7336f3c..137f1db3 100644 --- a/extension/sidepanel.selftest.js +++ b/extension/sidepanel.selftest.js @@ -1110,7 +1110,7 @@ const tick = () => new Promise((r) => setImmediate(r)); // C350: a reply's 复制 Markdown says 已复制 Markdown, like the export menu's. ctx.navigator.clipboard = { writeText: async () => {} }; await ctx.copyMarkdown("x"); - assert.deepStrictEqual(notices, [["复制失败:无法访问剪贴板", true], ["已复制 Markdown", false]]); + assert.deepStrictEqual(notices, [["复制失败:无法访问剪贴板,点一下页面再试", true], ["已复制 Markdown", false]]); delete ctx.navigator.clipboard; [ctx.showConversationContextNotice, ctx.chrome.runtime.sendMessage] = prev; } diff --git a/extension/triage/triage.selftest.js b/extension/triage/triage.selftest.js index 55fc3510..1b1c8dd9 100644 --- a/extension/triage/triage.selftest.js +++ b/extension/triage/triage.selftest.js @@ -3996,7 +3996,7 @@ const unfavOnlyOf = (d) => Object.fromEntries(Object.entries(d).filter(([, v]) = t.el.writeFormat.value = "digest"; t.navigator = { clipboard: { writeText: async () => { throw new Error("没有权限"); } } }; await t.exportDigest("copy"); - assert.ok(t.el.writeProgress.textContent === "复制失败:没有权限" && cls.has("fail-text"), "copy failure in red"); + assert.ok(t.el.writeProgress.textContent === "复制失败:无法访问剪贴板,点一下页面再试" && cls.has("fail-text"), "copy failure in red"); handlers["triage-export"] = (m) => ({ ok: true, data: { path: `库/${m.filename}`, text: `已写入 Obsidian:库/${m.filename}` } }); await t.exportDigest("obsidian"); assert.ok(/^已写入 Obsidian:库\/B站摘录-/.test(t.el.writeProgress.textContent) && !cls.has("fail-text"), t.el.writeProgress.textContent);