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
4 changes: 3 additions & 1 deletion webapp/_webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
"scripts": {
"dev": "nodemon --watch src --ext ts,js,tsx,jsx,json --exec 'npm run build'",
"dev:chat": "vite dev --config vite.config.dev.ts",
"build": "tsc -b && npm run _build:default && npm run _build:background && npm run _build:intermediate",
"build": "tsc -b && npm run _build:default && npm run _build:background && npm run _build:intermediate && npm run _build:settings && npm run _build:popup",
"_build": "vite build",
"_build:default": "VITE_CONFIG=default npm run _build",
"_build:background": "VITE_CONFIG=background npm run _build",
"_build:intermediate": "VITE_CONFIG=intermediate npm run _build",
"_build:settings": "VITE_CONFIG=settings npm run _build",
"_build:popup": "VITE_CONFIG=popup npm run _build",
"lint": "eslint .",
"format": "prettier --write .",
"build:local:chrome": "bash -c 'source ./scripts/build-local-chrome.sh'",
Expand Down
Binary file added webapp/_webapp/public/images/locator.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
110 changes: 4 additions & 106 deletions webapp/_webapp/public/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,112 +3,10 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PaperDebugger</title>
<link href="https://fonts.googleapis.com/css2?family=Exo+2:wght@600&display=swap" rel="stylesheet" />
<style>
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
body {
padding: 1rem;
padding-left: 1.5rem;
min-width: 280px;
max-width: 340px;
min-height: 220px;
background: #fafafa;
color: #222;
font-family:
"Exo 2",
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Helvetica Neue",
Arial,
sans-serif;
border: 1px solid #ececec;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
}
.title {
font-size: 1.5rem;
font-weight: 600;
letter-spacing: 0.02em;
text-align: left;
font-family: "Exo 2", sans-serif;
}
.subtitle {
font-size: 1rem;
font-weight: 600;
letter-spacing: 0.02em;
text-align: left;
font-family: "Exo 2", sans-serif;
}
.steps {
margin: 0.5rem 0 0 0;
padding: 0 0rem;
flex: 1;
}
.step {
font-size: 0.9rem;
font-weight: 400;
margin-bottom: 0.7rem;
line-height: 1.5;
border-left: 3px solid #e0e0e0;
padding-left: 0.7em;
background: none;
border-radius: 0;
}
.step strong {
color: #3b82f6;
font-weight: 600;
margin-right: 0.3em;
}
.footer {
font-size: 0.75rem;
text-align: right;
font-weight: 100;
color: #888;
padding: 0.7rem 1.2rem 1.1rem 0;
border-top: 1px solid #ececec;
background: none;
border-radius: 0;
letter-spacing: 0.01em;
}
.highlight {
color: #3b82f6;
font-weight: 600;
}
.noselect {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.nodrag {
-webkit-user-drag: none;
-webkit-user-select: none;
-webkit-user-select: none;
}
</style>
<title>PaperDebugger Popup</title>
</head>
<body oncontextmenu="return false" class="nodrag">
<div oncontextmenu="return false" class="title noselect">PaperDebugger</div>
<div class="subtitle noselect">How to use</div>
<div class="steps noselect">
<div class="step">
<strong>1.</strong>In
<a class="highlight nodrag" href="https://overleaf.com/project" target="_blank">overleaf.com</a>, open any of
your projects.
</div>
<div class="step"><strong>2.</strong>PaperDebugger is in the <b>top left</b> of the project page.</div>
</div>
<div class="footer noselect">Happy writing!</div>
<body>
<div id="root"></div>
<script type="module" src="popup.js"></script>
</body>
</html>
12 changes: 12 additions & 0 deletions webapp/_webapp/public/settings.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PaperDebugger Settings</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="settings.js"></script>
</body>
</html>
43 changes: 42 additions & 1 deletion webapp/_webapp/src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { getAllCookies } from "./libs/browser";
import { HANDLER_NAMES } from "./shared/constants";
import { blobToBase64 } from "./libs/helpers";
import { registerContentScripts } from "./libs/permissions";

export type Handler<A, T> = {
name: string;
Expand Down Expand Up @@ -78,12 +79,50 @@ export const fetchImageHandler: Handler<string, string> = {
},
};

const registerContentScriptsIfPermitted = async () => {
try {
const { origins = [] } = await chrome.permissions.getAll();
if (!origins.length) {
console.log("[PaperDebugger] No origins found, skipping content script registration");
return;
}
await registerContentScripts(origins);
} catch (error) {
console.error("[PaperDebugger] Unable to register content scripts", error);
}
};

export const requestHostPermissionHandler: Handler<string, boolean> = {
name: HANDLER_NAMES.REQUEST_HOST_PERMISSION,
handler: async (origin, sendResponse) => {
const granted = await chrome.permissions.request({ origins: [origin] });
if (granted) {
// chrome.permissions.request requires a user gesture context, the requestHostPermissionHandler is in the background script
// and called via async messaging from the settings page.
// Here we must register content scripts because when a message is sent through chrome.runtime.sendMessage,
// the user gesture context is not preserved in the background script handler,
// causing the permission request to fail with "This function must be called during a user gesture."
// The permission request needs to be called directly from the settings page where the user click occurs,
// not delegated to the background script.
// Therefore, we must register content scripts here.
await registerContentScriptsIfPermitted();
}
sendResponse(granted);
},
};

// @ts-expect-error: browser may not be defined in all environments
const browserAPI = typeof browser !== "undefined" ? browser : chrome;

browserAPI.runtime?.onMessage?.addListener(
(request: { action: string; args: unknown }, _: unknown, sendResponse: (response: unknown) => void) => {
const handlers = [getCookiesHandler, getUrlHandler, getOrCreateSessionIdHandler, fetchImageHandler];
const handlers = [
getCookiesHandler,
getUrlHandler,
getOrCreateSessionIdHandler,
fetchImageHandler,
requestHostPermissionHandler,
];

const handler = handlers.find((h) => h.name === request.action) as HandlerAny;
if (!handler) {
Expand All @@ -100,3 +139,5 @@ browserAPI.runtime?.onMessage?.addListener(
return true;
},
);

registerContentScriptsIfPermitted();
1 change: 1 addition & 0 deletions webapp/_webapp/src/intermediate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,4 @@ export { getCookies };
export const getUrl = makeFunction<string, string>(HANDLER_NAMES.GET_URL);
export const getOrCreateSessionId = makeFunction<void, string>(HANDLER_NAMES.GET_OR_CREATE_SESSION_ID);
export const fetchImage = makeFunction<string, string>(HANDLER_NAMES.FETCH_IMAGE);
export const requestHostPermission = makeFunction<string, boolean>(HANDLER_NAMES.REQUEST_HOST_PERMISSION);
3 changes: 3 additions & 0 deletions webapp/_webapp/src/libs/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export function getManifest() {
// This is the version on github tag.
manifestJSON.version = semver.clean(version || "") || "0.0.0";

// @ts-expect-error we don't use this variable permissions_explanation
delete manifestJSON.permissions_explanation;

if (betaBuild === "true") {
manifestJSON.version_name = `v${manifestJSON.version}-${monorepoRevision}-beta`;
manifestJSON.name = "PaperDebugger BETA";
Expand Down
37 changes: 37 additions & 0 deletions webapp/_webapp/src/libs/permissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// can not running in content_script. registerContentScripts can only be called in service_worker.
export async function registerContentScripts(origins?: string[]) {
try {
const resolvedOrigins = origins ?? (await chrome.permissions.getAll()).origins ?? [];
if (resolvedOrigins.length === 0) {
console.log("[PaperDebugger] No origins found, skipping content script registration");
return;
}

const scriptIds = (await chrome.scripting.getRegisteredContentScripts()).map((script) => script.id);
if (scriptIds.length > 0) {
console.log("[PaperDebugger] Unregistering dynamic content scripts", scriptIds);
await chrome.scripting.unregisterContentScripts({ ids: scriptIds });
}

await chrome.scripting.registerContentScripts([
{
id: "content-script-main",
js: ["paperdebugger.js"],
persistAcrossSessions: true,
matches: resolvedOrigins,
world: "MAIN",
},
{
id: "content-script-intermediate",
js: ["intermediate.js"],
persistAcrossSessions: true,
matches: resolvedOrigins,
runAt: "document_start",
},
]);

console.log("[PaperDebugger] Registration complete", resolvedOrigins);
} catch (error) {
console.error("[PaperDebugger] Failed to register content scripts", error);
}
}
2 changes: 2 additions & 0 deletions webapp/_webapp/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ export const Main = () => {
);
};

console.log("[PaperDebugger] PaperDebugger injected, find toolbar-left or ide-redesign-toolbar-menu-bar to add button");

if (!import.meta.env.DEV) {
onElementAppeared(".toolbar-left .toolbar-item, .ide-redesign-toolbar-menu-bar", () => {
logInfo("initializing");
Expand Down
19 changes: 5 additions & 14 deletions webapp/_webapp/src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,20 @@
"48": "images/logo-1024.png"
},
"host_permissions": ["*://*.overleaf.com/"],
"permissions": ["cookies", "storage"],
"optional_host_permissions": ["*://*/*"],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Host permission pattern missing wildcard for paths

The host_permissions value *://*.overleaf.com/ only matches the root path (e.g., https://www.overleaf.com/) due to the trailing / without a wildcard. The extension needs to work on project pages like https://www.overleaf.com/project/abc123, which won't match this pattern. The pattern needs *://*.overleaf.com/* (with /* at the end) to match all paths and allow the content scripts to be injected on project pages.

Fix in Cursor Fix in Web

"permissions_explanation": "The optional_host_permissions pattern '*://*/*' allows the extension to request access to any website. This is necessary to support self-hosted Overleaf instances and similar use cases. Users will be prompted to grant access only when needed. Please review the extension documentation for details on security and privacy implications.",
"permissions": ["cookies", "storage", "scripting", "activeTab"],
"options_page": "settings.html",
"action": {
"default_popup": "popup.html"
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"js": ["paperdebugger.js"],
"matches": ["https://*.overleaf.com/project/*"],
"world": "MAIN"
},
{
"js": ["intermediate.js"],
"matches": ["https://*.overleaf.com/project/*"],
"run_at": "document_start"
}
],
"web_accessible_resources": [
{
"resources": ["images/*"],
"matches": ["https://*.overleaf.com/*"]
"matches": ["*://*/*"]
}
],
"key": "[AUTO-GENERATED]"
Expand Down
1 change: 1 addition & 0 deletions webapp/_webapp/src/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export const HANDLER_NAMES = {
GET_URL: "getUrl",
GET_OR_CREATE_SESSION_ID: "getOrCreateSessionId",
FETCH_IMAGE: "fetchImage",
REQUEST_HOST_PERMISSION: "requestHostPermission",
} as const;
Loading