Your Linear work on the desktop: the issues assigned to you, and your Linear inbox beside them.
- Issues tab — everything assigned to you that is not completed or cancelled. Titles are the largest thing on screen and wrap rather than truncate; identifier, state and due date sit quietly beneath. Rows glow in proportion to how urgent they are, so anything overdue stands out at a glance. Optionally grouped by team.
- Activity tab — your Linear inbox: mentions, review requests and pull request comments, replies on things you follow, assignments, status changes and the rest. Nine checkboxes decide which categories appear; reactions are off by default. Each row shows what the person actually wrote, with markdown stripped, and a line saying what happened where there is no remark to show; hovering shows it in full. Unread rows carry an accent border and a dot, and the tab carries an unread count. Opening one marks it read in Linear, exactly as opening it in the app would.
- Paged, ten rows at a time. Arrows beneath the list reach the rest. Stop paging and the list drifts back to the newest rows after thirty seconds — configurable, and held for as long as the pointer is over the desklet. Paging costs no network traffic: the whole window is fetched once and sliced locally.
- Two ways to sign in — a personal API key, or a browser sign-in for workspaces where an admin has switched personal keys off.
- One request per refresh. The viewer, the issues and the notifications arrive in a single GraphQL document, so the default five-minute interval uses about 12 of the 1,500 requests an hour a personal key allows.
- Survives a bad network. The last good response is cached, so a dropped connection shows stale data with a note rather than an empty desklet.
- Four colour modes — by priority, by workflow state, by the colour Linear itself uses for the state, or a fixed rainbow by list position. Borrowed Linear colours are brightened until they clear a 4.5:1 contrast ratio against the desklet surface.
Cinnamon 5.6 or newer (for libsoup 3). Developed and verified against Cinnamon 6.6.7.
git clone <this-repo> ~/git/linear-desklet
ln -s ~/git/linear-desklet/linear@ashex/files/linear@ashex \
~/.local/share/cinnamon/desklets/linear@ashexThen add it from System Settings → Desklets.
Two options, chosen in the desklet's settings.
Click Sign in with Linear. Your browser opens Linear's consent page, and
the desklet briefly listens on 127.0.0.1 to receive the reply. Nothing is
pasted, and no long-lived credential appears in the settings window.
Use this if your workspace does not allow personal API keys — an admin can switch off member key creation under Settings → Administration → API, and that setting does not apply to admins, so "it works for me" is not evidence it works for everyone.
The authentication process uses a localhost callback URI, in case the callback URI is on a port used on your machine, switch to another under Settings > Advanced.
If you prefer to use your own oauth app, specify the OAuth Client ID under Settings > Advanced
Paste a key from Settings → Security and access → Personal API keys.
The key is stored in plain text in
~/.config/cinnamon/spices/linear@ashex/<instance>.json, readable by
anything running as your user, and visible in the settings window.
OAuth tokens are kept out of that file: they go to
~/.local/state/linear@ashex/tokens-<instance>.json, created 0600.
libsecret would be better, but GJS needs Secret-1.typelib, which is not
part of a default Mint install.
read only, unless Mark a row read when you open it is on, which also
needs write. Linear has no notification-specific scope and write is
workspace-wide, so it is not requested unless that feature is actually
wanted. Turning it on after signing in prompts you to sign in again.
| Group | What is in it |
|---|---|
| Linear account | Sign-in method, API key or Connect/Disconnect |
| Issues | How many to show, whether to highlight the first, sort order, team grouping, how early a due date counts as imminent |
| Activity | Which notification categories to include, rows per page, how long before the list returns to the first page, how far back to fetch, unread only, whether opening one marks it read |
| Size and layout | Width, scale, density, header, which tab to open on |
| Appearance | Colour mode, surface opacity, neon glow, accent tinting, dark or light surface |
| Behaviour | Refresh interval, network timeout, what clicking the background does |
| Advanced | Sign-in port, your own OAuth Client ID |
- Some notification fields are marked internal by Linear. The
title,subtitleandurlfields on notifications are what Linear's own inbox renders from, but they carry no compatibility promise. The client asks for them, and falls back to a reduced query plus locally composed wording if they ever stop validating.tools/smoke-test.jsreports which path is in use. - Filtering happens client-side, both kinds. Linear has no server-side
filter on read state, and
NotificationFilterhas nocategoryfield, so "unread only" and the category checkboxes both trim the list after it arrives. The query therefore fetches a window far larger than one page — which is also what makes paging free. - Notification types are not an enum.
Notification.typeis a plainString, so filtering the query by type fails silently when a name is wrong or when Linear adds one: the list simply comes back shorter, with no error. The desklet filters oncategoryinstead, and anything it does not recognise is shown rather than hidden. - Document links are rebuilt by joining, not by string-building.
DocumentNotificationexposes only adocumentId, and a document's URL is keyed on itsslugId— an unrelated value that cannot be derived from the id. Linear routes a bareslugIdbut not a baredocumentId, so there is no string to construct. The fallback query fetches the workspace documents alongside and matches them up locally, which costs no extra request but is capped at 250 documents; beyond that the tail is dropped and logged rather than shown as a dead link. - A pull request link goes to the forge. Linear's own link points at a
/review/page whose slug the notification does not carry, so the fallback usespullRequest.urlinstead. It is a different destination, not a broken one — and it is where the comment actually is. No anchor is added, becausepullRequestCommentIdis a Linear id rather than the forge's and cannot address a comment there. - The libsoup 2 code path is untested. This was developed on a machine with
libsoup 3 only (no
Soup-2.4.typelib), so the Mint 20/21 branch has never been executed. - No avatars. Linear's avatar images sit behind the same API key, so showing them would mean a second authenticated request per row and a cache of other people's faces on disk. Initials are used instead.
- OAuth tokens are not in a keyring.
Secret-1.typelibis not part of a default Mint install, so they live in a0600file instead.
MIT License. That licence covers this desklet's own source code and nothing else.
The short version of both: this runs entirely on your own machine, talks
only to api.linear.app, and the author operates no server and receives no
data. Your credentials, your workspace's rules and your machine's security
are yours to look after. Provided as is, with no warranty.
"Linear" and "linear.app" are trademarks of Linear Orbit, Inc., who also own the copyright in the Linear service. This is an unofficial, third-party client, not made by, endorsed by or affiliated with them. Their name is used only to describe what the desklet connects to. No Linear artwork is bundled; the icon is original. The issues and mentions the desklet displays belong to you and your organisation, not to the author, who never sees them.
