From 951eb22b6e7f4fecc5662cf7b52a0a66a354ba53 Mon Sep 17 00:00:00 2001 From: Arpit Saxena Date: Mon, 23 Sep 2024 19:39:51 -0700 Subject: [PATCH 1/3] wip --- extension/src/popup/Popup.tsx | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/extension/src/popup/Popup.tsx b/extension/src/popup/Popup.tsx index a0e7a3e..2257fa6 100644 --- a/extension/src/popup/Popup.tsx +++ b/extension/src/popup/Popup.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react'; -import { Box, Button, Text, ChakraProvider, Image, HStack, IconButton } from '@chakra-ui/react' +import { Box, Button, Text, ChakraProvider, Image, HStack, IconButton, Link } from '@chakra-ui/react' import { BsTerminalFill, BsTools } from "react-icons/bs"; import { TbWorldShare } from "react-icons/tb"; import { get } from 'lodash' @@ -9,18 +9,6 @@ const requestToolLink = 'https://minusx.ai/tool-request' const websiteLink = 'https://minusx.ai' export const Popup: React.FC = () => { - const defaultPopupText = `We currently support MinusX on any Jupyter (Lab, Notebook, Lite, etc), or Metabase (SQL Query page) instance. You can test-drive it in a playground we've created for you, or request support for your favourite analytics apps!` - const [popupText, setPopupText] = React.useState(defaultPopupText); - useEffect(() => { - chrome.storage.local.get().then((localConfigs) => { - const popupText = get(localConfigs, "configs.popup_text") - if (popupText) { - setPopupText(popupText) - } else { - setPopupText(defaultPopupText) - } - }) - }) return ( @@ -28,7 +16,12 @@ export const Popup: React.FC = () => { } onClick={() => window.open(websiteLink, '_blank')} isRound={true}/> - {popupText} + + Supported tools: Jupyter (Lab, Notebook, Lite, etc), Metabase (SQL Query page), Posthog (HogQL page). +
+ Don't see MinusX on your app? Send a console.log("hello")}>bug report. +
From 70e69c007fd533e1ce35ff79cfdc4ad8163f5163 Mon Sep 17 00:00:00 2001 From: Arpit Saxena Date: Tue, 24 Sep 2024 15:00:47 -0700 Subject: [PATCH 2/3] changed permissions so bg can call function when theres nothing injected --- extension/src/manifest.json | 3 ++- extension/src/popup/Popup.tsx | 28 ++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/extension/src/manifest.json b/extension/src/manifest.json index 28a4556..6081cc5 100755 --- a/extension/src/manifest.json +++ b/extension/src/manifest.json @@ -5,7 +5,8 @@ "version": "0.0.18", "background": { "service_worker": "background.bundle.js" }, "permissions": [ - "storage" + "storage", + "scripting" ], "action": { "default_icon": "icon-34.png", diff --git a/extension/src/popup/Popup.tsx b/extension/src/popup/Popup.tsx index 2257fa6..9639980 100644 --- a/extension/src/popup/Popup.tsx +++ b/extension/src/popup/Popup.tsx @@ -7,7 +7,31 @@ import { get } from 'lodash' const playgroundLink = 'https://minusx.ai/playground' const requestToolLink = 'https://minusx.ai/tool-request' const websiteLink = 'https://minusx.ai' - +function DOMtoString(selector: any) { + if (selector) { + selector = document.querySelector(selector); + if (!selector) return "ERROR: querySelector failed to find node" + } else { + selector = document.documentElement; + } + return selector.outerHTML; +} +const getSource = () => { + return chrome.tabs.query({ active: true, currentWindow: true }).then(function (tabs) { + var activeTab = tabs[0]; + var activeTabId = activeTab.id; + if (!activeTabId) return "ERROR: no active tab found" + console.log("tab id" , activeTabId) + return chrome.scripting.executeScript({ + target: { tabId: activeTabId }, + injectImmediately: true, // uncomment this to make it execute straight away, other wise it will wait for document_idle + func: DOMtoString, + args: ['html'] + }).then(results => { + return results[0].result + }) +}) +} export const Popup: React.FC = () => { return ( @@ -20,7 +44,7 @@ export const Popup: React.FC = () => { Supported tools: Jupyter (Lab, Notebook, Lite, etc), Metabase (SQL Query page), Posthog (HogQL page).
Don't see MinusX on your app? Send a console.log("hello")}>bug report. + color="blue" onClick={() => getSource().then(src => alert(src))}>bug report. From 91ce1109d4a4a7330d5e5ccaf8511b36cfc389c0 Mon Sep 17 00:00:00 2001 From: Arpit Saxena Date: Tue, 24 Sep 2024 15:41:42 -0700 Subject: [PATCH 3/3] removed scripting permission, instead bundling diagnostics.ts always in extension to get page source. avoids new permission issues. --- extension/src/content/RPCs/index.ts | 24 ++----------- extension/src/content/RPCs/initListeners.ts | 38 +++++++++++++++++---- extension/src/content/debug.ts | 2 +- extension/src/content/diagnostics.ts | 17 +++++++++ extension/src/manifest.json | 17 ++++++--- extension/src/package.ts | 2 +- extension/src/popup/Popup.tsx | 25 +++++--------- extension/webpack.config.js | 2 ++ 8 files changed, 75 insertions(+), 52 deletions(-) create mode 100644 extension/src/content/diagnostics.ts diff --git a/extension/src/content/RPCs/index.ts b/extension/src/content/RPCs/index.ts index 9eed8df..1bbda41 100644 --- a/extension/src/content/RPCs/index.ts +++ b/extension/src/content/RPCs/index.ts @@ -7,7 +7,7 @@ import { copyToClipboard } from "./copyToClipboard" import { getElementScreenCapture } from "./elementScreenCapture" import ripple from "./ripple" import { fetchData } from "./fetchData" -import { initWindowListener, RPCPayload } from './initListeners' +import { initBgRpc, initWindowListener, RPCPayload } from './initListeners' import { attachMutationListener, detachMutationListener, initMutationObserver } from "./mutationObserver" import { respondToOtherTab, forwardToTab, getPendingMessage } from "./crossInstanceComms" import { configs } from "../../constants" @@ -44,26 +44,6 @@ type RPC = typeof rpc export const initRPC = () => { initWindowListener(rpc) - // Function to handle messages from the background script - chrome.runtime.onMessage.addListener((event, sender, sendResponse) => { - const payload: RPCPayload = event - if (!payload || !(payload.fn in rpc)) { - const error = 'Invalid payload' - sendResponse({ error }); - return true; - } - if (sender.id == chrome.runtime.id) { - try { - Promise.resolve(rpc[payload.fn](payload.args, sender)).then((response) => { - sendResponse({ response }); - }).catch(error => { - sendResponse({ error }); - }) - } catch (error) { - sendResponse({ error }); - } - } - return true - }); + initBgRpc(rpc) initMutationObserver() } \ No newline at end of file diff --git a/extension/src/content/RPCs/initListeners.ts b/extension/src/content/RPCs/initListeners.ts index 0ad8ae7..78ae439 100644 --- a/extension/src/content/RPCs/initListeners.ts +++ b/extension/src/content/RPCs/initListeners.ts @@ -1,10 +1,10 @@ import { configs } from "../../constants" export type RPCPayload = { - fn: string - args: any[] - id: number - timeout: number + fn: string + args: any[] + id: number + timeout: number } export type RPCError = { @@ -26,9 +26,9 @@ export type RPCErrorResponse = { type: 'error' } -export const initWindowListener = (rpc: T) => { +export const initWindowListener = (rpc: T) => { const TRUSTED_ORIGINS = [configs.WEB_URL] - window.addEventListener('message', function(event) { + window.addEventListener('message', function (event) { const payload: RPCPayload = event.data if (!TRUSTED_ORIGINS.includes(event.origin) || !payload || !(payload.fn in rpc)) { return false; @@ -63,7 +63,7 @@ export const initWindowListener = (rpc: T) => { }); }).catch(rawError => { const error: RPCError = { - message: rawError?.message?? 'An error occurred', + message: rawError?.message ?? 'An error occurred', } try { error.error = JSON.parse(JSON.stringify(rawError)) @@ -80,4 +80,28 @@ export const initWindowListener = (rpc: T) => { }) return true }); +} + +export const initBgRpc = (rpc: T) => { + // Function to handle messages from the background script + chrome.runtime.onMessage.addListener((event, sender, sendResponse) => { + const payload: RPCPayload = event + if (!payload || !(payload.fn in rpc)) { + const error = 'Invalid payload' + sendResponse({ error }); + return true; + } + if (sender.id == chrome.runtime.id) { + try { + Promise.resolve(rpc[payload.fn](payload.args, sender)).then((response) => { + sendResponse({ response }); + }).catch(error => { + sendResponse({ error }); + }) + } catch (error) { + sendResponse({ error }); + } + } + return true + }); } \ No newline at end of file diff --git a/extension/src/content/debug.ts b/extension/src/content/debug.ts index 90514d3..661672d 100644 --- a/extension/src/content/debug.ts +++ b/extension/src/content/debug.ts @@ -1,4 +1,4 @@ import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event'; import { fireEvent } from '@testing-library/dom'; window.userEvent = userEvent -window.fireEvent = fireEvent +window.fireEvent = fireEvent \ No newline at end of file diff --git a/extension/src/content/diagnostics.ts b/extension/src/content/diagnostics.ts new file mode 100644 index 0000000..9860edb --- /dev/null +++ b/extension/src/content/diagnostics.ts @@ -0,0 +1,17 @@ +import { initBgRpc } from 'extension' + +const getPageSource = (selector: any): string => { + if (selector) { + selector = document.querySelector(selector); + if (!selector) return "ERROR: querySelector failed to find node" + } else { + selector = document.documentElement; + } + return selector.outerHTML; +} + + +export const rpc = { + getPageSource, +} +initBgRpc(rpc) \ No newline at end of file diff --git a/extension/src/manifest.json b/extension/src/manifest.json index 6081cc5..47c7af4 100755 --- a/extension/src/manifest.json +++ b/extension/src/manifest.json @@ -5,8 +5,7 @@ "version": "0.0.18", "background": { "service_worker": "background.bundle.js" }, "permissions": [ - "storage", - "scripting" + "storage" ], "action": { "default_icon": "icon-34.png", @@ -21,7 +20,7 @@ "http://*/*", "https://*/*" ], - "js": ["contentScript.bundle.js"], + "js": ["contentScript.bundle.js", "diagnostics.bundle.js"], "css": [], "run_at": "document_start" } @@ -31,7 +30,17 @@ ], "web_accessible_resources": [ { - "resources": ["content.styles.css", "icon-128.png", "icon-34.png", "*.svg", "metabase.bundle.js", "jupyter.bundle.js", "posthog.bundle.js", "debug.bundle.js"], + "resources": [ + "content.styles.css", + "icon-128.png", + "icon-34.png", + "*.svg", + "metabase.bundle.js", + "jupyter.bundle.js", + "posthog.bundle.js", + "debug.bundle.js", + "diagnostics.bundle.js" + ], "matches": [""] } ], diff --git a/extension/src/package.ts b/extension/src/package.ts index 8846b30..1b37f78 100644 --- a/extension/src/package.ts +++ b/extension/src/package.ts @@ -1,2 +1,2 @@ export { resolveSelector } from './helpers/pageParse/resolveSelectors' -export { initWindowListener } from './content/RPCs/initListeners' +export { initWindowListener, initBgRpc } from './content/RPCs/initListeners' diff --git a/extension/src/popup/Popup.tsx b/extension/src/popup/Popup.tsx index 9639980..2fad3df 100644 --- a/extension/src/popup/Popup.tsx +++ b/extension/src/popup/Popup.tsx @@ -7,29 +7,20 @@ import { get } from 'lodash' const playgroundLink = 'https://minusx.ai/playground' const requestToolLink = 'https://minusx.ai/tool-request' const websiteLink = 'https://minusx.ai' -function DOMtoString(selector: any) { - if (selector) { - selector = document.querySelector(selector); - if (!selector) return "ERROR: querySelector failed to find node" - } else { - selector = document.documentElement; - } - return selector.outerHTML; -} + const getSource = () => { return chrome.tabs.query({ active: true, currentWindow: true }).then(function (tabs) { var activeTab = tabs[0]; var activeTabId = activeTab.id; if (!activeTabId) return "ERROR: no active tab found" console.log("tab id" , activeTabId) - return chrome.scripting.executeScript({ - target: { tabId: activeTabId }, - injectImmediately: true, // uncomment this to make it execute straight away, other wise it will wait for document_idle - func: DOMtoString, - args: ['html'] - }).then(results => { - return results[0].result - }) + return chrome.tabs.sendMessage(activeTabId, {fn: 'getPageSource', args: ['html']}) + .then(results => { + console.log(results) + return results.response + }).catch(err => { + console.log(err) + }) }) } export const Popup: React.FC = () => { diff --git a/extension/webpack.config.js b/extension/webpack.config.js index f4e11d4..9a94705 100755 --- a/extension/webpack.config.js +++ b/extension/webpack.config.js @@ -42,6 +42,8 @@ var options = { popup: path.join(__dirname, 'src', 'popup', 'index.tsx'), contentScript: path.join(__dirname, 'src', 'content', 'index.ts'), debug: path.join(__dirname, 'src', 'content', 'debug.ts'), + // diagnostics file for bug reporting + diagnostics: path.join(__dirname, 'src', 'content', 'diagnostics.ts'), // @ppsreejith: We can generate this in the future, for now adding manually // Same in manifest.json file metabase: path.join(__dirname, '..', 'apps', 'src', 'metabase', 'inject.ts'),