Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,15 +273,6 @@ async function videoTabPayload(tabId, opened) {
}
}

async function openAiSidepanelForTab(tabId) {
if (chrome.sidePanel?.open) {
await chrome.sidePanel.open({ tabId });
return;
}

throw new Error("当前浏览器不支持扩展侧边栏");
}

const FETCH_TIMEOUT_MS = 15000;

// Obsidian's Local REST API and the cover images written into the vault; a hung request would block the write forever.
Expand Down Expand Up @@ -496,7 +487,7 @@ const BACKGROUND_HANDLERS = {
const tabId = Number(message.tabId || sender?.tab?.id || 0) || 0;
if (!tabId) throw new Error(MdgErrors.TEXT.noTab);
// Before any await: sidePanel.open needs the click that sent the message.
const openPromise = openAiSidepanelForTab(tabId);
const openPromise = MdgErrors.openSidepanel({ tabId });
// Awaited once settings load; without a handler now, a fast rejection is reported as uncaught.
openPromise.catch(() => {});
const settings = await getMergedSettings();
Expand Down
13 changes: 12 additions & 1 deletion extension/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
netDown: "网络断了",
// Asking Chrome or the background to open the side panel failed (popup, video page, 摘读记录, 分拣台); 「:原因」 follows.
sidepanelOpenFailed: "打开侧边栏失败",
// The browser has no extension side panel, or turned it off (Chromium's "API Unavailable"): openSidepanel's reason.
sidepanelUnsupported: "当前浏览器不支持扩展侧边栏",
// 继续问 on a 摘读记录 record that is neither a video nor a web page (a chrome:// or extension page).
notVideo: "这个页面不能读取,不能继续问",
// The side panel's 停止 ended the reply (the background's port message, the panel's own stop).
Expand Down Expand Up @@ -369,6 +371,15 @@
return send();
}

// chrome.sidePanel.open for every entry (background, 摘读记录, 分拣台), called inside the click before any await. A
// browser without the API, or with it turned off (Chromium rejects with "API Unavailable"), says sidepanelUnsupported.
const openSidepanel = (options) =>
chrome.sidePanel?.open
? chrome.sidePanel.open(options).catch((error) => {
throw /API Unavailable/i.test(String(error?.message || "")) ? new Error(TEXT.sidepanelUnsupported, { cause: error }) : error;
})
: Promise.reject(new Error(TEXT.sidepanelUnsupported));

// What chrome.runtime calls throw in a content script whose extension was reloaded (badges.js checks
// chrome.runtime.id instead).
const reloaded = (error) => /Extension context invalidated/.test(String(error?.message || ""));
Expand All @@ -394,5 +405,5 @@
else node.removeAttribute("aria-description");
}

globalThis.MdgErrors = Object.freeze({ TEXT, LABEL, action, remedies, pageCode, pageText, grantPages, subtitleCode, text, messageOf, fromResp, grantOrigin, noReceiver, pageSilent, tabMessage, reloaded, escapeHtml, setReason, subtitleMissing, aiTimeout, aiStalled, upTagTitle, noAccess, NET_TIMEOUT_MS, netTimeout, CODE, TIMEOUT_STATUS });
globalThis.MdgErrors = Object.freeze({ TEXT, LABEL, action, remedies, pageCode, pageText, grantPages, subtitleCode, text, messageOf, fromResp, grantOrigin, noReceiver, pageSilent, tabMessage, openSidepanel, reloaded, escapeHtml, setReason, subtitleMissing, aiTimeout, aiStalled, upTagTitle, noAccess, NET_TIMEOUT_MS, netTimeout, CODE, TIMEOUT_STATUS });
})();
11 changes: 11 additions & 0 deletions extension/errors.selftest.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ assert.strictEqual(E.messageOf(null, "兜底"), "兜底");
for (const file of [...sources, "note.js"]) {
assert.ok(!/拿不到字幕|字幕抓取失败[:(]\$\{|无字幕[,。]/.test(read(file)), `${file} words a missing subtitle itself; use MdgErrors.subtitleMissing`);
}
// openSidepanel: Chromium's "API Unavailable" (side panel turned off) and a missing API say the same sentence; any other
// rejection passes on as it is.
const opens = async (open) => {
ctx.chrome.sidePanel = open && { open };
return E.openSidepanel({ tabId: 1 }).then(() => "opened", (error) => error.message);
};
assert.strictEqual(await opens(async () => {}), "opened");
assert.strictEqual(await opens(async () => { throw new Error("API Unavailable"); }), E.TEXT.sidepanelUnsupported);
assert.strictEqual(await opens(null), E.TEXT.sidepanelUnsupported);
assert.strictEqual(await opens(async () => { throw new Error("No active side panel for tabId: 1"); }), "No active side panel for tabId: 1");

console.log("errors selftest ok");
})().catch((e) => {
console.error(e);
Expand Down
4 changes: 2 additions & 2 deletions extension/history/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ const readsFromPage = (g) => isWebPage(g) || Boolean(g.context.videoId && MdgSit
// click (sidePanel.open needs the gesture), then its tab in this window is focused, or opened, and the panel asked for
// the conversation as a video's is (askSidepanel).
function askAboutPage(group) {
const opening = chrome.sidePanel.open({ windowId: OWN_TAB?.windowId });
const opening = MdgErrors.openSidepanel({ windowId: OWN_TAB?.windowId });
setStatus(MdgErrors.TEXT.sidepanelOpening);
void (async () => {
try {
Expand Down Expand Up @@ -782,7 +782,7 @@ els.list.addEventListener("click", (event) => {
MdgLimits.askSidepanel({ tabId: OWN_TAB?.id, contextRef: group.context });
const opening = MdgErrors.TEXT.sidepanelOpening;
setStatus(opening);
chrome.sidePanel.open({ tabId: OWN_TAB?.id }).then(
MdgErrors.openSidepanel({ tabId: OWN_TAB?.id }).then(
() => els.status.textContent === opening && setStatus(""),
(error) => setStatus(`${MdgErrors.TEXT.sidepanelOpenFailed}:${error.message}`, true)
);
Expand Down
2 changes: 1 addition & 1 deletion extension/triage/fav-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ let ownTabId;
function askAi(it) {
const contextRef = { site: "bilibili", videoId: it.bvid, title: it.title, author: it.upper, url: videoUrl(it.bvid) };
MdgLimits.askSidepanel({ tabId: ownTabId, contextRef });
chrome.sidePanel.open({ tabId: ownTabId }).catch((err) => toast(`${MdgErrors.TEXT.sidepanelOpenFailed}:${err.message}`, true));
MdgErrors.openSidepanel({ tabId: ownTabId }).catch((err) => toast(`${MdgErrors.TEXT.sidepanelOpenFailed}:${err.message}`, true));
}

// Busy button: disabled + aria-busy (tokens.css draws the spinner) + 「…中」 text; false restores it.
Expand Down
Loading