Skip to content

Latest commit

 

History

617 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Marks: Text Highlighter

A cross-browser extension that allows you to highlight and manage text on web pages. Supports Chrome, Firefox, and Firefox for Android.

Features

  • Text Highlighting: Select and highlight text on web pages with multiple colors
  • Highlight Management: Manage and review highlighted text per page
  • Cross-Device Sync: Sync highlights across devices using browser storage.sync
  • Minimap: View highlighted positions at a glance with a minimap on the right side of the page
  • Multilingual Support: Available in English, Korean, Japanese, and Chinese
  • Cross-Browser Support: Works on Chrome, Firefox, and Firefox for Android
  • Keyboard Shortcuts: Quick highlighting with customizable keyboard shortcuts (desktop only)
  • Selection Controls: Floating highlight UI on text selection, ideal for mobile devices

Getting Started

Prerequisites

  • Node.js 22.16.0 or higher
  • npm 10.9.0 or higher

Installation

# Clone the repository
git clone https://github.com/yourusername/text-highlighter.git
cd text-highlighter

# Install dependencies
npm install

Browser Support

This extension supports:

  • Chrome: Manifest V3 with native Chrome APIs
  • Firefox: Manifest V3 with native WebExtensions APIs (requires Firefox 140.0+)
  • Firefox for Android: Manifest V3 with mobile-optimized UI (requires Firefox for Android 142.0+)

Firefox Android Notes

On Firefox for Android, the following desktop-only APIs are unavailable and handled gracefully:

  • Context Menus (contextMenus): Not supported. Use the Selection Controls UI instead (enabled by default on mobile).
  • Keyboard Shortcuts (commands): Not supported. Use the Selection Controls UI instead.
  • Windows API (windows): Not supported. The extension uses the Tabs API as a fallback.
  • Sync Storage (storage.sync): Sync currently works between Firefox desktop devices, but Firefox for Android does not sync highlight data.

The Selection Controls feature (floating highlight icon on text selection) is automatically enabled on mobile devices, providing a touch-friendly alternative to context menus and keyboard shortcuts.

Development

Agent Skills

Repository skills live in .agents/skills/, which is tracked in git and shared across agent tools. Claude Code only discovers skills under .claude/skills/, so link the two rather than keeping a second copy:

npm run link-skills

This creates .claude/skills as a link to .agents/skills (a directory junction on Windows, which needs no elevation). Run it once per clone — .claude/ is gitignored, so the link is local to your machine. It is idempotent and safe to re-run. Restart Claude Code afterwards for the skills to be picked up.

Add new skills to .agents/skills/<name>/SKILL.md only; the link exposes them automatically.

Testing

Three suites, run separately:

# Unit and integration tests (Jest, jsdom)
npm test

# End-to-end tests on Chromium (Playwright)
npx playwright install   # browsers, required before the first run
npx playwright test

# Firefox smoke tests (Selenium + geckodriver)
npm run test:e2e:firefox

npm test and the Playwright suite run in CI on every push. The Firefox suite does not: it is three checks that confirm the Firefox build comes up, applies a highlight, restores it after a reload, and opens its popup — run it by hand before publishing a Firefox release. It needs Firefox installed locally (geckodriver is downloaded automatically), builds dist-firefox/ itself, and takes a few seconds.

# Watch it run instead of going headless
HEADFUL=1 npm run test:e2e:firefox

# Point at a Firefox that is not in the default location
FIREFOX_BINARY="/path/to/firefox" npm run test:e2e:firefox

Testing on Firefox for Android

To test the extension on a real Android device:

Prerequisites

  1. Install Firefox for Android on your device
  2. Enable USB debugging on your Android device (Settings > Developer options > USB debugging)
  3. Connect your device via USB and authorize the connection

Using web-ext

# Run on connected Android device
npx web-ext run -t firefox-android --adb-device <device-id> --firefox-apk org.mozilla.firefox -s dist-firefox

To find your device ID:

adb devices

Using about:debugging

  1. On your Android device, open Firefox and go to about:config
  2. Set xpcom.debug.remote.enabled to true
  3. On your desktop Firefox, go to about:debugging > Setup
  4. Add your device and connect
  5. Load the extension from dist-firefox/manifest.json

Viewing Logs

# View extension logs from the Android device
adb logcat -s GeckoConsole

Deployment

Development Build

Run the deployment script to build both Chrome and Firefox extension files at once:

npm run deploy

To build a single browser instead, use npm run deploy:chrome or npm run deploy:firefox.

For Chrome

Run the Chrome-specific deployment script to copy only the required files to the dist directory for loading into Chrome:

npm run deploy:chrome

To load the deployed extension in Chrome:

  1. Open chrome://extensions in Chrome browser
  2. Enable "Developer mode" in the top right
  3. Click "Load unpacked extension"
  4. Select the generated dist directory
For Firefox

Run the Firefox-specific deployment script:

npm run deploy:firefox

To load the deployed extension in Firefox:

  1. Open about:debugging in Firefox browser
  2. Click "This Firefox" in the sidebar
  3. Click "Load Temporary Add-on"
  4. Select the manifest.json file from the generated dist-firefox directory

Production Build

For creating a production-ready extension package, you can now specify the target browser:

npm run version-deploy <version> [browser]

This command will:

  1. Update the version in the appropriate manifest file (manifest.json for Chrome, manifest-firefox.json for Firefox)
  2. Set DEBUG_MODE to false in all JavaScript files
  3. Build the extension to the appropriate directory (dist/ for Chrome, dist-firefox/ for Firefox)
  4. Create a browser-specific zip file in the outputs/ directory
Chrome Production Build (default)
npm run version-deploy 1.2.0
# or explicitly
npm run version-deploy 1.2.0 chrome

This creates outputs/text-highlighter-1.2.0-chrome.zip ready for submission to the Chrome Web Store.

Firefox Production Build
npm run version-deploy 1.2.0 firefox

This creates outputs/text-highlighter-1.2.0-firefox.zip ready for submission to Firefox Add-ons (AMO).

Note: Each browser build uses its own manifest file and output directory, allowing you to maintain separate versions for each browser if needed.

Technical Implementation

Cross-Browser Compatibility

The extension uses a browserAPI compatibility layer to support Chrome and Firefox:

  • Chrome: Uses native chrome.* APIs directly
  • Firefox (Desktop/Android): Uses native browser.* APIs directly
  • Manifest Files: Separate manifests for browser-specific configurations
    • manifest.json: Chrome-optimized (default)
    • manifest-firefox.json: Firefox-optimized with gecko and gecko_android settings

API Compatibility

API Chrome Firefox Desktop Firefox Android
Storage O O O
Sync Storage O O X
Tabs O O O
Runtime O O O
Internationalization O O O
Context Menus O O X
Commands O O X
Windows O O X

On Firefox Android, unavailable APIs are conditionally guarded using browser.runtime.getPlatformInfo() to detect the platform at runtime.

Cloud Sync Convergence

Cloud sync (background/cloud-sync-service.js) runs a pull-merge-push cycle on a 15-minute alarm. The PUT is skipped when the merged blob matches the one just fetched, so an idle cycle costs a single GET.

That guard rests on an invariant worth knowing before touching this code: the merge must be a pure function of the two blobs, giving the same answer on every device. A rule phrased as "prefer mine" breaks it, because each device resolves it in its own favour — neither ever adopts the other, and every cycle pushes. Nothing errors; the symptom is a setting that quietly never syncs, plus a KV write on every cycle from users who changed nothing.

Two rules in mergeBlobs still resolve ties that way:

  • settings: on equal updatedAt, local wins (strict >)
  • page title: local wins whenever it is non-empty, regardless of recency

Adoption has to be complete, too. applySettingsFromSync copies the sender's settings timestamp, so any field it declines to adopt leaves two devices claiming the same timestamp with different content — which is exactly the tie the rules above resolve badly. Treat null as a value, not as an absent field.

See #108 for a case that reached production, where a skipped null produced a PUT on roughly 20% of all sync cycles.

Contribution

  1. Fork the project
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A cross-browser extension that allows you to highlight and manage text on web pages. Supports Chrome, Firefox, and Firefox for Android.

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages