Repository navigation
Releases
HardlyReader v0.6.40
Added
- Favourites, said in words. The star's state is a favourite everywhere a
person reads it: the filter pair, the post list, the article bar, the viewer,
the theme panel and the account control. Nothing migrated — the server's
…/state/com.google/starred, the stream ids andisStarred()keep their
names, because they are identifiers, not sentences. - Messages from the author, in Settings. A bell with a dot when something
arrived, and a section that lists them; one request when the sheet opens, no
timer, no push, and a failure says what failed rather than drawing an empty
inbox. The two reads aresecurity definerfunctions keyed onauth.uid()
(supabase/app-messages.sql), so targeting and read state are the server's
decision, and a message for one app never appears in the other.
Changed
- The filters are two switches, not four words. Unread and Favourites are
independent and combine: pressing one of a pair shows that half, pressing it
again shows everything, and an axis that is showing everything wears a slash.
The buttons are icons — a circle, a circle with a tick, a star filled and
hollow — because the row already speaks that language. A fresh install opens
on unread only; the empty list offers "Show what you have read" in one press. - The article bar's read button is the list's own mark. A hollow ring until
the post is read, a tick inside it after, instead of the two text characters
the bar used to use. - The scroll past the end of a post gets heavier as it fills. The pull is
1 − (1 − u)²on the travel: a 100px notch is worth 72px of pull on the first
one and 8px on the last, the first quarter of the line costs 13% of the travel
and the last quarter half of it. The total is unchanged — 728px at ×1 — so it
is the distribution that moved, not the distance. - The page slides away to reveal the post it is scrolling towards. From 80%
of the pull the open post lifts and the next one's feed and headline show in
the strip underneath. This existed for a thumb only; a wheel had none of it and
swapped the post with nothing moving. "Reveal next" is on by default now —
saved under a new key, so an install that has ever written a preference is not
stuck on the old silent default. - The fit button grows the window by what the card is short of. Height only:
a wider pane draws a card's picture taller, so the pane keeps its width, the
window only grows, and never past the screen. It used to resize the whole
window's width on every press — 299px for a page, 1133px for a banner — which
is what "overly expands the window" was. - A read card dims harder than a read row (0.32 against 0.48, the centred
one 0.6), because a card is mostly picture and a picture at 0.48 still looks
like a picture.
Fixed
- A card lands where the pane's middle falls inside it, not at the nearest
centre, which with unequal heights could round backwards; and the card is
re-placed once its picture supplies its real height, standing down the moment
the reader touches the column. - A starred card is identifiable in Cards. Its mat is the star's gold at 44%
instead of the accent's 28%, with a 3px band of that gold along the card's
bottom edge, so a feed of read comics has a way to show which ones are
favourites.
HardlyReader v0.6.39
HardlyReader 0.6.39
Pure preview build: the iPhone IPA is unsigned and needs sideloading, and the Windows and macOS bundles are unsigned too.
Added
- Windows and macOS builds on the release page. An
.exeinstaller (and an
.msi) for 64-bit Windows, and one universal.dmgfor Apple Silicon and
Intel Macs. Both are unsigned: Windows shows a SmartScreen prompt until you
allow it, and macOS needs Right-click → Open the first time. The macOS app is
ad-hoc signed, which is what lets an arm64 build start at all.
Changed
- The feed indent lives in
Settings → Feedsand nowhere else. The strip
under the sidebar's controls could put itself away with its own label and never
come back; the slider is the sheet's row on every device now, and the
preference that gated the strip is gone rather than left behind. - A regular row is one height, whatever the post has to say. The title and
the preview each hold two lines' worth of space whether or not their text fills
them, and the byline keeps the row's floor, so a column of rows reads as a
column instead of a ragged edge. The height follows List size — 84px at 80%,
103 at 100%, 142 at 140%. An opened post is exempt: its article is the row.
0.6.38 — Settings says where the project lives, and two panes fill the frame
[0.6.38] — 2026-11-05
Added
- Settings → About has a Project row, opening
github.com/hardlysleeping/HardlyReader. The
screen that says what this app is should also say where to read about it.
Fixed
- Swapping three panes down to two fills the frame again. Pressing 2 asks the
window to shrink by the article's width, and the panes were drawn at the width they
had asked for even after the window said no — a maximised window, a window manager
that ignores the request, or the moment before the frame moves. A two-pane layout
then sat against the left edge of a wider window, with an empty band on the right
and a divider that measured from a width the window never had. The frame now
redraws the moment the press gives up on that width.
The iPhone build is unsigned — sideload it. This release carries all four bundles: AppImage, Flatpak, APK, IPA.
HardlyReader v0.6.37 (preview)
HardlyReader 0.6.37
Pure preview build: the iPhone IPA is unsigned and needs sideloading.
Added
- The wallpaper can be hidden while the reader is locked. Appearance →
Background → When locked: Hide swaps the picture for the page's own
colour until the lock opens, because a photograph says more about the person
holding the phone than the list in front of it does. Off by default, and it
stays on the device like the picture itself. - Bloom. The picture's bright parts glow a little, from one extra layer
blended withscreen— nothing over black, most of its value over white, so
the highlights lift rather than the frame. A slider beside Blur and Dim, 0–100,
default 35. - The phone's top names the service it syncs with — FreshRSS, Miniflux, or
the server's host — instead of the app's own name, and pressing it opens the
accounts on this device: switch, add another, sign out.
Changed
- Buzz's topics are eight that name a story: Money and Design are gone, Film
is Film/TV and covers television, and AI is a word inside Tech rather than a
section beside it. Stock replaces Money, with the market's own words. - A category row folds on the first press, in every layout, with nothing
selected first; the old select-then-open-then-fold ladder is gone. A double tap
on the name still opens the category's posts — and everything left of the name
only ever folds, even when tapped twice, so a thumb reaching for the arrow
cannot open a category by accident. - The feed you opened stays at the top, with the post list's bar directly
under it and the posts scrolling beneath: the row pins for exactly as long as
its own posts are on screen. - Folded, the sidebar's control band is one hairline across the whole pane —
16px, edge to edge, and closer to the search row — instead of a 20px strip
stopping 11px short of the glass. - A wallpaper at blur 0 is drawn sharply.
blur(0px)still made the picture
its own composited layer, which a phone rasterises coarse and scales; the
filter is now absent at zero. The stored picture is also kept at 2600px rather
than 1800 — a tall phone'scoverwas stretching it 2.1×.
HardlyReader v0.6.27 (preview)
iOS preview, unsigned — sideload with your own tooling.
Accounts
- A second account gets its own foldable band in the sidebar, with that account's
categories and feeds under it. One account draws no band at all. - With more than one account signed in, the reading list and starred list draw an
account heading over each account's own months and weeks. A row's mark goes back
to the account it came from. - A row's identity carries its account, so the same feed subscribed on two servers
is two rows and neither can take the other's read/star mark. - Sync walks the accounts one at a time; an account that does not answer is named in
the sync readout instead of blanking the list. - A library kept on this device is never merged into a server's reading list unless
you turn it on. - An account only appears as a row when it actually holds a feed: one account plus an
empty device library looks exactly like the app always did.
Fix
- Accounts saved on the device are all restored on launch; before this a restart
knew only the account it resumed, so the account rows never appeared again.
Layout
- The top bar's controls start below the island instead of inside it.
- On its side the notch is paid by whichever column actually reaches the rim, and the
status bar is hidden by the native half rather than by CSS.
This build is the same code as 0.6.27's IPA: dist-ipa/HardlyReader-0.6.27-ios-unsigned.ipa
(4,727,114 bytes, sha256 a0f5cbb8…45f4).
HardlyReader v0.6.26 (preview)
Preview build. 0.6.26 is the current working line: all four bundles are cut from the same commit as the app in the tree. The iOS IPA is unsigned and needs sideloading.
Added
- Buzz. A bolt at the top of the sidebar finds the stories several of your
feeds are telling at once: one engine, ranked coverage-first, over the last 48
hours. Topics are a name plus the words a reader gives them — the articles pane
becomes the topic's stories, and the topic rows stay collapsed with their feeds'
icons until asked. - A category can wear a picture, guessed from its name when it has none, and
a wrong guess gets a way to be corrected: the category menu's Icon… opens the
picker anchored at the press, with the automatic guess as one of the choices. - A phone can swipe to the next article. Past the end of an article the swipe
fills the same line the wheel fills, with the same resistance and the same
"scroll effort" setting, and releasing at a full line loads the next article.
Changed
-
An unfolded feed stays put. On a phone, opening a feed now scrolls its row
to the top of the tree and pins it there: the feed's name and the four views
under it stay against the top while its posts scroll beneath, instead of
carrying the view options away with them. Measured on the built stylesheet with
the tree's real classes — at scrollTop 200 the row sits at 0 and the views bar
at the row's own height. -
A feed opens at its first post. Tapping a second feed closed the first,
which removed its posts from above and left the scroll at the end of the feed
you had just asked for; the row is now pinned (and the scroll set to it) after
the new posts arrive. -
Feed indent moved into the Aa panel. It was a phone's Settings → Feeds
row, two doors from the list it moves; it is now the first row of the Aa
panel, above Text size, on the slider the sidebar already mounts. Same store,
same write, one slider in three frames rather than a second setting. -
"Pictures" is "Thumbnails" in that panel, and so is everything inside it —
the group, the position, the size and both stepper labels. "Pictures above
text" now reads as thumbnails too. -
A grid tile keeps its picture whole. Star and read were a floating pill in
the tile's top-right corner, drawn over the one part of a square tile the eye
goes to first. They are now the last line of the tile, under the words, in
normal flow; a thumb gets a 34px target. -
A phone's title bar names where you are in 15px, not 12.5px. The title is
the one thing in that bar that changes meaning as you move, and at 12.5 it read
like a caption on the back arrow.
Bundles
hardlyreader-0.6.26-linux-x86_64.AppImage— Linux desktop, executablehardlyreader-0.6.26-x86_64.flatpak— Linux desktop,flatpak install --user ./hardlyreader-0.6.26-x86_64.flatpakhardlyreader-0.6.26-android.apk— Android, versionCode 6026, signed with the release keyhardlyreader-0.6.26-ios.ipa— iOS, unsigned,CFBundleShortVersionString0.6.26
0.6.25
HardlyReader 0.6.25
The first post in a list no longer starts under the header's rule.
The column was flush at the top — only the foot of the list kept any air — so
the first row's accent hairline, its turn and the dark band inside it were drawn
right against the line above, and it read as clipped.
Every list now keeps the same 12px above the first post that it already kept
below the last. Measured on the built stylesheet: the gap between the list's top
edge and the first row was 0.0px and is now 12.0px, with the foot unchanged.
Rows, Tree, Grid and the one-line view are all inset by it. Cards is untouched:
a card is the pane, it is centred in it, and it never touches the top. The
shadows are unchanged.
149/149 suites, svelte-check 0 errors, tsc clean.
HardlyReader 0.6.24
A starred post keeps its gold once it has been read.
The bug. Starring an article in the FreshRSS web UI looked like a different
thing from starring it in the app: in the Starred list, some rows were gold and
some had no gold at all. Where the star was made never mattered — the app has
always read the star off the article's own state, so a star set in the server UI
arrives exactly like one set in the app.
What actually differed was whether the post had been read. The gold edge on a
row was drawn only for an unread post, and a read post is dimmed to 48%, which
turns the star's gold into mustard. So a post read in the morning and starred in
the afternoon had no gold edge and a muddy star — which reads as "not starred".
The fix. The edge now belongs to starred or unread rows. The wash stays the
unread language, so a row can still say "new", "starred", or both — a starred read
post is a gold edge with no wash. And a starred read row now dims to 62% instead of
48%, because at 48% the one colour that says "starred" stops being that colour.
Measured in the built stylesheet through Chromium, before and after, on the same
four rows:
starred, unread edge 0px→gold, wash yes opacity 1.00 → 1.00
starred, read edge 0px → gold, wash no opacity 0.48 → 0.62
plain, unread edge blue, wash yes opacity 1.00 → 1.00
plain, read edge 0px opacity 0.48 → 0.48
Nothing else moved: the four list views, cards, the grid, the compact list, the
turn shapes and their directions, and the row actions behave as they did. A new
suite (starred-rows, 11 checks) guards the three promises, and the five
stylesheet contracts that named the old unread-only selector were rewritten to the
new one rather than deleted.
149/149 suites, tsc clean, svelte-check 0 errors.
The iOS build is unsigned — sign it on the device with your own Apple ID.
HardlyReader 0.6.23
A phone's reading options are now a sheet, and its top bar stops touching the clock.
A phone's reading options are a sheet, not a popover in the corner. The Aa
panel used to open as a 252px card in the top corner, over the article you were
reading, with no handle and no gesture back. On a phone it now rises from the
bottom edge: the grabber owns the drag, letting go past a third of its height or
flicking it closes, and pulling up gives 28px of elastic and stops because there
is nothing above it. Windows keep the popover exactly as it was.
The phone's top bar clears the status bar by six pixels instead of touching
it. The bar rode above the raw inset by a hard 24, so on a 62pt inset its top
edge landed on the clock's last row and the status bar clipped its hairline. The
number is now --status-lift, declared once, and the header's padding, its
height and the Aa panel's anchor all read it — 18, not 24.
Both were measured with real touch events and a forced inset rather than
reasoned about: 390×844 for the sheet (flush to the bottom, 1:1 drag, 220px
closes, 90px flick closes, up stops at −28px), and portrait at a forced 62pt
inset for the bar (padding-top 44px where it was 38, height 80 where it was 74;
nothing moves at inset 0 or in landscape). Desktop at 1440×900 is unchanged.
The iOS build is unsigned — sign it on the device with your own Apple ID.
HardlyReader 0.6.18
Four things that were quietly wrong, and the work behind them.
The lock folded the band by writing the reader's own preference, so one lock left
the band shut for every launch after it. The fold is the lock's now — remembered,
not stored — and unlocking gives back the fold you had.
The launch crawl fetched new posts and then repainted from a stream cache a few
seconds old, which is why a fresh load could look like it had grabbed nothing.
It forces the stream it repaints, the way a manual refresh already did.
A pin says where its mark lives. With the categories extension answering it is
the extension's; without it the mark is kept on this device, and the band and the
row menu both say so instead of only naming what is missing. A passcode kept on
this device says that too.
Pictures got a policy rather than a hope: a scheme rule for what may be cached
(deliberately not same-origin, so a self-hosted server still caches), admission
that prunes to the ceiling before a write instead of after, a size rule that
never upscales and never re-encodes a rounding error, and thumbnails warmed
before full pictures in reading order.