Software stack
In the product
- TypeScript and WXT. One set of source files makes a manifest for each browser.
- React 19 with
@wxt-dev/module-react. dexiewithdexie-react-hooksfor the cache.hls.jsfor video from Patreon.@tanstack/react-virtualfor the list of posts.- Tailwind CSS 4 and
shadcn/uion Base UI for the panel (ADR-017, the panel).shadcn/uiis not a dependency at run time: the command copies a component intocomponents/ui/, and that file then belongs to Gopher. The components and the palette come from the Aqua registry (@aqua, ADR-018), whichcomponents.jsonnames underregistries.@phosphor-icons/reactgives the icons, one import for each icon.components.jsonsays"iconLibrary": "phosphor", and Aqua writeslucide-reactimports inSelect. That file carries Phosphor imports, and the comment in the file records the change. A Phosphor icon gives itself noaria-hidden, thus each decorative icon must carry that attribute.
Dexie holds entity data only. Media bytes go to the Cache API. The state of the service worker
goes to browser.storage.session. The build loads hls.js only when it is necessary. The file
is 574 kB, and most sessions do not play a video.
The panel costs 183 kB of JavaScript and 11 kB of CSS, after compression. Base UI and the icons
made it 73 kB larger than the panel that we wrote ourselves. Phosphor holds six weights for each
icon, and 9 kB of that number is the difference from lucide-react. The panel loads no code over
the network, thus this is a cost in memory and not in time. Measure it again if the panel gets
more components.
For development: Playwright and tsx.
Software that Gopher does not use, and why
@tanstack/react-table. The table of attachments sorts three columns. That is approximately 40 lines incomponents/media.tsx. The library solves a much larger problem.- A library for localization (
@wxt-dev/i18n,i18next,react-intl). The layer inlib/i18n/is approximately 100 lines and has no dependency (ADR-016, localization). It gives the panel what the panel needs: a catalog for each language, placeholders, plural forms fromIntl.PluralRules, and a setting that the user controls. A library adds bytes, a second set of names, and a message format that nothing here uses. Examine this decision again if Gopher gets a language with rules thatIntlcannot express, or many more languages. webextension-polyfill. WXT 0.21 does not include it.wxt/browsergivesglobalThis.browseron Firefox andglobalThis.chromeon Chromium. Both give a promise for each API that Gopher calls. There is one difference: a listener forruntime.onMessagemust callsendResponseand then returntrue. If the listener returns a promise, only Firefox sends the reply.