This project was bootstrapped with Create React App.
Grant you have the following tools installed before starting
- Install the project dependencies by running
yarnin the root folder. - Run the first build with
yarn build - Open your browser and navigate to chrome://extensions/
- Be sure has the "Developer mode" on
- Click "Load unpacked" and select the "dist" folder in this repository.
Everytime you build this extension, even with automatic building (yarn start), you need to refresh the extension.
yarn start: Watches for file changes in the src directory and automatically rebuild the project into the dist folder.yarn test: Runs the tests for this project. You may want to add the-wflag keep it watching for changes.yarn build: The same asstartbut without watching. Useful for CI builds.yarn clean: Cleans the dist folder. Useful if you had some kind of build problem.
This project has three main pieces:
Popup: The litte popup that opens when the user clicks in the extension's icon.Background: A service worker that performs background operations and communicates with the storage.Content: The content scripts. This gets injected into every page and controls the highlighter, the markers, and the sidebar.
Popup's only function is to show the enable/disable button and get the user's OpenAI api key. It has a very simple component to do this. The only particularity to keep in mind is that it is treated as the smallest possible window, so it needs to be wrapped in a container setting its size to be predictable.
See Popup.tsx
Background is designed to provide data and long-running tasks capabilities to the extension. Because of this, the entrypoint (Background.ts) is all about communication.
It uses a Mux/Demux (ref) strategy to listen to commands and emit updates to subscriptions. Check Mux.ts and Demux.ts for more information. They are abstractions above the native Chrome APIs to estabilish communication between content scripts and service workers.
All the commands it can respond to and the subscriptions available are defined under src/background and suffixed with -Command or -Subscription, and exposed by Background.ts.
The second responsibility of this architecture piece is to manage data. This is done by setting up an IndexedDB using Dexie.js as an abstraction to be easier to manipulate and watch for changes.
The option to use it was made considering that the native alternative (chrome.storage) would imply in a lot of effort to manage.
Here lies the most complex piece of the structure. This is composed by three other main pieces:
4.3.1. Highlighter
This component is resposible to detect when the user selects a piece of text in the page, show the option to highlight it, and persist the selection to the database along with metadata necessary to recreate it with the markers.
4.3.2. Highlight Markers
This component reads from the database the highlights made on the current page and recreate them with proper styling (the classic yellow background with red text).
This is a very critical component because it hurts the premise of React and directly modifies the DOM. The reason is that the pieces it needs to modify aren't controlled by React.
It is also very sensitive to DOM changes. If the CSS selector to a given highlight changes, it won't be able to reproduce the highlight.
4.3.3. Sidebar
The sidebar is where the magic happens. It appears whenever a page has one or more highlights. It shows the list and allows the user to remove them if necessary.
If the user hasn't configured an OpenAI key, it asks for it. If the user did configure one, it summarizes and shows the text along with a few hashtags.