Following from a Discover card, a preview or a profile opened a modal
titled "Feeds" or "Edit Feeds", built by joining two words, which also
gave "編集 フィード" in Japanese. It now uses whole phrases: Add Feed,
Edit Feed, Add List and Edit List.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The list follow and edit form let free users turn on Private Follow, and
the server then rejected the update with "Free user no permission". The
switch is now disabled for free users with the paid badge, as in the
feed form.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Discover's Trending tab showed an empty card when a language and view
had no trending feeds or the request failed, and a category search
without matches left a blank area under the search box. Trending now
says there are no trending feeds, or shows the error with a Retry
button, and the category search says no results were found.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Cmd+Z badge on the Undo button of the unfollow and mark-all-read
toasts kept the near-white gradient of the kbd style under white text,
so it was invisible in light theme. The badge now drops that gradient.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
List search results showed a Preview button that was always disabled,
because it only knew how to open a feed. It now opens the list's
timeline, which already previews a list the user does not follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Searching Discover for lists said "Found 2 feeds" or "No feeds found".
The summary now follows what the search looked for and says "Found 2
lists" or "No lists found".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preview with a required route parameter left empty only moved the focus,
because the form never rendered its errors and its inputs were not marked
invalid. Each parameter now shows "This parameter is required." and is
announced as invalid.
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>
The All view lists the feeds and lists of every view, but its sidebar
left out the Inbox section, which only the Articles view showed. Inboxes
now show in the All view too, where their items already open.
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 category of only spaces was saved as is and made a folder with no
name, and "Tech " made a second folder next to "Tech". The follow form
and the New Category dialog now trim the name; the dialog rejects an
empty one and the follow form treats it as no category, like renaming.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening /discover directly let the sidebar take focus right after the
search box autofocused. The blur validated the empty box, so it showed
"Too small: expected string to have >=1 characters" before any typing.
The sidebar now takes focus on mount only when nothing else has it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A broken OPML file only showed the server's error, which is
"Internal Server Error" or raw parser output. The toast now says the file
could not be read, with the server text below it. Error messages read
through getFetchErrorInfo also drop the request dump the SDK appends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With several feeds selected, Switch to Another View moved only the feed
that was right-clicked, unlike the other items of that menu. It now
moves the whole selection and hides only a view all of them are in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The toast used the feed's own title rather than the name shown in the
sidebar and read "<feed> have been unfollowed." It now uses the custom
title when there is one, with singular wording in English and French.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Undo sent the list id as a feed id too, and the server answered 404
"Feed not found", so the list stayed unfollowed without any message.
Undo now sends a feed id only for feeds and shows an error toast when
the server rejects it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Undo followed the feed again with the feed's own title, so a feed the
user had renamed came back under its original name, and a feed without
a custom title got its current title pinned as one. Undo now sends the
title the subscription had.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Escape in the Category field of the Add and Edit Feed forms closed the
whole modal and dropped the typed title and category, because Radix
handles Escape on the document first. The modal now stays open while a
combobox shows its suggestions, and the next Escape closes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Any http(s) address opened the feed preview directly, so a site's home
page such as https://sspai.com always ended in "Error in fetching feed."
Web addresses now go through discovery and list the site's feeds; an
address that is itself a feed still opens its preview.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The export filename joined the UTC date to the local time, so for some
hours of the day it carried the wrong day next to the local time. Build
both from local time, and use the folo- prefix instead of follow-.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The list card footer printed a hard-coded English "subscriber" or
"subscribers" in every language, with "n > 1" as the plural rule. Use the
pluralized feed.follower key that Discover uses for the same count.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The round header buttons kept their icon while loading, so a pending save
looked like a disabled Save. Show the spinner instead. A double click could
also start a second save before the pending state reached the button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The section stayed blank under its heading when there was nothing to show
or the request failed. Show an empty-state line, or an error with Retry.
Feeds without a site link and without a category were also dropped from
the list; group them by the domain of the feed URL.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without a local sync cursor, a failed subscriptions request left the
sidebar on the "Add more feeds" empty state, as if the user followed
nothing, and nothing retried it. Show an error with Retry instead, except
for guests, who cannot load subscriptions at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening an entry URL while signed out, for example right after logging out,
answered 401 and the entry pane said "Network Error" with a Retry that
could never succeed. Show a log-in prompt with a Login button for a 401.
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>
The panel's 700px minimum width won over its 95vw maximum, so in a window
narrower than about 740px the right side, close button included, was cut
off and could not be scrolled into view. Cap the minimum at 95% of the
viewport.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The X of stacked modals and of the shortcuts guide had no accessible name.
The stacked modal X also had tabIndex 1, so Tab jumped out of the dialog
and focus came back to the X, and Cancel and Confirm could not be reached
by tabbing forward.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GlassButton only showed its description as a tooltip, so Back, New Rule,
Save and the other icon buttons had no accessible name, and a dimmed Save
was still announced as enabled. Use the description as the aria-label and
let callers mark the button aria-disabled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every feed row got dnd-kit's draggable attributes, which say
aria-disabled=true while nothing is selected, so screen readers announced
each feed as unavailable. Only rows in the multi-selection, the ones that
can be dragged, get those attributes now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editor saves rules with an empty name and shows "Action N" for them,
but the importer rejected an empty name as missing, so such an export or
clipboard copy could not be imported again. Only reject a missing name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Below 768px the web app swaps its layout for the download page, which
unmounted the Actions editor and silently discarded unsaved edits. Discard
them when the user confirms leaving instead of on unmount.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The catch-all route renders outside the app, so on a direct load it
mounted before i18n was set up and showed raw keys such as
errors_page.not_found.description. Render it once the app is ready.
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>
Validation failures all carry code 3, so the toast only said "Unprocessable
content" and dropped the server's message that names the field, such as an
invalid webhook URL in an action rule. Show that message as the description,
without the request context the SDK appends.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since the regex help wrapper was added, the operator select took the whole
condition row and the text value input shrank to about 26px. Give the
wrapper the same flex sizing the bare input had.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cancelling the prompt left the router blocker blocked, so Back, Escape and
sidebar clicks on the Actions page did nothing and never asked again.
Reset the blocker when the prompt is cancelled or dismissed.
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 /settings routes only describe the settings tabs and their layout
rendered nothing, so a typed or bookmarked settings URL on the web app
was an empty white page. Go to the main view and open the requested tab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Android has no sheet swipe to cancel, and its hardware back button
dismissed a dirty follow, list or RSSHub form at once. Modal groups now
record when their form has unsaved changes, and the back handler asks
with the same prompt the header button and the iOS swipe use, which now
share one helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
i18next answers a missing key with the key minus its namespace, so the
"t(key) === key" check never matched and an untranslated API code was
shown as a bare number. SDK errors with codes like AUTH_REQUIRED became
"NaN", which is what a signed-out mobile timeline showed. Compare
against the bare code and skip codes that are not numbers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The selection screen inserts the recommended feeds in its own mount
effect, which ran before the selected-feeds reader subscribed under
jotai 3, so the list stayed on "No feeds selected yet" while finishing
still followed every hidden feed. Read the selection with
useAtomValueRawSync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The subscription column focuses its sidebar in a mount effect, which
writes the focus scope in the same commit that mounts the timeline
header. jotai 3 subscribes after that commit, so the header kept the
empty scope and never bound Shift+Cmd+A until focus moved elsewhere.
Read focus scopes with useAtomValueRawSync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The user provider writes the session user in an effect of the same
commit that mounts the login page's avatar, and jotai 3 subscribes
after that commit, so the avatar kept reading no user and the signed-in
view never showed who was signed in. Read whoami with
useAtomValueRawSync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editor painted the initials avatar with the system background to
stand out from the grouped background in light mode, which made it
black on black in dark mode. The fallback avatar now has a fill that
shows on both, so drop the override.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The email text did not shrink, so a long address ran past the card edge
with letters clipped and pushed the verification badge and the chevron
off screen. Let it shrink and truncate in the middle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A timeline with nothing in it showed the search message "No results
found." although nothing was searched. Show "No entries yet" instead.
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 initials fallback filled its circle with secondary system background,
which is the same color as the grouped background of the Subscriptions
tab, so the header avatar showed bare letters there. Use a translucent
fill that stands out on every background.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upserting a feed kept the stored subscriber count, updates per week and
latest entry date whenever one was set, in memory and in SQLite, so the
follow screen and search cards kept showing the first count ever seen.
Keep the stored values only when the incoming feed has none, as feeds
from subscriptions carry no analytics.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>