Component Showcase

Desktop Companion Shipped · 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. Everything on this page is shipped and installable today.

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