diff --git a/PENDING.md b/PENDING.md index 522e114b..cf2aebc1 100644 --- a/PENDING.md +++ b/PENDING.md @@ -50,7 +50,10 @@ code change. closed panels too so a client can see what exists; `webContents(-N)` throws " is not open; ask the user to open the panel from the sidebar first" until the user docks it. Password-field refusal and the - action log still apply. + action log still apply. The connector panel.html now splits site + approvals from panel approvals into their own sections, with the add-on's + own title ("Aegis panel") above the mono origin string and a ✕ Remove + button that goes through the existing `remove-origin` IPC. ## session: `parallel:collision-policy` diff --git a/bundled-addons/claude-connector/index.js b/bundled-addons/claude-connector/index.js index 85f33cfe..bf59fbb6 100644 --- a/bundled-addons/claude-connector/index.js +++ b/bundled-addons/claude-connector/index.js @@ -116,6 +116,26 @@ module.exports = { function publicState() { const st = server ? server.status() : { listening: false, port: null, connected: false, clients: [], sessions: [] }; const url = server && server.url; + // Panel approvals (theseus-panel://) and site approvals share + // the allowedOrigins list on disk. Split them for the UI so each gets + // its own section, and enrich panel rows with the add-on's own title. + const PANEL_PREFIX = "theseus-panel://"; + const panelTitles = new Map(); + try { + for (const t of api.tabControl.list()) { + if (t && t.kind === "panel" && t.addon) panelTitles.set(String(t.addon).toLowerCase(), t.title || t.addon); + } + } catch {} + const allowedPanels = []; + const allowedSites = []; + for (const o of S.allowedOrigins) { + if (typeof o === "string" && o.startsWith(PANEL_PREFIX)) { + const addon = o.slice(PANEL_PREFIX.length); + allowedPanels.push({ origin: o, addon, title: panelTitles.get(addon.toLowerCase()) || addon.replace(/^\w/, (c) => c.toUpperCase()) }); + } else { + allowedSites.push(o); + } + } return { enabled: true, accept: S.accept, @@ -128,6 +148,8 @@ module.exports = { controlledTabs: controller.controlledTabIds(), allowEvaluate: S.allowEvaluate, allowedOrigins: S.allowedOrigins, + allowedSites, + allowedPanels, uploadDirs: S.uploadDirs, sessionLimit: S.sessionLimit, token: S.token, diff --git a/bundled-addons/claude-connector/panel.html b/bundled-addons/claude-connector/panel.html index a92d082a..344bbffd 100644 --- a/bundled-addons/claude-connector/panel.html +++ b/bundled-addons/claude-connector/panel.html @@ -116,6 +116,12 @@ + +

Upload folders

Claude can attach files only from these folders.

@@ -156,9 +162,20 @@ $("setup").hidden = !st.commands; renderApps(st.apps); if (st.commands) { $("cmdCode").textContent = st.commands.claudeCode; $("cmdDesk").textContent = st.commands.claudeDesktop; } - $("origins").innerHTML = st.allowedOrigins.length - ? st.allowedOrigins.map((o) => `
  • ${esc(o)}
  • `).join("") + // Site approvals (http(s)) and panel approvals (theseus-panel://) are + // kept apart on screen even though they share one list on disk, so the + // user sees them for what they are. publicState() does the split and + // enriches each panel with the add-on's own title; the fallback keeps + // older hosts rendering the mixed list the way they used to. + const sites = Array.isArray(st.allowedSites) ? st.allowedSites : (st.allowedOrigins || []).filter((o) => !String(o).startsWith("theseus-panel://")); + const panels = Array.isArray(st.allowedPanels) ? st.allowedPanels : (st.allowedOrigins || []).filter((o) => String(o).startsWith("theseus-panel://")).map((o) => ({ origin: o, addon: o.slice("theseus-panel://".length), title: o.slice("theseus-panel://".length).replace(/^\w/, (c) => c.toUpperCase()) })); + $("origins").innerHTML = sites.length + ? sites.map((o) => `
  • ${esc(o)}
  • `).join("") : '
  • None yet.
  • '; + $("panelsSection").hidden = panels.length === 0; + $("panels").innerHTML = panels.length + ? panels.map((p) => `
  • ${esc(p.title)} panel
    ${esc(p.origin)}
  • `).join("") + : ""; $("dirs").innerHTML = st.uploadDirs.length ? st.uploadDirs.map((d) => `
  • ${esc(d)}
  • `).join("") : '
  • None. Uploads are refused.
  • ';