Skip to content

GOODJINC/Sideist

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sideist

Sideist is a lightweight local sync bridge for Todoist and Google Calendar.

The first version focuses on the sync engine: it runs on your Windows PC, polls both APIs, stores local sync state, and keeps mapped Todoist projects and Google calendars aligned.

Sideist is an independent open-source project. It is not affiliated with, endorsed by, or sponsored by Todoist, Google, or Google Calendar.

Current Shape

  • .NET 10
  • No external NuGet packages
  • Todoist API v1 Sync endpoint
  • Google Calendar API v3
  • Local JSON state store
  • Configurable polling interval, default 15 seconds
  • Configurable default time zone, default Asia/Seoul
  • Exponential backoff after API/network failures
  • Google refresh token support for long-running sync

Project Structure

src/Sideist.Core             Pure sync models, ports, and sync engine
src/Sideist.Infrastructure   Todoist/Google HTTP clients and local state store
src/Sideist.Cli              Console runner for sync-once and background loop
src/Sideist.Tray             Windows tray background app

Build

dotnet build Sideist.slnx

Create Config

dotnet run --project src\Sideist.Cli -- init-config

This creates sideist.config.json. That file is ignored by git because it can contain tokens.

You can also copy sideist.config.example.json.

Run Once

dotnet run --project src\Sideist.Cli -- sync-once

Find Mapping IDs

dotnet run --project src\Sideist.Cli -- list-todoist-projects
dotnet run --project src\Sideist.Cli -- list-google-calendars

Use those IDs in sideist.config.json.

Google Login

Create a Google OAuth client with application type Desktop app, then put its client ID and client secret in sideist.config.json.

{
  "googleClientId": "...apps.googleusercontent.com",
  "googleClientSecret": "..."
}

Then run:

dotnet run --project src\Sideist.Cli -- google-login

Sideist opens a browser, waits on a local loopback callback, and stores the returned access token and refresh token in sideist.config.json.

If Google shows invalid_client, check that:

  • The OAuth client type is Desktop app
  • googleClientId is the value that ends with .apps.googleusercontent.com
  • googleClientSecret is the matching secret from the same OAuth client
  • The OAuth client was not deleted or copied from another Google Cloud project by mistake
  • You copied values without extra spaces, quotes inside quotes, or line breaks

Run Background Loop

dotnet run --project src\Sideist.Cli -- run

Stop with Ctrl+C.

Run Windows Tray App

dotnet run --project src\Sideist.Tray

The tray app starts syncing immediately and adds a Sideist icon to the Windows notification area.

On first run, Sideist opens an onboarding wizard when required credentials are missing. The wizard guides you through:

  • Todoist personal token setup
  • Google Calendar API and Desktop app OAuth setup
  • Browser-based Google login
  • Secure token storage confirmation

Tray menu actions:

  • Settings
  • Eisenhower Matrix
  • Sync now
  • Pause / Resume
  • Open project folder
  • Start with Windows
  • Exit

The Settings window lets you edit account tokens, tray icon click behavior, sync interval, default time zone, completion/deletion/conflict policies, project/calendar mappings, and Eisenhower Matrix filters without editing JSON by hand. The account tab includes Todoist token and Google OAuth setup guides, quick links, a Google login button, and reveal buttons for checking secret fields while typing. The JSON file can still be opened from inside the settings window for advanced edits.

Eisenhower Matrix

The tray app includes an Eisenhower Matrix window based on Todoist priority:

  • Todoist P1 -> Do now
  • Todoist P2 -> Schedule
  • Todoist P3 -> Delegate/reduce
  • Todoist P4 -> Hold/remove

Open it from the tray menu, or set the tray icon left-click action to OpenEisenhowerMatrix in Settings. Matrix settings can filter specific Todoist projects, include/exclude tasks with due dates, include/exclude tasks without due dates, and limit the due-date range around today.

Official languages are Korean and English. Change Language in the settings window, or set it in JSON:

{
  "language": "ko"
}

Use ko for Korean or en for English.

Publish

Create a self-contained Windows build:

.\scripts\publish.ps1

The output is written to artifacts\Sideist-win-x64.

Security

When settings are saved from the tray settings window, sensitive tokens are stored in Windows Credential Manager under Sideist/* entries and removed from sideist.config.json.

If a secret field is cleared and saved from Settings, the matching Windows Credential Manager entry is removed too.

Never commit sideist.config.json, OAuth tokens, personal API tokens, or published binaries. The repository .gitignore excludes local config, build outputs, and published artifacts.

If a token was pasted into an issue, chat, or commit history, revoke and regenerate it from the provider dashboard.

License

MIT. See LICENSE.

Recent Sync Log

The tray app writes recent sync messages to:

%LOCALAPPDATA%\Sideist\sync-history.jsonl

You can view and clear this from the Recent Sync tab in Settings.

Do not run Sideist.Cli -- run and Sideist.Tray at the same time for the same config, because both processes would try to sync the same account pair.

To pass a custom config path:

dotnet run --project src\Sideist.Tray -- --config D:\Projects\Sideist\sideist.config.json

Environment Variables

Tokens can be supplied through environment variables instead of the config file.

SIDEIST_TODOIST_TOKEN
SIDEIST_GOOGLE_ACCESS_TOKEN
SIDEIST_GOOGLE_CLIENT_ID
SIDEIST_GOOGLE_CLIENT_SECRET
SIDEIST_GOOGLE_REFRESH_TOKEN

For long-running sync, prefer google-login so googleClientId, googleClientSecret, and googleRefreshToken are configured. A raw Google access token is short-lived.

MVP Sync Rules

  • Todoist task with due date -> Google Calendar event
  • Todoist task with due datetime -> timed Google Calendar event
  • Todoist task with due date only -> all-day Google Calendar event
  • Google Calendar event -> Todoist task in the mapped project
  • Removing a Todoist due date removes the linked Google Calendar event
  • Todoist completion keeps the linked Google event unchanged by default
  • Deleting a Google event deletes the linked Todoist task by default
  • Recurring tasks/events are not deeply normalized yet

Completion And Deletion Policies

sideist.config.json supports safer policy controls:

{
  "sync": {
    "todoistCompletedAction": "keepGoogleEvent",
    "todoistCompletedMarkerPrefix": "[Done] ",
    "googleEventDeletedAction": "deleteTodoistTask",
    "conflictResolutionAction": "preferLatest"
  }
}

Allowed todoistCompletedAction values:

  • markGoogleEventDone
  • keepGoogleEvent
  • deleteGoogleEvent

When todoistCompletedAction is markGoogleEventDone, todoistCompletedMarkerPrefix is added to the Google event title. For a checkmark-style prefix, use \u2713 in JSON.

Allowed googleEventDeletedAction values:

  • deleteTodoistTask
  • removeTodoistDue
  • keepTodoistTask
  • completeTodoistTask

Allowed conflictResolutionAction values:

  • preferLatest
  • preferTodoist
  • preferGoogle
  • skipAndLog

Conflicts are detected when the same linked Todoist task and Google Calendar event both changed before the next sync cycle. The default keeps the most recently updated side.

Roadmap

  • Add Todoist OAuth
  • Add conflict review and dry-run preview UI
  • Replace JSON state with SQLite when mapping/log volume grows
  • Add signed installer
  • Add deeper recurring task/event handling

About

Local sync bridge for Todoist and Google Calendar

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors