Claude connector: the left sidebar's web app (Telegram, WhatsApp, X, the AI chat) is tab 0
The quick-link panel is one WebContentsView outside `tabs`, so the connector could not see a web app opened in the sidebar, and the signed-in session there (Telegram's channel account, for one) was out of reach. tabControl.list() now ends with the panel as tab 0 while it holds a web page, tagged with the app's name; webContents, navigate, close and activate handle it (activate reopens the panel, close closes it). It runs the same preload as a web tab and still needs the site approved, so it hands out nothing a tab doesn't. Listed last so it is never picked as the active tab when a tool call omits tab_id.
This commit is contained in:
parent
5c270ef62f
commit
1457de96b8
3 changed files with 44 additions and 5 deletions
|
|
@ -96,7 +96,7 @@ Claude Desktop ─stdio─► stdio-shim.cjs ──HTTP────────
|
|||
|
||||
| call | does |
|
||||
| --- | --- |
|
||||
| `list()` | every tab: id, title, url, kind (`web`/`settings`/`addon`/`home`), active, pending |
|
||||
| `list()` | every tab: id, title, url, kind (`web`/`settings`/`addon`/`home`), active, pending; then the left sidebar's web app (Telegram, WhatsApp, X, the AI chat) as tab `0` with `sidebar: <app name>`, while it holds a web page. It is one view, not a tab, and runs the web tabs' preload, so it hands out nothing a tab doesn't; it comes last so it is never taken for the active tab. `activate` reopens the panel, `close` closes it, `navigate` loads in it |
|
||||
| `webContents(id)` | the tab's webContents, after loading a dormant tab and thawing a frozen one; throws for non-web tabs |
|
||||
| `open(url, {background})` | new tab through main's own `createTab` (so BCNR names resolve as typed) |
|
||||
| `navigate(id, url)` | main's `navigateTab` |
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ class Controller {
|
|||
const rows = this.tabControl.list().map((t) => {
|
||||
const o = originOf(t.url);
|
||||
const approved = o && this.policy.isAllowed(o);
|
||||
const base = { id: t.id, active: t.active, window: t.window, kind: t.kind, controlled: this.drivers.has(t.id) };
|
||||
const base = { id: t.id, active: t.active, window: t.window, kind: t.kind, controlled: this.drivers.has(t.id), ...(t.sidebar ? { sidebar: t.sidebar } : {}) };
|
||||
if (t.kind !== "web") return { ...base, title: t.kind === "settings" ? "Settings" : t.title };
|
||||
return approved ? { ...base, title: t.title, url: t.url } : { ...base, host: hostOf(t.url), approved: false };
|
||||
});
|
||||
|
|
|
|||
45
main.js
45
main.js
|
|
@ -3212,13 +3212,35 @@ function tabControlKind(t) {
|
|||
const u = (t.pending && t.pending.url) || (() => { try { return t.view.webContents.getURL(); } catch { return ""; } })() || t.url || "";
|
||||
return /^(?:https?|bns):\/\//i.test(u) ? "web" : "home";
|
||||
}
|
||||
// The left sidebar's web app (Telegram, WhatsApp, X, the AI chat) is one
|
||||
// view, not a tab. While it holds a web page the connector sees it as tab 0,
|
||||
// listed last so it is never taken for "the active tab". It runs the same
|
||||
// preload as a web tab, so it hands out nothing a tab doesn't.
|
||||
const SIDEBAR_CTRL_ID = 0;
|
||||
let lastQuickLinkId = null; // the web app the panel last showed, to reopen it
|
||||
function sidebarCtrlEntry() {
|
||||
if (!quickPanel) return null;
|
||||
const wc = quickPanel.webContents;
|
||||
let url = "", title = "";
|
||||
try { url = wc.getURL(); title = wc.getTitle(); } catch { return null; }
|
||||
if (!/^(?:https?|bns):\/\//i.test(url)) return null;
|
||||
const qid = activeQuickLinkId || lastQuickLinkId;
|
||||
const L = qid === "llm" ? { title: "AI" } : ((settings.quickLinks || []).find((x) => x.id === qid) || quickLinkFor(url));
|
||||
return { id: SIDEBAR_CTRL_ID, title, url, kind: "web", sidebar: (L && L.title) || "Sidebar", active: !!activeQuickLinkId && !leftPanelId, pending: false, window: 0 };
|
||||
}
|
||||
function sidebarCtrl(id) {
|
||||
if (Number(id) !== SIDEBAR_CTRL_ID) return null;
|
||||
const e = sidebarCtrlEntry();
|
||||
if (!e) throw new Error("the sidebar has no web app open");
|
||||
return e;
|
||||
}
|
||||
function tabControlTab(id) {
|
||||
const t = tabById(Number(id));
|
||||
if (!t) throw new Error(`no tab ${id}`);
|
||||
return t;
|
||||
}
|
||||
const tabControlHook = {
|
||||
list: () => tabs.map((t) => ({
|
||||
list: () => [...tabs.map((t) => ({
|
||||
id: t.id,
|
||||
title: t.title || "",
|
||||
url: (t.pending && t.pending.url) || t.url || "",
|
||||
|
|
@ -3226,8 +3248,13 @@ const tabControlHook = {
|
|||
active: isShownTabId(t.id),
|
||||
pending: !!t.pending,
|
||||
window: Math.max(0, windows.indexOf(t.w)),
|
||||
})),
|
||||
})), sidebarCtrlEntry()].filter(Boolean),
|
||||
webContents: (id) => {
|
||||
if (sidebarCtrl(id)) {
|
||||
const wc = quickPanel.webContents;
|
||||
if (!wc || wc.isDestroyed()) throw new Error("the sidebar is closed");
|
||||
return wc;
|
||||
}
|
||||
const t = tabControlTab(id);
|
||||
if (tabControlKind(t) !== "web") throw new Error("only web pages can be controlled (not Settings, extension or home tabs)");
|
||||
if (t.pending) withWin(t.w, () => materializePending(t));
|
||||
|
|
@ -3243,6 +3270,12 @@ const tabControlHook = {
|
|||
return id;
|
||||
},
|
||||
navigate: (id, url, addonId) => {
|
||||
if (sidebarCtrl(id)) {
|
||||
if (!/^(?:https?|bns):\/\//i.test(url)) throw new Error("navigate needs an http(s) URL");
|
||||
console.log(`[addons] [${addonId}] navigate the sidebar app`);
|
||||
quickPanel.webContents.loadURL(url).catch(() => {});
|
||||
return;
|
||||
}
|
||||
const t = tabControlTab(id);
|
||||
if (tabControlKind(t) !== "web") throw new Error("only web pages can be navigated");
|
||||
if (!/^(?:https?|bns):\/\//i.test(url)) throw new Error("navigate needs an http(s) URL");
|
||||
|
|
@ -3250,12 +3283,17 @@ const tabControlHook = {
|
|||
return withWin(t.w, () => navigateTab(t.id, url));
|
||||
},
|
||||
close: (id, addonId) => {
|
||||
if (sidebarCtrl(id)) { console.log(`[addons] [${addonId}] close the sidebar app`); closeQuickPanel(); return; }
|
||||
const t = tabControlTab(id);
|
||||
if (tabControlKind(t) !== "web") throw new Error("only web tabs can be closed");
|
||||
console.log(`[addons] [${addonId}] close tab ${t.id}`);
|
||||
closeTab(t.id);
|
||||
},
|
||||
activate: (id) => { const t = tabControlTab(id); setActive(t.id); },
|
||||
activate: (id) => {
|
||||
const e = sidebarCtrl(id);
|
||||
if (e) { const q = activeQuickLinkId || lastQuickLinkId || quickLinkFor(e.url)?.id; if (q && !e.active) openQuickPanel(q); return; }
|
||||
const t = tabControlTab(id); setActive(t.id);
|
||||
},
|
||||
setStatus: (st) => {
|
||||
const next = {
|
||||
connected: !!st.connected,
|
||||
|
|
@ -10267,6 +10305,7 @@ function openQuickPanel(id, at) {
|
|||
if (leftPanelId) closeLeftPanel();
|
||||
if (activeQuickLinkId !== id) leftPanelMax = false;
|
||||
activeQuickLinkId = id;
|
||||
lastQuickLinkId = id;
|
||||
try {
|
||||
const cur = quickPanel.webContents.getURL();
|
||||
// Only re-navigate when the panel isn't already showing this origin —
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue