Skip to content

Conversation

@mk1316
Copy link
Owner

@mk1316 mk1316 commented Feb 5, 2026

Summary

This PR introduces ActivityWatch (AW) compatibility to the application by adding a new database layer and API commands that follow the ActivityWatch data model. This enables the app to store and query activity data in a format compatible with ActivityWatch tools and integrations.

Key Changes

Database & Models

  • New migration (0003_activitywatch_compat.sql): Creates ActivityWatch-compatible schema with:

    • buckets table: Containers for events from specific watchers on specific hosts
    • events table: Activity data with flexible JSON payloads
    • key_value table: Settings and metadata storage
    • Views for easy querying (window events, usage summaries)
    • Migration of existing session data to new event format
  • New models (aw_models.rs): ActivityWatch-compatible data structures:

    • Bucket: Container for events from a watcher
    • Event: Activity event with timestamp, duration, and JSON data
    • Heartbeat: Efficient event submission format
    • ServerInfo: API info response
    • Helper types for window, AFK, web tab, and editor events
  • New database layer (aw_database.rs): Async database operations including:

    • Bucket CRUD operations
    • Event management with filtering and pagination
    • Heartbeat processing with intelligent event merging (extends events within pulsetime if data matches)
    • Usage summary queries
    • Key-value store for settings

API Commands

  • New commands (aw_commands.rs): 20+ Tauri commands exposing the ActivityWatch API:
    • Info: aw_get_info
    • Buckets: aw_get_buckets, aw_get_bucket, aw_create_bucket, aw_delete_bucket
    • Events: aw_get_events, aw_get_event, aw_insert_events, aw_delete_event, aw_get_event_count
    • Heartbeat: aw_heartbeat (efficient event submission)
    • Queries: aw_get_usage_summary, aw_get_current_event
    • Settings: aw_get_setting, aw_set_setting
    • Export: aw_export_bucket, aw_export_all

Integration

  • Tracking integration: Modified start_tracking() to:

    • Initialize ActivityWatch bucket for window tracking
    • Send heartbeats on app switches and during idle periods
    • Maintain backward compatibility with legacy session tracking
  • Dependencies: Added gethostname crate for hostname detection

Notable Implementation Details

  • Heartbeat merging: The heartbeat endpoint intelligently merges consecutive events with identical data that occur within the pulsetime window, reducing storage overhead
  • Backward compatibility: Existing session data is automatically migrated to the new event format
  • Flexible data model: Events use JSON for the data field, allowing arbitrary activity types
  • Async/await: All database operations are async using sqlx with tokio runtime
  • Persistent device ID: Reuses existing device ID system for consistent identification

Testing

The database layer includes unit tests for datetime parsing to ensure compatibility with both RFC3339 and SQLite datetime formats.

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J


Note

High Risk
Large cross-cutting change touching persistence/migrations, background tracking, a new local HTTP server, and cloud sync plumbing; regressions could affect data integrity, performance, or expose unintended local API behavior.

Overview
Adds an ActivityWatch-compatible data layer to the desktop app: a new SQLite migration introduces buckets, events, and key_value tables (plus indexes/views) and migrates legacy sessions into AW-style events.

Introduces new Rust modules for AW models, database operations (including heartbeat merging), Tauri command handlers (CRUD, export, settings, summaries), and an Axum REST server exposing AW-like endpoints on 127.0.0.1:5600; the app startup now enables SQLite foreign keys for all pooled connections and manages an additional AwDb state.

Updates usage tracking to create an AW window bucket and continuously emit AW heartbeats (on app switches and periodic keep-alives) while keeping legacy session tracking.

Adds a Neon-based cloud sync implementation (neon.rs) and updates frontend hooks/types to support AW APIs, AW query/categorization helpers, and Neon bucket/event sync; Supabase sync is marked deprecated. Webclient also includes small auth hardening (null-supabase guard) and UI/theming updates (fonts + global CSS refresh).

Written by Cursor Bugbot for commit 53e3ea9. This will update automatically on new commits. Configure here.

- Add SQL migration for buckets and events tables (AW-compatible schema)
- Create Rust structs for Bucket, Event, Heartbeat in aw_models.rs
- Implement AwDatabase with CRUD operations and heartbeat merging
- Add Tauri commands for AW API (aw_commands.rs)
- Update tracking loop to send heartbeats alongside legacy sessions
- Add TypeScript types for AW API (AWBucket, AWEvent, etc.)
- Create useActivityWatch React hook for frontend integration

This enables compatibility with ActivityWatch ecosystem including
Awatcher for Linux, browser extensions, and editor plugins.

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
- Add axum, tower, tower-http dependencies for HTTP server
- Create aw_server.rs with full AW REST API implementation
- Server runs on localhost:5600 (standard AW port)
- Implement all bucket endpoints (CRUD)
- Implement all event endpoints (CRUD, count)
- Implement heartbeat endpoint with pulsetime merging
- Implement export endpoints (single bucket and all)
- Add CORS support for browser extensions
- Server starts automatically on app launch

This enables external watchers like Awatcher for Linux,
browser extensions, and editor plugins to connect directly.

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
- Added Neon PostgreSQL client for cloud sync (replaces Supabase)
- Implemented Query API with aw-query compatible syntax
- Added query functions: filter, merge, categorize, summarize
- Updated useSync hook to work with Neon
- Added useNeonSync and useQuery frontend hooks
- Added regex dependency for category matching
- Deprecated Supabase module (to be removed)

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
@vercel
Copy link

vercel bot commented Feb 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
loopd Error Error Feb 6, 2026 9:16pm

- Fix heartbeat merge logic to check time_diff >= 0 to prevent event
  data corruption from clock drift causing negative time differences
- Fix migration timestamps to use RFC3339 format (strftime with 'T'
  separator and 'Z' suffix) for proper lexicographic comparison with
  new events
- Fix glob-to-regex conversion to properly escape regex metacharacters
  before converting glob wildcards, preventing patterns like "test.app"
  from incorrectly matching "testXapp"

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
- Fix parse_datetime to log warnings and return epoch time instead of
  current time on parse failure, preventing silent data corruption
- Enable SQLite foreign key constraints with PRAGMA foreign_keys = ON
  to ensure ON DELETE CASCADE works properly for bucket/event cleanup

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
- Fix Neon URL construction to properly handle query parameters by
  inserting /sql before the query string instead of naively appending
- Fix query parser to check for function calls before variable assignment,
  preventing misparse of function arguments containing '=' characters
- Fix heartbeat duration inflation by using duration: 0.0 instead of 1.0,
  since actual duration is calculated from timestamp differences during merge

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
- Fix foreign key PRAGMA to apply to all pool connections by using
  SqliteConnectOptions with .pragma() instead of executing PRAGMA
  on a single connection after pool creation
- Fix empty title fallback by skipping heartbeat when window title
  can't be retrieved, preventing event fragmentation from empty
  title comparisons failing

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
- Remove duplicate BucketExport struct from aw_server.rs, import from aw_models
- Fix TOCTOU race condition in get_or_create_bucket using INSERT OR IGNORE
- Wrap insert_events in a transaction for atomic all-or-nothing insertion
- Change heartbeat pulsetime from 5 seconds to 10 minutes for better
  consecutive event merging

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
Fetch app and title together from get_active_app_with_title() to ensure
consistency. Verify that the app hasn't changed before sending heartbeat
to prevent corrupted event data when user switches apps mid-heartbeat.

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
Parse connection string to extract just the host component, stripping
user:pass credentials and database path. Neon's HTTP API expects the
endpoint at https://host/sql, not with database path included.

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
- Fix font loading issue by using CSS-based Google Fonts instead of next/font
- Update Supabase clients to handle missing env vars gracefully during build
- Completely redesign landing page with modern aesthetic:
  - Interactive mouse-following gradient background
  - Bento grid layout for features section
  - Dashboard preview mockup in hero
  - Improved animations and micro-interactions
  - New violet/fuchsia/cyan color scheme
  - Platform stats and status indicators
- Update globals.css with new design system tokens and utilities

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
Import SupabaseClient type from @supabase/supabase-js instead of
@supabase/ssr which doesn't export this type.

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
Clean up unused state and event listener to avoid potential warnings.

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
Configure Vercel to build from the webclient subdirectory instead
of the root, ensuring proper Next.js build process.

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
.execute(&self.pool)
.await?;
Ok(())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused delete_setting method is dead code

Low Severity

The delete_setting method in AwDatabase is defined but never called. There's no Tauri command or REST API endpoint that exposes this functionality. The key-value store API only has get_setting and set_setting exposed to consumers.

Fix in Cursor Fix in Web

The Vercel configuration should be in the same directory as the
Next.js application's package.json.

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
The vercel.json file might be conflicting with Vercel's project
configuration. Removing it to let Vercel use dashboard settings.

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

- Fix Neon URL construction to strip port numbers from host
  (e.g., "host:5432" -> "host" for HTTPS connection)
- Remove unused NeonEvent::from_event helper method
- Remove unused AwDatabase::create_bucket method (get_or_create_bucket is used instead)
- Remove unused AwDatabase::delete_setting method
- Fix NULL local_id duplication issue by skipping events without local_id
  (PostgreSQL's unique constraint doesn't prevent duplicates when NULL)

https://claude.ai/code/session_01QYNePFqXFj4AaRtTDJVe6J
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants