Time-Bridge
- 4 Devlogs
- 7 Total hours
I’m building TimeBridge, it’s a Chrome extension that kills the mental math of scheduling across timezones. As you type, the extension passively reads your text.
I’m building TimeBridge, it’s a Chrome extension that kills the mental math of scheduling across timezones. As you type, the extension passively reads your text.
Logging the leftover time that got caught up during the project maintenance and finalization
The core loop works. So you type a time, the badge pops up and it tells you what time it is for your contact. But after using it for a few days, two massive problems became obvious: it was running everywhere and it was too loud.
Here’s how I fixed the extension to play nice with your browsing experience.
Originally, the manifest was set to <all_urls>. This meant the content script was injecting itself into every single page you visited. Your banking site, Reddit, random blogs. Even if the badge didn’t pop up, the event listeners were running on every text box on the internet which felt invasive and heavy. So I added an enabledSites list to chrome.storage.local. The extension now defaults to only running on whatsapp.com, slack.com and mail.google.com.
I also updated the popup UI. If you want to add a new site (like discord.com), you can either type it manually in the popup or just hit the “+ Add current site” button which grabs the URL of the tab you’re currently looking at. The content script listens to storage changes so if you add a site, the extension instantly activates on that tab and no reload is required.
The badge was a full card which activated whenever you typed a time or date, a giant dark tooltip would expand below your text box. If you were just typing a normal message that happened to include a time, the UI was visually shouting at you. SO I shrunk the UI down to a tiny 26x26 pixel clock icon (🕐). It still appears when you type a time phrase but it stays out of your way. If you actually want to see the conversion, you click the icon and the full panel expands. If you click away, it collapses back to the icon. It’s much less stressful to type with!
The skeleton is alive, the real-world quirks are patched and the timezone picker doesn’t suck anymore. But how does it actually feel to use?
Here’s how it works. You add a contact and the extension detects the time for them right where you’re typing.
When you click the extension icon, the popup opens. That right there is your contact book.
Before, I was using a native <select> dropdown for the timezones which meant scrolling through 418 IANA zones which was terrible. Now, it’s a custom search-as-you-type combobox.
You just type a name (like “Jean”) then start typing a country, city or abbreviation in the timezone box. If you type “Europe” the combobox instantly surfaces the available results because of the alias map I added. It still needs to be refined since it doesn’t yet contain ALL of the timezones. Also when you hit Enter and click “Add Contact”, it saves to chrome.storage.local, Jean now shows up in your list with her current local time updating live.
Now you head over to Gmail, Slack web or Twitter, anywhere with a text box. You start typing a message, lets say “Let’s sync tomorrow at 3pm”
Behind the scenes, it works like this:
The Listener: A content script is running on the page. It’s using a single delegated document.addEventListener in the capture phase. This means it catches your typing whether you’re in a plain , a rich text with nested formatting spans or even a contact form embedded in an iframe.
The Debounce: It doesn’t parse on every single keystroke (that would lag the browser). It uses a 250ms debounce. When you pause typing for a quarter of a second, it grabs the text.
The Parser: It passes that text to chrono-node, the parsing library I bundled. Chrono looks at the phrase “tomorrow at 3pm” and figures out what “tomorrow” actually is based on today’s date and spits out an actual Date object.
If Chrono finds a time, the extension builds a floating, dark-colored tooltip (the badge) and injects it right below the text box you’re typing in.
The badge does three things:
It shows you the exact phrase it detected: “tomorrow at 3pm”
It grabs the last contact you used (in this case, Jean) and automatically converts that 3pm time into her local time, displaying it right there.
It gives you a dropdown. If you actually meant to schedule this with a different contact, you can switch the dropdown right in the badge and it recalculates the time instantly.
One of the most annoying things to figure out was what happens when you hit Enter. On Slack or WhatsApp, Enter sends the message and clears the text box immediately.
Because of my 250ms debounce, the extension was occasionally showing the badge after the message was already sent, making it float over an empty text box. The fix was adding a specific keydown listener. If you hit enter (without shift), the extension kills the debounce timer and hides the badge instantly which means no more ghost badges
Spent the weekend building the first version of TimeBridge, a small Chrome extension aimed at making timezone coordination less of a headache. The idea is to detect time references as you type and eventually let you convert them to your contacts’ local times without doing the math yourself.
I started with two core features. A popup contact book that saves names and timezones using chrome.storage.local and a live text parser powered by chrono-node. The popup displays contacts’ local times while the parser detects phrases like “let’s call tomorrow at 3pm” using a debounced content script. I also used the browser’s native Intl.supportedValuesOf("timeZone") API to populate the timezone list instead of maintaining one manually and bundled only the English parser to bring its size down from around 400 KB to 100 KB but getting everything to run turned out to be its own challenge.
Chrome refused to load the extension because the bundled parser file was missing and regenerating it exposed an empty package.json that caused npm to throw an EJSONPARSE error. After fixing the dependencies, rebuilding the bundle with esbuild and correcting the script order in the manifest, the extension finally loaded. I also had to enable file URL access in Chrome before my local test page would work with the content script.
The skeleton is alive and well, the contacts persist, local times update and time expressions are detected as I type. There’s no UI for the parser yet so everything still goes to the console