{"id":51450768,"url":"https://github.com/olafkfreund/waycal","last_synced_at":"2026-07-05T20:30:24.461Z","repository":{"id":366074941,"uuid":"1274976612","full_name":"olafkfreund/waycal","owner":"olafkfreund","description":"Calendar, Mail and Tasks widgets for Wayland/niri — Quickshell (QML) front end driven by the gog CLI. Declarative Nix flake + home-manager.","archived":false,"fork":false,"pushed_at":"2026-06-20T05:49:39.000Z","size":42,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-20T07:14:03.365Z","etag":null,"topics":["calendar","gmail","google-tasks","gruvbox","home-manager","niri","nix","qml","quickshell","wayland"],"latest_commit_sha":null,"homepage":null,"language":"QML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/olafkfreund.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-20T05:21:49.000Z","updated_at":"2026-06-20T05:49:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/olafkfreund/waycal","commit_stats":null,"previous_names":["olafkfreund/waycal"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/olafkfreund/waycal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olafkfreund%2Fwaycal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olafkfreund%2Fwaycal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olafkfreund%2Fwaycal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olafkfreund%2Fwaycal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/olafkfreund","download_url":"https://codeload.github.com/olafkfreund/waycal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/olafkfreund%2Fwaycal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35168795,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calendar","gmail","google-tasks","gruvbox","home-manager","niri","nix","qml","quickshell","wayland"],"created_at":"2026-07-05T20:30:22.021Z","updated_at":"2026-07-05T20:30:24.455Z","avatar_url":"https://github.com/olafkfreund.png","language":"QML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# waycal\n\nA three-widget Google productivity suite for **Wayland / niri**, rendered with\n[Quickshell](https://quickshell.org) and driven entirely by the\n[`gog`](https://github.com/) CLI — no in-app OAuth, no Google API client.\n\n| Widget | What it shows | Toggle |\n| --- | --- | --- |\n| **Calendar** | always-on agenda (next N days) + full-month dashboard overlay | `Mod+C` |\n| **Mail** | unread Gmail threads + detail overlay | `Mod+M` |\n| **Tasks** | open Google Tasks, check to complete | `Mod+T` |\n\nInspired by [waylandar](https://github.com/samjoshuadud/waylandar), but the\nbackend is the `gog` CLI rather than a bundled Python Google-API client.\n\n## How it works\n\n```\nniri keybind ─► qs -c waycal ipc call \u003ctarget\u003e toggle\n                          │\n   systemd --user: waycal.service  (EnvironmentFile → GOG_KEYRING_PASSWORD, GOG_ACCOUNT)\n                          │  spawns (inherits env)\n       QML Process ─► waycal-fetch \u003ccmd\u003e     (thin Python adapter, stdlib only)\n                          │  spawns (inherits env)\n                      gog --json …            (Google Calendar / Gmail / Tasks)\n```\n\nThe entire frontend↔backend contract is **\"a process prints a JSON array to\nstdout; QML `JSON.parse`s it.\"** `waycal-fetch` shells out to `gog --json …`,\nnormalizes the result into a small uniform schema, and prints it. On failure it\nprints `{\"error\": …, \"needsAuth\": …}` so a widget can show a hint instead of\ncrashing. No sockets, no IPC files.\n\n### Normalized schemas\n- **event** `{id, title, description, start, end, allDay, location, link, calendar}`\n- **mail** `{id, threadId, from, subject, snippet, date, unread, link}`\n- **task** `{id, listId, list, title, notes, due, status, link}`\n\n## Prerequisites\n\n- `gog` configured with at least one account (`gog auth add you@example.com`).\n- A Wayland compositor with `wlr-layer-shell` (niri, Hyprland, sway, …).\n- Quickshell (pulled in by the flake / home-manager module).\n\n### The one hard requirement: gog's keyring password\n\n`gog` stores its OAuth refresh token in a **file keyring** that needs\n`GOG_KEYRING_PASSWORD` to be readable in a non-interactive process. The widgets\nrun non-interactively, so this **must** be supplied. The home-manager module\ndoes it via a systemd `EnvironmentFile` secret (see below). Verify your setup:\n\n```bash\ngog auth doctor\n```\n\n## Install (NixOS flake + home-manager module)\n\n\u003e home-manager is consumed as a **flake module**, not the `home-manager` CLI.\n\n```nix\n# flake.nix\n{\n  inputs.waycal.url = \"github:olafkfreund/waycal\";\n\n  # in your home-manager configuration:\n  imports = [ inputs.waycal.homeManagerModules.waycal ];\n\n  programs.waycal = {\n    enable = true;\n    account = \"you@example.com\";\n    # secret file containing:  GOG_KEYRING_PASSWORD=…   (and optionally GOG_ACCOUNT=…)\n    keyringPasswordFile = config.age.secrets.\"waycal-gog\".path;\n\n    settings = {\n      agenda_days = 7;\n      mail_query  = \"in:inbox is:unread\";\n      task_lists  = \"all\";\n    };\n  };\n}\n```\n\nThis installs `waycal` + `quickshell`, writes the Quickshell config to\n`~/.config/quickshell/waycal`, renders `~/.config/waycal/config.toml`, and starts\na **systemd --user** service (`waycal.service`) that runs `qs -c waycal` with the\nkeyring secret in its environment.\n\nThen add the [niri keybinds](examples/niri-config.kdl).\n\n## Use without the module\n\n```bash\n# run the UI directly (gog must find GOG_KEYRING_PASSWORD in the env)\nnix run github:olafkfreund/waycal#  # or: nix develop, then `qs -p ./frontend`\n\n# debug the adapter on its own\nnix run github:olafkfreund/waycal#waycal-fetch -- agenda --days 7 | jq\n```\n\nCopy [`examples/config.toml`](examples/config.toml) to `~/.config/waycal/config.toml`.\n\n## Configuration\n\nAll keys live in `~/.config/waycal/config.toml` (or `programs.waycal.settings`):\n\n| key | default | meaning |\n| --- | --- | --- |\n| `account` | `\"\"` | gog account email (`\"\"` → `$GOG_ACCOUNT` / single token) |\n| `agenda_days` | `7` | days ahead for the agenda widget |\n| `agenda_max` | `50` | max agenda events |\n| `calendars` | `\"all\"` | `\"all\"` or comma-separated ids/names |\n| `mail_query` | `\"in:inbox is:unread\"` | Gmail search query |\n| `mail_max` | `20` | max mail rows |\n| `task_lists` | `\"all\"` | `\"all\"` or comma-separated list ids/names |\n| `task_max` | `50` | max tasks per list |\n\n## Development\n\n```bash\nnix develop          # quickshell + python3 + uv + jq + nix linters\nqs -p ./frontend     # run the UI from the source tree\npython backend/waycal_fetch.py agenda --days 7 | jq\npython backend/waycal_fetch.py raw -- calendar events --from today --days 1   # dump raw gog JSON\n```\n\n`waycal-fetch raw -- \u003cgog args\u003e` prints gog's untouched JSON — handy if a field\nisn't being picked up and a normalizer in `backend/waycal_fetch.py` needs a tweak.\n\n## Roadmap (Phase 2)\n\n- `notify-send` reminder daemon (a systemd timer running `waycal-fetch agenda`,\n  independent of widget visibility).\n- Live [matugen](matugen/Theme.qml.tmpl) theming (the `Theme` singleton is already\n  the single source of all colors).\n- Multi-account.\n\n## Layout\n\n```\nbackend/waycal_fetch.py     # the gog adapter (stdlib only)\nfrontend/                   # Quickshell config (shell.qml + widgets + 3 service singletons)\nnix/package.nix             # build: copy + waycal-fetch wrapper\nmodules/home-manager.nix    # programs.waycal (config + systemd secret wiring)\nmatugen/Theme.qml.tmpl      # optional theming template\nexamples/                   # niri keybinds + sample config.toml\n```\n\n## License\n\nMIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folafkfreund%2Fwaycal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Folafkfreund%2Fwaycal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folafkfreund%2Fwaycal/lists"}