Readers can step through the entries of a list from the reader instead of
scrolling to the end of each entry or going back to the list.
- An up/down control floats at the bottom of the reader: liquid glass on
iOS 26+, a blur on older iOS and an elevated surface on Android, above
the floating player bar while it shows.
- Entries swap in place, so the screen pushed from the list keeps its
back gesture and the control stays under the thumb. Pulling up past the
end does the same instead of replacing the screen with a fade.
- The list hands the reader its loaded entries and loads its next page
when the reader nears the last one; the next entry's content loads
ahead.
- The control slides away with the header when "Hide Header While
Reading" is on, and while pulling up to the next entry.
- Going back shows the entry read last below the list header. FlashList
adds viewOffset to the scroll position, so the old +70 hid it.
There is no horizontal swipe: on iOS 26 swiping right anywhere in the
content goes back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server fetches RSSHub feeds from Folo's built-in instance first and uses the instance a user
chose only when that fails. The page says so, keeps the built-in one marked In Use, marks the
chosen one Fallback and offers "Use as Fallback" and "Use Only This" instead of "Use".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
POST /rsshub now refuses an address that is not http(s) or has credentials, a query or a
fragment, answering only "Unprocessable content". The form says what is wrong first, and still
saves an instance edited with the address it was stored with.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server now sends the approval link to the current address; the new one gets its link after
that. Desktop and mobile say so for verified accounts, and the store no longer shows the new
address before the change is done.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a device without a usable Play Store, for example with no Google
account signed in, the billing connection opens but every query fails.
The plan screen showed no purchase buttons without saying why, and
Restore Purchases surfaced the billing library's English error.
When Play products cannot load, the plan screen now explains that Google
Play is unavailable, and Restore Purchases and upgrades report the same
localized message when owned purchases cannot be read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Google Play found the Android app leading users to Stripe Checkout, which
its Payments policy forbids for apps it distributes. Android installs from
Google Play now buy, restore and manage subscriptions through Google Play
Billing only; the GitHub APK keeps Stripe, and iOS keeps the App Store.
- getMobileBillingProvider() picks the App Store, Google Play or Stripe.
An unknown installer counts as Google Play, so Stripe needs a known
non-Play installer.
- GooglePlayBillingProvider (expo-iap) loads the Play products listed in
PAYMENT_PLAN_LIST, buys the eligible offer (the free trial when Play
offers one, else the base plan) with the user's account token as the
obfuscated account ID, has the server verify and acknowledge the
purchase through /billing/google/verify, then acknowledges it locally.
Upgrades replace the current Play subscription with time proration.
Unacknowledged and pending purchases are synced once the store
connects, and Restore Purchases verifies every owned subscription.
- The plan screen shows Play prices and free trial length, opens the Play
subscription center to manage, and never offers Stripe or another
store's subscription on a Play install; a subscription bought
elsewhere shows a neutral hint instead.
- Only the provider for this install's store is mounted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mobile refused only spaces and invisible characters in a new or changed handle,
unlike desktop and the server's new rule. Both now take letters A-Z, digits and
underscores up to 36, keep an unchanged legacy handle saving, and map HANDLE_INVALID.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A save now sends the version of the rules it started from as expectedUpdatedAt,
so the server answers 409 instead of replacing another device's changes.
Desktop and mobile then offer to load the latest rules, keep editing, or save over them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Edit Profile refuses only spaces and invisible characters in a new handle,
as the server does, yet said "Use only letters, numbers and underscores."
from desktop's stricter check, although dots, dashes and CJK handles save.
It now says what it refuses, in every locale.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With no rules, Spotlight showed "Add rule" as gray text that did nothing,
and the footer repeated "Add rule" even when rules existed. The empty list
now has an "Add rule" button, and the footer says what Spotlight
highlights and which rule wins.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The English "Hide read" description quoted "Show unread only", while the
setting it refers to is shown title-cased as "Show Unread Only". The other
languages already quote their labels as shown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new Spotlight description said rules only color the entry list, but
they also color the text of an opened entry and AI summaries. The copy
now names all three in every language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The list delete warning ended in "!.." in English, Japanese and French.
The Thumbnail Ratio select sat 8px below its label because of a leftover
offset and a malformed class. In a narrow Feeds table the date column
kept its 150px and cut names to one letter; below 640px it now gives its
space to the names.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Spotlight tab was a lone "Add rule" button with no word on what a
rule does. It now says that rules highlight words or patterns in the
entry list and that the higher rule wins, and shows "No rules yet."
On the web, the empty Notifications list now says to add an Action with
"Notification of New Entry", reload and allow browser notifications.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Screen readers met nameless controls in Preferences: the AI shortcut,
MCP and task edit buttons and switches, Enable MCP Services and Enable
BYOK, the BYOK provider and email edit buttons, and two auth buttons both
called "Link". They now carry names such as "Edit shortcut", the item
name, "Change Email" and "Link Google".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Account, password and 2FA forms showed messages such as "Invalid string:
must match pattern /^\w+$/" or "Too small: expected string to have >=8
characters", in every language. Common text checks now say "This field
is required.", "Use at least 8 characters." and the like, translated,
and the handle and TOTP code get their own messages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Hide read" only hides read subscriptions while the unread-only filter is
on, so turning it on in the default view seemed to do nothing. Its
description now says so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The setting is the same remembered filter the timeline header button
switches, so a quick "Show all" in the header turned the "on launch"
preference off. It is now called "Show unread only" and says that the
header button changes it and that the choice is kept for the next launch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Saving a handle another user has failed with a bare "Internal Server
Error" toast, because the server lets the unique index reject it. After
a failed save the form now looks the handle up and, when someone else
has it, marks the Handle field with "This handle is already taken."
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For an unverified account the Change Email form said "Email changed." and
showed the new address, but the server only sends a link to it and keeps
the old email until that link is opened. The form now says the
verification email was sent, closes, and leaves the shown email alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
These forms showed zod's generic messages, such as "Invalid string: must
match pattern /^[a-z0-9_-]+$/" for an inbox handle with a space, and
"Too small: expected string to have >=1 characters" for an empty list
title, category name or Discover search. They now say what to enter, in
every language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Screen readers announced a bare "button" for the sidebar sort control and
its options, the category rename save and cancel, the follow "+" on
previewed feeds and lists, the Discover clear results X, the inbox copy
and edit buttons, and the list row and Manage Feeds actions. Each now has
a name, the sort trigger is a focusable button and the sort options say
which one is active.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A "Matches Regex" condition with a pattern that does not compile saved
with a success toast, and the rule then never matched. Show the error
under the input and refuse to save while a rule has such a pattern.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
whitespace-pre also turns off wrapping and wins over text-wrap in the
Tailwind 4 output, so long ask() messages such as the delete rule warning
were cut off mid-sentence. Use whitespace-pre-line, which keeps line breaks
and wraps, and put the unsaved actions prompt in sentence case.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When GET /actions failed on a fresh device, the Actions page showed the
empty state, and saving a new rule there replaced every rule on the server.
Show a load error with Retry instead, and make saveRules refuse to send
rules before the server's rules were loaded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The appearance settings shared the desktop strings, so iOS and Android
offered to show the unread count "in the dock icon" and described
special badges "in the sidebar". Use app icon and subscription list
wording on mobile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The accent color swatches and the custom color button were icon-only
buttons without a label or selected state, so screen readers announced
nine unnamed buttons. Each now has its color name and aria-pressed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every keystroke in the proxy field was sent to the main process, which
reset the session proxy and created a new undici agent for each partial
value, and an invalid value (such as "127.0.0.1:7890" without a scheme)
was silently dropped while the field kept showing it. The field now
applies on blur or Enter, treats a host and port without a scheme as an
HTTP proxy, shows an error when the main process rejects the value, and
then displays the proxy that was actually stored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The delete icon next to edit and pause removed the custom integration,
with its URL template and headers, at once and without undo. MCP
services and BYOK providers in the same settings ask first; custom
integrations now ask too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Enabling two-factor authentication only showed a QR code. An
authenticator on the same computer, such as a password manager, cannot
scan it, and there was no way to get the key. A "Copy setup key"
button under the code now copies the TOTP secret.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared Autocomplete always set aria-label "Select Category", which
was wrong for the feed search in the list settings. Callers now pass a
label that matches their purpose. The list search reuses
lists.select_feeds, whose zh-CN copy drops a stray period and zh-TW
copy uses the Taiwan word for "current".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing imports CustomIntegrationValidator, DiscoverForm (replaced by
UnifiedDiscoverForm), TimelinePromptSection (taken off the AI settings
page in 691307925) or AiOnboardingModal. URLSchemeHandler.getExamples,
the CustomIntegrationManager validate helpers, formatFileSize and
withDisplayStateHandler have no callers. Remove them along with the
locale keys only they used.
The aiTimelinePrompt setting stays because the server still reads it
when it scores the AI timeline.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the hard-coded English left in the desktop renderer, the Electron
main process, the mobile app and the shared packages to i18next, with
en, zh-CN, zh-TW, ja and fr-FR translations. SSR share pages are
unchanged.
- Fill every namespace in every language, delete keys no code reads
(server error codes stay) and update translations left behind by
later English changes.
- Stop HTML-escaping interpolated values in the desktop renderer.
React already escapes text, so names with & or quotes showed entities.
- Render error boundary fallbacks as components so they can use hooks.
- Localize zod validation messages with the app language.
- Throw ClipboardError from the clipboard helpers and show translated
toasts. formatEstimatedMins takes a formatter so callers localize the
units.
- Use one glossary term per concept in ja, fr-FR and zh-TW.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add every zh-TW key missing from settings and mobile, which fell back to
Simplified Chinese, and translate the values still in English: wallet
transaction labels, social link placeholders, and English words inside
translated strings (Proxy, Fetch, Authenticator, issue, token, App).
Brand names, acronyms and terms that zh-CN also keeps in English stay
as they are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Electron never loads locales at runtime: every language is bundled in
default-resource.electron.ts. The ai namespace of zh-CN and zh-TW still
pointed at the English placeholder added before those translations
existed, so the AI settings and chat panel stayed in English.
- Import the real ai locale files, and test the Electron map against
locales/ for every supported language and namespace.
- Skip the localStorage locale cache in Electron and start i18next in the
user's language. Only builds from before June 2025 wrote that cache and
nothing refreshes it, so it could only shadow the bundled resources.
- Translate the zh-TW plan settings titles.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- add a Reading settings screen (content font, line height, text color,
inline style, code theme) reachable from Appearance and from the entry
"more" menu
- add a System / Light / Dark theme setting applied through
Appearance.setColorScheme so the reader WebView follows it as well
- optionally slide the entry header away while scrolling down and keep a
blurred status bar strip so the clock stays readable
- forward reader typography to the html renderer through a new
setReaderStyle bridge call
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On Linux, `Tray.destroy()` does not remove the icon from StatusNotifierItem
hosts (waybar, KDE Plasma), so toggling "Minimize to tray" off and on stacked a
dead icon every time. Keep the single Tray instance for the app's lifetime
instead; the window close handler reads `getTrayConfig()` live, so a tray icon
that outlives a disabled setting is inert.
The icon cannot be removed from a running process, so `setTrayConfig` reports
when a restart is needed and the renderer offers one via a new `app.relaunch`
IPC. That relaunch runs from `$APPIMAGE` where set (AppImage's `execPath` points
into a mount that is gone by then), drops `--start-in-tray` so it never comes
back hidden, and uses `app.quit()` so `before-quit` still persists the window
bounds and flushes cookies.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Replace text input with native dialog.showOpenDialog for vault path selection
- Add path validity detection with three-state UI (unselected/valid/invalid)
- Fix YAML frontmatter parsing failure with Chinese/special characters
- Fix ENOENT error when vault subdirectory doesn't exist
- Add AI summary (description) to Obsidian frontmatter export
- Add author fallback to feed title when entry author is empty
- Increase filename truncation limit from 20 to 80 characters for CJK titles
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>