Skim v1.0.27 — "Old News" 📰
The one where your second computer stops greeting you with a breathless "12 NEW MESSAGES!" about mail you finished reading at lunch.
Fixed
No more ghost notifications. Read your mail at work all day, come home, boot the other machine and Skim used to toast about every single message that arrived meanwhile, badge and all. It had the "already read" flag right there in its hands the whole time. It just... didn't look. Now it looks. Only mail the server still holds unread gets a toast.
The tray badge tells the truth on startup. It used to paint yesterday's unread count the instant the icon appeared, then quietly correct itself minutes later after a full folder sweep. Now the badge waits for the first inbox sync and shows the real number no stale flash, no "wait, where did those 12 go?"
Badge follows push, not the 5-minute poll. The IDLE-driven inbox sync the one that actually fires when mail lands never repainted the badge. Now it does, on success and on failure (offline still gets the cached count, just honestly).
Housekeeping
Skim v1.0.16 — "Are You Still There?" 👻
The one where Skim stops impersonating a corpse.
A self-update could relaunch Skim into a window with a titlebar and nothing. Restarts didn't help. Reinstalling didn't help. Then, hours later, it just came back which is somehow more unsettling than staying broken.
Turns out nothing was broken. The whole UI was waiting on the first mailbox query, that query was politely queued behind the catch-up sync, and the app had no way to say "hang on, I'm reading" so it said nothing at all. For hours. In an empty window. Cool.
Fixed
Skim v1.0.22 — "Ask the MX, not the vibes" 🛂
The one where Skim stops pretending that Microsoft owns exactly four domains on the entire internet.
New
Work & school accounts are finally recognized. If your company lives on Exchange Online, Skim now asks your domain's MX records who really hosts the mail. Everything Microsoft runs answers on *.protection.outlook.com, so the sign-in button that actually works shows up on its own no more falling into the manual IMAP/SMTP form to guess hostnames that were never going to connect. (Yes, hotmail.de too. Sorry it took a whole minor version to notice Germany exists. )
Behind a mail gateway? Proofpoint and Mimecast tenants don't advertise Microsoft in MX at all, so unknown domains now get one quiet "Work or school account?" line instead of a shrug. One line. We counted.
The DNS probe goes through the machine's own resolver (DnsQuery_W) your domain never visits a third party, results are memoized, the whole thing is capped at 3 seconds, and if it fails the screen looks exactly like it did before.
Fixed
Skim v1.0.24 — "You were knocking with the wrong key" 🔑
The one where OpenAI's smartest models finally answer. Turns out we'd been calling one JSON field by a name they retired back when o1 shipped, and they were not shy about it.
Fixed
-
Reasoning models on a custom OpenAI endpoint work again. Point the OpenAI-compatible provider at OpenAI itself, pick anything from the o-series or the GPT-5 family, and every single request died before the first token: Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead. Which is a wonderfully clear error message to receive zero times, because you're in a chat window and all you see is that nothing happened.
The lazy fix would be to rename the field everywhere and move on. Except only about half of the OpenAI-compatible world speaks the new name: Mistral answers 422, and Ollama and llama-cpp-python ignore it in complete silence which doesn't fail your request, it just removes the ceiling on the answer. That's strictly worse than an error, because nobody ever finds out.
So Skim now opens with the name every server understands, and switches only for an endpoint that asks for the other one by name, in its own error and only when that error names both keys, which is what separates "use the other name" from "that number is too big". Cost: one rejected request the first time you talk to such an endpoint. No new settings, no model list to maintain, nothing for you to choose. Exactly the amount of thinking about this you should have to do: none.
Huge thanks to @ondrahracek for this one not just the fix, but a writeup that walked through what LangChain and goose each broke trying the obvious thing first, and two tests verified by mutation. Reviewing that was a genuine pleasure.
Housekeeping
Skim v1.0.26 — "Think Less, Answer More" 🧠
The one where OpenAI told us our tools and its own thinking couldn't be in the same room, and we said fine, we'll ask nicely twice.
Fixed
Mailbox chat works again on gpt-5.6 models. Point the OpenAI-compatible provider at OpenAI with a gpt-5.6-* model and every single round that carried tools came back as a 400 and a wall of English error prose in the chat bubble. Retrying showed you the same wall. Very helpful. (#40)
The twist: Skim never sent reasoning_effort at all. The model reasons at medium on its own, then refuses its own default alongside our tools so nothing in our request even named the thing that broke it.We still don't guess. No model-name regex, no "is this OpenAI?" sniffing the same base URL serves OpenRouter, Ollama, LM Studio, vLLM and whatever you typed in that box, and half of them would 422 on the "fix". So Skim sends nothing up front, waits for the endpoint to name the remedy in its own error, and retries exactly once with the value it asked for tools still attached, because answering without the search results would rather defeat the point.
Housekeeping
Skim v1.0.23 — "Press the button, something happens" 🔘
The one where the Restart to update button admits it heard you. Turns out shipping an updater whose only feedback is "trust me bro" was a bold choice.
Fixed
"Restart to update" now actually restarts. Before: you clicked, nothing happened, you clicked four more times out of spite, and a minute later the app quietly closed on its own. The click was stuck waiting on a database write that the mail sync had no intention of letting go of. Now the banner flips to "Installing update " the instant you click it and stops being a button, so those four extra clicks can't each launch their own installer racing the other three for your install folder.
A clean exit before the installer takes over. The updater ends this process with process::exit(0) no goodbyes. So on the way out Skim now parks the sync engines and folds the write-ahead log back into the database, instead of leaving a few megabytes of unfinished business for the next build to untangle on its very first query.
The message list stopped lying to you. "Nothing to skim" used to mean either "this folder is empty" or "the read blew up and nobody told you". Guess which one you got after updating. It now says "Couldn't load retry" and gives you the retry, and only claims a folder is empty when it actually read the folder.
Sync events that arrive before the app is ready no longer vanish. The mail engine starts syncing in Rust before the window has finished asking who owns which mailbox; the first "there's new mail" used to land in the void, and the next scheduled one was five minutes away. They're collected now.
Reopening from the tray does something again. Hiding to the tray only hides the webview no frontend code re-runs on the way back, so a list left empty by a failed read stayed empty no matter how many times you reopened it. The window now says "I'm back", and a broken view puts itself right.
Housekeeping
Skim v1.0.12 — "Warm Sockets" 🔥🧦
The one where the email is already downloaded before you click it. Turns out the fastest way to fetch a message is to have fetched it earlier. Nobel committee, we're waiting.
Faster
New mail arrives with its body attached. When a message lands, Skim quietly pulls the body onto the fetch connection right away newest first, small ones only, three at a time. So tapping that notification is now a read from your own disk instead of a chat with the server.
The connection wakes up when you do. Alt-tab back to Skim and it logs in and re-selects your inbox in the background, so your click pays for a FETCH and nothing else. Costs precisely zero when everything's already warm.
Waking from sleep no longer confuses OAuth with itself. All connections now share one token cache, so two of them can't sprint for the same rotating Microsoft refresh token at the same time and both lose.
Token requests got a timeout and a reused HTTPS client. Previously a black-holed auth endpoint could hang around for as long as Windows kept the socket alive. Now it gets 15 seconds and then it's out.
Discovering a dead connection dropped from ~22 40s to ~4 6s. One tiny NOOP up front instead of twenty seconds of staring at "Loading ". Bold, we know.
Fixed
Skim v1.0.6 — "Bring Your Own Brain" 🧠
The one where Skim stops being picky about which AI you talk to, and the whole app goes on a diet at the same time. Efficiency and choice, in one download. You're welcome.
New
Bring your own AI endpoint (OpenAI-compatible). A third provider joins Anthropic and OpenRouter: point Skim at any OpenAI-compatible API Ollama, LM Studio, vLLM, your homelab gateway, whatever. Just a Base URL and a Model ID; the API key is optional (leave it blank for local servers that don't want one). No key = no Authorization header, so your Ollama box stops complaining. (Closes #12.)
AI chat that can actually breathe. The command palette (Ctrl+K) can now expand to near-fullscreen for reading long answers one toggle in the corner, only in chat mode. Repeat Ctrl+K flips it open/closed instead of nuking the palette. It remembers your preference, and text stays in a comfy ~760px reading column instead of sprawling edge-to-edge.
Fixed
Skim v1.0.13 — "Chat, unchained" 🪟
The one where the AI chat stops living inside a panel and becomes an actual window you can put wherever you like. Also the one where we deleted a setting instead of adding one.
New
Pop a chat out into its own window hit Ctrl+K in any AI chat (ask, palette, or the folder digest) and it flies off into a frameless window of its own. Drag it to the second monitor, keep reading mail underneath, and it keeps streaming the whole time.
Close it and it comes home Esc, , Alt+F4, whatever you press, the conversation slides back inline exactly where it was. No "session expired", no starting over.
Click a citation in a popped-out chat and the actual email opens in the main window which politely un-minimizes itself from the tray instead of making you go find it.
Open a second, third, fourth chat window they cascade instead of stacking pixel-perfectly on top of each other, which was funny exactly once.
Skim v1.0.21 — "Tick, tick, boom" ☑️💥
The one where you can finally deal with forty newsletters without clicking forty times. We know. We're as surprised as you are that it took until 1.0.21.
New
Bulk select hover a row, tick the box, and go. Shift+click grabs a range, X ticks the highlighted row, Ctrl+A takes everything loaded, Escape lets go.
Your keys already work on it. E, Del, !, V, U act on the selection when there is one, and on the highlighted row when there isn't. No new shortcuts to memorize, you're welcome.
The header quietly becomes the action bar instead of a second strip elbowing in. Same height, no rows covered, nothing new sitting there when you're not selecting anything.
Tick boxes stay invisible until they're wanted they fade in on hover, and their slot is always reserved, so nothing jumps around. Minimalism is a lifestyle.
"Select all" stops at what's actually loaded. Skim has no undo, and archiving 4,000 emails you never laid eyes on is not a feature, it's an incident.
