Browser Extension

Chrome MV3 + Firefox WebExtension — start / stop the Leenops timer from any web app, with Start-timer buttons injected into Jira, Linear, Asana, Notion, GitHub.

The Leenops browser extension puts a Start-timer button inside the apps you already work in — Jira, Linear, Asana, Notion, GitHub, Trello, and 100+ others. No more tab-switching to start tracking time on a card.

The extension is a separate package living at extensions/browser/ in the Leenops repo. It consumes only the public REST API — same surface external integrations use — so it's also the proof point that the Time module could be extracted into a standalone product later.

Install

Chrome / Edge / Brave

The extension is installed as an unpacked build in the current release. A public browser-store listing is not available.

cd extensions/browser
npm install
npm run build

Then in Chrome, open chrome://extensions, toggle Developer mode, choose Load unpacked, and pick extensions/browser/dist/.

Firefox

The Firefox add-on is installed temporarily from the built package in the current release. A public Firefox Add-ons listing is not available.

cd extensions/browser
npm run build

Then in Firefox, open about:debugging, choose This Firefox, choose Load Temporary Add-on, and pick extensions/browser/dist/manifest.json.

Connect to your workspace

  1. Click the extension icon to open the popup.
  2. Workspace URL: https://workspace.leenops.com (or your custom domain).
  3. API token: open https://workspace.leenops.com/settings/profileAPI tokensCreate token → name it "Browser extension" → copy.
  4. Paste the token into the popup → Connect.

The popup verifies the token by hitting GET /api/v1/time/timer/active. Once connected, the popup shows your active timer (if any) + Start / Stop buttons.

The token is stored in chrome.storage.local and never leaves your browser — it goes only to your workspace URL.

What the extension does

A minimal Toggl-style popup:

  • Active timer — entity icon + description + live elapsed clock + Stop
  • Quick start — text input + entity picker + Start
  • Click description to navigate to /time/timer in a new tab.

Content script

On a configured allowlist of domains, the extension injects a Start timer for this page button at a sensible spot:

AppButton placementDescription pre-fill
JiraIssue toolbarJIRA-123: <summary>
LinearIssue headerLIN-123: <title>
AsanaTask header<task name>
NotionPage header<page title>
GitHubIssue / PR header<repo>#<num>: <title>
TrelloCard detail<card name>

Click → POSTs to /api/v1/time/timer/start with entity_type: 'internal' and the description from the page title. (The extension doesn't try to map external IDs onto Leenops entities — that's a future iteration.)

Background service worker

Long-lived. Maintains a connection to the popup, polls /api/v1/time/timer/active periodically, updates the extension badge with the elapsed minutes when a timer runs.

Keyboard shortcut

Default: Ctrl+Shift+T (Chrome / Firefox / Edge) — toggles the popup. Customize via chrome://extensions/shortcuts.

Allowlist + privacy

The extension only injects scripts on domains you've allowlisted in the popup settings (Settings → Allowlist). Default allowlist:

  • *.atlassian.net (Jira)
  • linear.app
  • app.asana.com
  • *.notion.so
  • github.com
  • trello.com

You can add your own domains. The extension never reads page content beyond the title + URL.

Troubleshooting

Button doesn't appear on a page — check that the domain is in your allowlist. Reload the page after adding.

"Unauthorized" when starting a timer — your API token may be revoked or rotated. Open the popup → Settings → re-paste the token.

Multiple timers running across browser + app — the database enforces "at most one running timer per user", so the older one auto-stops. The extension polls every 30s, so the popup may briefly show stale state — refresh the popup.

Want to disable popup tracking on certain sites — Settings → Allowlist → remove the domain.

API surface used

The extension consumes only the public REST API:

  • GET /api/v1/time/timer/active — poll for the active timer
  • POST /api/v1/time/timer/start — start a new one
  • POST /api/v1/time/timer/stop — stop the active one
  • POST /api/v1/time-entries — log a manual entry from the popup
  • GET /api/v1/time/reports/summary — for the popup's "today / this week" snapshot

No @/modules/* imports, no Supabase client. The same REST surface is what powers Zapier, Make, n8n, and any custom integration you'd write.


Next steps

  • Timer — the in-app composer, three views, and idle prompt
  • Integrations — the same REST API the extension uses, available to your own scripts