Component Showcase

Desktop Companion Built ยท v0.2.0

The browser can only see the browser. The companion is a small Windows tray app that watches the rest of the desktop and tells the extension what it finds, so a stint spent in Figma, Excel or a call is time your day actually accounts for instead of a five-hour hole labelled "idle". It is a separate product with its own release line, its own palette, and its own database. Every component below is built and running today. It is not yet something you can download.

Get the companion

No public download yet
Windows 10/11 ยท x64 Version 0.2.0 Tauri ยท Rust

The companion is real and it runs, but it exists as a binary on the machine that compiled it and nowhere else. There is no repository, no release and no installer to point you at, so this page will not hand you a link that does not work. Windows only, and not by oversight: the window monitor is bound to Win32 and the capture engine draws through GDI, so macOS and Linux are not a build target away, they are a port.

It watches which application and window is in front, sorts that into categories, keeps it in a local SQLite database, and pushes what it sees to the extension over a loopback WebSocket. It can also capture the screen while the browser is not in front, throttled and redacted, which is the half of the record the extension cannot see. That capture is the reason this is not a casual install, and the reason the release below is gated rather than rushed.

Goes to the backlog as a request, not to a download.
Blocked on
  • A hosted repository. The source has no git remote. It has never left this machine.
  • An installer. Only cargo build --release has run, which yields a bare 16 MB exe with no Start-menu entry, no uninstall and no auto-start registration. Bundling (tauri build) has not been run.
  • Code signing. No certificate is configured. An unsigned binary that captures the screen and opens a socket will trip SmartScreen, and should.
  • A companion update channel. The existing updater ships the extension, not the companion. Handing out 0.2.0 today means every fix is a manual reinstall.
  • Docs that match the code. The README still says v0.1.0 and "no screenshots, ever" while a capture engine ships. Publishing against that would deny the most privacy-sensitive thing the product does.
  • Auth on the local socket. Port 9147 is loopback-bound but unauthenticated. Any local process can connect. Acceptable on one developer's machine, not on yours.

Component library

15 surfaces & subsystems ยท rendered from live companion tokens

The companion has almost no UI on purpose. It is a tray icon, one optional 480ร—640 window, and a great deal of machinery underneath - because the thing it is trying to be is invisible: if you have to interact with it, it has already failed at its job. So most of what follows is not a screen but a subsystem, shown as the surface through which you would ever actually meet it. The stages here render the companion's own tokens (accent #4a9eff, green #10ac84) and its 8/4 radius scale - it is a different app from the extension, and it does not pretend otherwise.

โธ Pause Tabbing
๐Ÿ• Clock Out
๐Ÿ“ธ Capture โœ“
๐Ÿ“Š Activity Log
๐Ÿ›  Desk Panel
โฌ† Check for Updates
โœ… Start on Login
๐Ÿ”„ Restart
โŒ Exit Completely
๐Ÿ“‹
Tray menu Live
The companion's only permanent surface - there is no main window at all. Eleven items across three groups: what it is doing now, where to look, and how it starts. The first two relabel themselves against live state (Pause โ‡„ Resume, Clock In โ‡„ Clock Out), and Capture is a real checkbox that persists to settings.json rather than a dialog.
src-tauri/src/main.rs
tracking
Tabby Desk v0.2.0 (release) - Tabbing
paused
Tabby Desk v0.2.0 (release) - Paused
๐ŸŸข
Tray icon & tooltip Live
Ships no icon file: the 16ร—16 circle is rasterized in Rust at runtime, so the colour is state rather than a swapped asset. Green means it is watching, amber means you paused it. The tooltip is the only place the running build number is ever stated, and it says (release) or (debug) so a stray dev build cannot masquerade as the real one.
src-tauri/src/main.rs ยท make_tray_icon()

โšก Tabby Desk v0.2.0

โ— Tabbing
๐Ÿ‘ Active Window
Visual Studio Code
TagPicker.jsx - tabatha
๐Ÿ’ป development
โฐ Time Clock
04:12:38
since 09:02 ยท 18m break
๐Ÿ“Š Today
4h 12m
Active time
63
App switches
WebSocket server on ws://localhost:9147
๐Ÿ› 
Desk Panel Live
A 480ร—640 webview you open from the tray and close again - never shown at launch, because the companion is meant to be forgotten. Six stacked cards polling the Rust side once a second. Its version string is read at runtime rather than hardcoded, which is the fix for a build that spent a release claiming to be v0.1.0.
src/App.jsx
connected
โœ“ Extension connected
Tabatha is talking to your browser. v6.7.18
disconnected
โš  Extension stopped responding
Last seen 14:07:22. Open chrome://extensions and check Tabatha is still enabled.
never installed
๐Ÿงฉ Install the browser extension
The companion ships with it. Load it once and they find each other.
%APPDATA%\Tabatha Desktop\extension
1Copy the path above
2Open chrome://extensions
3Enable Developer mode โ†’ Load unpacked
๐Ÿงฉ
InstallGuide Live
Three states, not two: never installed gets the full walkthrough, but disconnected is deliberately a separate, amber case - "it broke" and "you never set it up" want opposite advice, and collapsing them sends people to re-install something they already have. Chrome refuses programmatic navigation to chrome:// URLs, so the open button copies the URL and says so instead of silently doing nothing.
src/components/InstallGuide.jsx
tracking
๐Ÿ‘ Active Window
Figma
Tabatha - Gatekeeper explorations
๐ŸŽจ design
idle ยท 5 min threshold
๐Ÿ‘ Active Window
Idle
No input for 5m 22s
โธ not counted
๐Ÿ‘
Window monitor Live
Polls the foreground window once a second through the Win32 API and cuts a new session whenever the app or the window title changes - title-level, so two hours in one editor still resolves into the files you were actually in. Idle after five minutes of no input. The whole loop runs inside a panic guard: a crash in one tick is logged and retried on the next rather than taking the tray down with it.
src-tauri/src/window_monitor.rs
clocked out
โฐ Time Clock
00:00:00
not clocked in
clocked in
โฐ Time Clock
04:12:38
since 09:02 ยท 18m break
on break
โฐ Time Clock
04:12:38
on break since 13:41
โฐ
Time Clock Live
The same shift the extension sees, driven from either end. Clocking in is idempotent and clocking out closes every open session rather than the newest - both are v0.2.0 fixes for a real bug: the tray, the extension and the auto-clock could each open a shift, and one stale row left the companion insisting you had been working for eleven days.
src-tauri/src/activity_log.rs
๐Ÿ“Š Today
4h 12m
Active time
63
App switches
18m
Break time
6
Categories
Top apps
๐Ÿ’ปCode1h 54m
๐ŸŽจFigma1h 06m
๐Ÿ’ฌSlack32m
๐Ÿ“ŠExcel21m
๐Ÿ“Š
Today summary Live
Four tiles and the top five apps. The still-open session is counted live rather than at its next boundary, so the number is right mid-morning instead of only after you switch away. Days roll at UTC, which is a real quirk worth knowing: near midnight in a distant timezone this card and the tray's report disagree, because the report prints local time.
src-tauri/src/activity_log.rs ยท get_daily_summary()
๐Ÿ• Recent Activity
๐Ÿ’ปCode - TagPicker.jsx22m
๐ŸŒChrome - (handed to extension)14m
๐ŸŽจFigma - Gatekeeper explorations38m
๐Ÿ’ฌSlack - #flux-dev6m
๐Ÿ“ŠExcel - Q1 charts.xlsx21m
๐Ÿ•
Recent Activity Live
The last twenty sessions, newest first, each as app ยท window title ยท duration. Browser rows are the seam in the whole architecture: the companion sees Chrome is focused but hands the content question to the extension, which knows the tab, its Context and its intent. Two trackers, one timeline, no double-count.
src/App.jsx ยท get_today_sessions
๐Ÿ’ปdevelopment~30
๐Ÿ’ฌcommunication~12
๐ŸŽจdesign~28
๐Ÿ“‹productivity~10
๐Ÿ“งemail~5
๐ŸŽตmedia~8
๐ŸŽฎentertainment~6
๐ŸŒbrowser~4
โš™๏ธsystem~7
โ“unknownfallback
๐Ÿท
Categorizer Live
Nine categories plus an honest unknown fallback, over roughly a hundred executable names - matched on exact filename, case-insensitively, so a renamed binary lands in unknown rather than being guessed at. Design is the biggest bucket after development, mostly because the Adobe suite is large. The extension's own category engine was ported from this file, which is why a day reads the same on both sides.
src-tauri/src/categorizer.rs
โ— Connected ws://localhost:9147/tabatha loopback only ยท 1 client
โ†‘ Companion โ†’ extension
APP_SWITCHAPP_SESSION_END COMPANION_STATUSCLOCK_STATE DAILY_SUMMARYIDLE_STATE UPDATE_READYCAPTURE_TAKEN CAPTURE_STATEFILE_WRITTEN
โ†“ Extension โ†’ companion
FOCUS_UPDATECLOCK_IN CLOCK_OUTTOGGLE_BREAK GET_CLOCK_STATEREQUEST_SUMMARY CATEGORY_RULESPING CAPTURE_CONFIGGET_CAPTURE_STATE CAPTURE_FRAMEWRITE_EXPORT
๐Ÿ”Œ
WebSocket bridge Live
The whole contract between the two products, on 127.0.0.1 only - never 0.0.0.0, so nothing on your network can listen in. Ten message types out, twelve in. If the port is taken the companion logs it and carries on tray-only rather than refusing to start. FOCUS_UPDATE is the interesting one: it splits your focus label into keywords and tags any desktop window whose title matches, so "Q1 charts" in the browser silently claims the Excel file too.
src-tauri/src/ws_server.rs
app_sessions
id ยท app_name ยท app_display_name ยท window_title ยท category ยท started_at ยท ended_at ยท duration_ms ยท matched_focus_id ยท matched_context
clock_sessions
id ยท clocked_in_at ยท clocked_out_at ยท total_break_ms ยท label ยท source
clock_breaks
id ยท clock_session_id โ†’ ยท started_at ยท ended_at ยท duration_ms ยท note
tabatha_activity.db ยท local SQLite ยท never leaves the machine unless you sync
๐Ÿ—„
Activity log Live
Three tables and three indexes of local SQLite. The part worth knowing is what happens after a crash or a flat battery: on every start it reconciles orphans - an app session left open is closed at the moment the next one began, not at "now", so a laptop that slept over the weekend does not wake up crediting you sixty hours in a text editor.
src-tauri/src/activity_log.rs
๐Ÿ“ธ Capture ยท off by default
%APPDATA%\Tabatha Desktop\captures\<partition>\<YYYY-MM>\2026-07-16T03-12-45-123Z_os_personal_report-xlsx-excel.jpg
skipped while idle skipped when a browser is in front suppressed by rule redaction fails closed min-gap throttle
Partition follows the clock: org while clocked in, otherwise personal - retained 90 and 30 days.
๐Ÿ“ธ
Screen capture Live ยท opt-in
The OS half of a browser-plus-desktop pair: the extension captures tabs, this captures everything else, and it stands down the moment a browser is in front so the two never race. Disabled until you turn it on. Sensitive-window rules can suppress or redact a region, and redaction fails closed - a rule that resolves to zero pixels suppresses the frame rather than writing an unredacted one. Note this contradicts the companion's own README, which still carries a "no screenshots, ever" line from v0.1.0; the code is the authority and the README is stale.
src-tauri/src/screen_capture.rs
โฌ† Update the extension
โœ“New version is strictly greater than the installed one
โœ“Zip SHA-256 matches the published manifest
โœ“Payload really contains manifest.json + background.js
โœ“Extension key is identical - or refuse
โœ“Staging dirs can never touch the database file
โœ“Swap is atomic, with rollback
Tray: "Tabby Desk - Checking for updatesโ€ฆ"
โฌ†
Extension updater Live
Updates the browser extension, not itself - the companion is how Tabatha self-distributes outside the Web Store. Six invariants, each individually tested, and the identity check is the one that matters: a changed extension key means a changed extension ID, which would silently orphan all your stored data, so a key mismatch refuses the update outright. Every outcome is reported through the tray tooltip, because a windowless app has no console to print to.
src-tauri/src/updater.rs
โœ… Start on Login
HKCU\โ€ฆ\CurrentVersion\Run โ†’ TabathaDesktop
๐Ÿ”— Protocol handler
HKCU\Software\Classes\tabatha://
Enabled once, on first run only. After that the tray toggle is the only thing that decides.
๐Ÿ”
Autostart & protocol Live
Two plain registry writes rather than a plugin. It turns itself on the first time it ever runs and then never touches the setting again - guarded by a sentinel file, so no update can quietly re-enable an autostart you deliberately switched off. The tabatha:// handler is registered every start (it is idempotent) and lets the extension relaunch the companion if it finds it gone.
src-tauri/src/main.rs ยท mod startup
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ TABBY DESK - ACTIVITY REPORT โ”‚ โ”‚ Thursday, 16 July 2026 โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Active time 4h 12m โ”‚ โ”‚ Break time 18m โ”‚ โ”‚ App switches 63 โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ ๐Ÿ’ป development 1h 54m 45% โ”‚ โ”‚ ๐ŸŽจ design 1h 06m 26% โ”‚ โ”‚ ๐Ÿ’ฌ communication 32m 13% โ”‚ โ”‚ ๐Ÿ“‹ productivity 21m 8% โ”‚ โ”‚ โš™๏ธ system 12m 5% โ”‚ โ”‚ โ“ unknown 7m 3% โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
๐Ÿ“„
Activity report Live
"๐Ÿ“Š Activity Log" in the tray writes this to a text file and hands it to whatever opens .txt. Box-drawing characters and a monospace grid, deliberately: it is meant to be readable in Notepad, pasteable into a message, and legible in ten years without Tabatha installed to open it. This is the report that prints local time while the panel above counts UTC days.
src-tauri/src/main.rs ยท show_log