Skip to content

Conversation

@hitarth-gg
Copy link
Collaborator

@hitarth-gg hitarth-gg commented Jun 9, 2025

Initial project setup with prototype

There are several things that need refinement in this prototype.

Some things that are on my to-do list as of now are:

  • preserve scroll position on re-render, and automatically scroll to the bottom when a new event is captured if the current scroll position is greater than 80%
  • custom thin scroll bars to save space
  • add logic to track remaining methods on ipcMain and ipcRenderer
  • use a consistent color palette throughout the project instead of hardcoding hex color values inline
  • add an option to open the DetailPanel at the bottom as well, currently it bugs out when the window width is small
  • add night mode
  • make the panel more responsive

To correctly see the DetailPanel on the right please dock the devtools to bottom as shown in the image below. I'll likely fix this UI issue in the next PR.
image

I'm open to completely reworking an approach if it needs to be reconsidered.

Comment on lines 35 to 66
document: 'readonly',
window: 'readonly',
console: 'readonly',
process: 'readonly',
chrome: 'readonly',
browser: 'readonly',
setTimeout: 'readonly',
clearTimeout: 'readonly',
setInterval: 'readonly',
clearInterval: 'readonly',
fetch: 'readonly',
URL: 'readonly',
URLSearchParams: 'readonly',
FormData: 'readonly',
Headers: 'readonly',
Request: 'readonly',
Response: 'readonly',
Blob: 'readonly',
File: 'readonly',
FileReader: 'readonly',
localStorage: 'readonly',
sessionStorage: 'readonly',
location: 'readonly',
history: 'readonly',
navigator: 'readonly',
Element: 'readonly',
HTMLElement: 'readonly',
Event: 'readonly',
CustomEvent: 'readonly',
MutationObserver: 'readonly',
IntersectionObserver: 'readonly',
ResizeObserver: 'readonly',
Copy link
Member

@erickzhao erickzhao Jun 10, 2025

Choose a reason for hiding this comment

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

issue(non-blocking): Can we use https://www.npmjs.com/package/globals shorthand as per eslint/eslint#16975?

Copy link
Collaborator Author

@hitarth-gg hitarth-gg Jun 13, 2025

Choose a reason for hiding this comment

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

Fixed, I added these:

globals: {
        ...globals.node,
        ...globals.browser,
        ...globals.webextensions,
      },

README.md Outdated
Comment on lines 21 to 24
app.whenReady().then(() => {
devtron.install()
// ...
}
Copy link
Member

@erickzhao erickzhao Jun 10, 2025

Choose a reason for hiding this comment

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

nit: missing a closing brace

Suggested change
app.whenReady().then(() => {
devtron.install()
// ...
}
app.whenReady().then(() => {
devtron.install()
// ...
})

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed: c528b44

@@ -0,0 +1,30 @@
'use strict';
Copy link
Member

@erickzhao erickzhao Jun 10, 2025

Choose a reason for hiding this comment

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

issue(non-blocking): Not necessary for the first commit but it would be nice to have everything in TypeScript. Can we open a follow-up issue and get that done? Not sure if we need to prioritize that.

Copy link
Member

Choose a reason for hiding this comment

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

Plus one here, Typescript will help a lot with long-term maintainability! Agree this doesn't need to be addressed in the scope of this initial PR, but definitely is something that would be easier to tackle earlier than later

Copy link
Collaborator Author

@hitarth-gg hitarth-gg Jun 12, 2025

Choose a reason for hiding this comment

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

Would you prefer that I keep this PR as-is and handle the TypeScript migration in a follow-up PR? Or would it be better to scrap this PR and rewrite everything in TypeScript from the start? Happy to go with whichever approach the team prefers.
Edit: As discussed in the Slack Huddle on June 12, 2025, I'll address this in a follow-up PR.

package.json Outdated
"postcss": "^8.5.4",
"postcss-loader": "^8.1.1",
"postcss-preset-env": "^10.2.1",
"prettier-plugin-brace-style": "^0.7.3",
Copy link
Member

@erickzhao erickzhao Jun 10, 2025

Choose a reason for hiding this comment

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

question: is this dep being used anywhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nope, I've removed the prettier-plugin-brace-style dep - 497b92b

package.json Outdated
Comment on lines 5 to 20
"main": "./lib/devtron.cjs",
"module": "./lib/devtron.mjs",
"exports": {
".": {
"import": "./lib/devtron.mjs",
"require": "./lib/devtron.cjs"
},
"./monitorRenderer": {
"import": "./lib/monitorRenderer.mjs",
"require": "./lib/monitorRenderer.cjs"
},
"./monitorMain": {
"import": "./lib/monitorMain.mjs",
"require": "./lib/monitorMain.cjs"
}
},
Copy link
Member

@erickzhao erickzhao Jun 10, 2025

Choose a reason for hiding this comment

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

question: Not super familiar with this syntax but do we still need main if we have exports defined?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Those were indeed not needed, I removed "main" and "module" both. - 497b92b

<div className="mb-3">
<span className="text-gray-600">Method: </span>
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium border border-yellow-400 bg-yellow-100 text-yellow-800">
{selectedRow.direction === 'renderer-to-main' ? '↓ RTM' : '↑ MTR'}{' '}
Copy link
Member

@erickzhao erickzhao Jun 10, 2025

Choose a reason for hiding this comment

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

suggestion: In the long run, we should probably find a better representation than "RTM" or "MTR" because it's not immediately clear what those terms mean at a glance

Copy link
Collaborator Author

@hitarth-gg hitarth-gg Jun 13, 2025

Choose a reason for hiding this comment

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

Agreed, I was thinking of adding a tooltip with the full name like renderer-to-main, or maybe switching to clearer terms like send and receive.
I have added this suggestion to my to-do list for now.

Should I open an issue for it ?


ipcRenderer.once = (channel, listener) => {
return originalOnce(channel, (_event, ...args) => {
track('main-to-renderer', channel, args);
Copy link
Member

@erickzhao erickzhao Jun 11, 2025

Choose a reason for hiding this comment

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

suggestion: I think it'd be interesting to distinguish .once vs. .on vs. .invoke calls. What do you think?

Copy link
Collaborator Author

@hitarth-gg hitarth-gg Jun 13, 2025

Choose a reason for hiding this comment

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

I agree, I think I'll save this one for after I implement Session to track IPCs from renderer-to-main since that will likely influence how I would want to handle this.

@@ -0,0 +1,69 @@
const events = [
Copy link
Member

@erickzhao erickzhao Jun 11, 2025

Choose a reason for hiding this comment

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

question: do you have an idea on a testing plan to maybe add unit tests using this fixture data?

Copy link
Collaborator Author

@hitarth-gg hitarth-gg Jun 13, 2025

Choose a reason for hiding this comment

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

I haven't finalized a testing plan yet, but I'll likely start working on it when I switch to TypeScript. The events array is currently used as placeholder data for testing the panel UI on localhost in dev mode.

package.json Outdated
@@ -0,0 +1,77 @@
{
"name": "devtron",
"version": "0.1.0",
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: The previous devtron version was at 1.4.0, let's bump this to 2.0.0?

Copy link
Member

Choose a reason for hiding this comment

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

I was actually going to suggest we set it to 0.0.0-development like our other repos, since this package will end up using automated releases. Since the repo already has a v1.4.0 tag, I believe as long as we throw in a "BREAKING CHANGE: ..." in whatever commit triggers the new release (which will be under the new @electron/devtron package name), it should jump to v2.0.0 for us and be good from there. 👍

Copy link
Member

Choose a reason for hiding this comment

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

Ah, thank you for the context here @dsanders11!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed the version to 0.0.0-development - 497b92b

package.json Outdated
"build": "npm run build:node && npm run build:browser",
"dev": "webpack serve --config webpack.dev.config.js"
},
"keywords": [],
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: It might be nice to match the keywords in the original devtron package.json, perhaps maybe adding ipc

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I added the keywords in this commit - 497b92b

package.json Outdated
"dev": "webpack serve --config webpack.dev.config.js"
},
"keywords": [],
"author": "",
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: You can put yourself here! I've also seen "Electron Community" used, feel free to use whatever you prefer

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done! - 497b92b

package.json Outdated
Comment on lines 71 to 74
"src/",
"public",
"webpack.config.js",
"package.json",
Copy link
Member

Choose a reason for hiding this comment

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

(nonblocking): we should remove these source code files and folders to keep the published package size small. again, maybe something to revisit when we're thinking about publishing

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Got it! I removed the src folder and some other unnecessary files/folders and added the ones that were relevant - 497b92b

manifest.json Outdated
@@ -0,0 +1,27 @@
{
"name": "devtron",
"version": "1.0.0",
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: The previous devtron manifest.json used 1.0, let's bump to 2.0.0

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done! - 9035436

@@ -0,0 +1,30 @@
'use strict';
Copy link
Member

Choose a reason for hiding this comment

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

Plus one here, Typescript will help a lot with long-term maintainability! Agree this doesn't need to be addressed in the scope of this initial PR, but definitely is something that would be easier to tackle earlier than later

},
"files": [
"index.js",
"dist/",
Copy link
Member

Choose a reason for hiding this comment

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

(nonblocking): not something we need to address right now but something to consider before publishing: we should test that the output of the packing the file with npm pack is what we expect and can be used by consumers via npm install {output .tgz}

Copy link
Collaborator Author

@hitarth-gg hitarth-gg Jun 13, 2025

Choose a reason for hiding this comment

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

Noted!, I’ll keep this in mind for future testing.
I tested the npm pack output locally by installing the .tgz in a fresh project, and it worked as expected.

README.md Outdated
## Building and Development
- Clone the repository to your local machine
- run `npm install` to install dependencies
- run `npm link` to link the package globally
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: I think we need a npm run build step after link?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I fixed it here: c528b44

@MarshallOfSound MarshallOfSound self-requested a review June 11, 2025 22:09
*/
const keepAlive = () => setInterval(chrome.runtime.getPlatformInfo, 20e3);
chrome.runtime.onStartup.addListener(keepAlive);
keepAlive();
Copy link
Member

Choose a reason for hiding this comment

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

Given devtron is only intended to run in Electron, we may be able to more reliably keep alive the extension without requiring hacks. The risk is that the Chrome team eventually changes this and we can no longer rely on it.

I authored serviceWorker.startTask() which will keep the service worker alive as long as it's active. It might be good to experiment with this approach.

Not a blocker for this PR though. Might be something to explore later on.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sounds good, I'll start exploring and experimenting with it then.


patchIpc('on');
patchIpc('once');
patchIpc('handle');
Copy link
Member

Choose a reason for hiding this comment

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

This is a good first start at intercepting IPCs for the main process, especially in getting some initial data to build out the devtron views.

One thing I'd like us to explore over the summer is a more comprehensive solution to tracking main process IPCs. In this solution, it's able to keep track of IPCs to ipcMain, but misses various other channels such as webContents.ipc, webFrameMain.ipc, and serviceWorkerMain.ipc.

Earlier in the year, I was able to refactor the IPC internals so that all IPCs sent to the main process goes through the Session (electron/electron#45452). We may be able to make some changes to the Electron codebase to better support this interception use case. This would be the best approach to capture all mentioned IPC channels.

I'm good with your current approach for now, but let's start talking about how we can plan to utilize the Session internals as a followup.

Copy link
Collaborator Author

@hitarth-gg hitarth-gg Jun 12, 2025

Choose a reason for hiding this comment

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

I looked into it and I can capture IPC messages going from renderer to main process using the following code.

function attachIPCInterceptors(ses, label = 'session') {

  ses.on('-ipc-message', (event, channel, args) => {
    console.log(`[${label}] async message`, { channel, args, event})
  })

  ses.on('-ipc-invoke', (event, channel, args) => {
    console.log(`[${label}] invoke request`, { channel, args, event})
  })

  ses.on('-ipc-message-sync', (event, channel, args) => {
    console.log(`[${label}] sync message`, { channel, args, event})
  })
}

const ses = session.defaultSession
attachIPCInterceptors(ses, 'mainSession')

For now, I’ve only tested this with the defaultSession, but for partitioned sessions, I’m thinking of passing their Session objects to this utility function to attach the same listeners.

Should I switch to using Session in this PR, or should I save that change for a separate one?

Copy link
Member

Choose a reason for hiding this comment

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

Should I switch to using Session in this PR, or should I save that change for a separate one?

Let's save it for a future PR.

@dsanders11 dsanders11 self-requested a review June 12, 2025 15:31
@hitarth-gg hitarth-gg force-pushed the next branch 2 times, most recently from 1cdc06c to 6fb9eaf Compare June 13, 2025 17:27
package.json Outdated
@@ -0,0 +1,79 @@
{
"name": "devtron",
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"name": "devtron",
"name": "@electron/devtron",

That's the package name we'll publish it under on npm when the project is done, so let's set it to that now. 👍

Copy link
Member

Choose a reason for hiding this comment

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

It looks like this change will require other changes (and updating the README), so I'll leave those associated changes to you.

Copy link
Collaborator Author

@hitarth-gg hitarth-gg Jun 14, 2025

Choose a reason for hiding this comment

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

Got it, I updated the package name and made relevant changes in commit ac77ee5 and d164149

Copy link
Member

@erickzhao erickzhao left a comment

Choose a reason for hiding this comment

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

Changes addressed LGTM

Copy link
Member

@samuelmaddock samuelmaddock left a comment

Choose a reason for hiding this comment

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

This is an excellent starting point, @hitarth-gg! Excited to see your work over the course of the summer.

Comment on lines +25 to +27
webContents.getAllWebContents().forEach((wc) => {
wc.send(MSG_TYPE.RENDER_EVENT, eventData);
});
Copy link
Member

Choose a reason for hiding this comment

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

Is my understanding of the message flow correct?
main -> renderer preload -> extension content script -> extension background worker

Sending events to all WebContents likely exposes more data than desired. In a follow up PR, we can be more precise by sending an IPC from main to the extension background worker directly.

// Get Devtron extension, ideally this is cached instead of looking it up each time
const devtronExtension = session.getAllExtensions().find(ext => ext.name === 'Devtron');

// Start or get ServiceWorkerMain instance
const serviceWorker = await session.serviceWorkers.startWorkerForScope(devtronExtension.url);

// Send IPC to background worker
serviceWorker.send(MSG_TYPE.RENDER_EVENT, data);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Is my understanding of the message flow correct?
main -> renderer preload -> extension content script -> extension background worker

Yes, that's correct. I wasn't happy with my initial implementation of this, good to know this alternative exists😀.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants