Releases: PathGao/Nifro
Release list
v0.9.0
This build is self-signed and not notarized by Apple, so a disk image you download and open yourself will be turned away the first time with "Apple could not verify Nifro is free of malware". That dialog offers only Move to Trash and Cancel — the way through is somewhere else:
- Press Cancel. Do not move it to the trash.
- Open System Settings → Privacy & Security and scroll to the bottom.
- There is a line naming Nifro, with an Open Anyway button. Press it, and confirm with your password or Touch ID.
- Press Open in the last dialog. macOS remembers, and never asks again.
Installing with Homebrew skips all of that — the cask clears the mark for you.
Install:
brew tap PathGao/tap https://github.com/PathGao/Nifro
brew trust --cask PathGao/tap/nifro
brew install --cask nifro
The middle line is not optional. Homebrew refuses to load a cask from outside its own
repositories until you say you trust it, because a cask can run code after installing.
This one runs a single command — it clears the "downloaded from the internet" mark macOS
puts on the app. Read it first: https://github.com/PathGao/Nifro/blob/main/Casks/nifro.rb
Apple silicon Nifro-arm64.dmg sha256 35c69372ab268208d38eaf1ac679cd9e951d56d2e9b69be00cd05c64c1a27039
Intel Nifro-x86_64.dmg sha256 c4d391242093d452622af262210284decdb251f207acffd2dfbd3408850fc5a2
What's Changed
- cask: bump to 0.1.3 by @github-actions[bot] in #11
- Check the roadmap against the code it describes by @PathGao in #12
- A catalogue entry with a zoom no longer costs the whole live gallery by @PathGao in #13
- A display that is gone stops getting a wallpaper of its own by @PathGao in #14
- A wallpaper can go away with its display, and three pieces of state get applied by @PathGao in #15
- "Main" meant two different displays, and one of them was frozen by @PathGao in #17
- Tell people a new version exists, on terms they set by @PathGao in #18
- Say that a page is on its way by @PathGao in #20
- A language picker, and the dead multi-display link goes by @PathGao in #19
- A panel with one column per display, replacing the menu by @PathGao in #21
- Compress the roadmap, settle four things it contradicted itself about, and translate it by @PathGao in #22
- The roadmap describes the app that shipped, not the one that was planned by @PathGao in #23
- The shortcuts footer says the one thing a reader of that page needs by @PathGao in #24
- The About tab leads with the gallery, and the feedback button stops being the one that gets cut by @PathGao in #25
- Clearing website data lives in the window that administers websites by @PathGao in #26
- Each display keeps its own rotation interval, next to the mode it qualifies by @PathGao in #27
- Count the app's visual vocabulary, and propose the guardrail that would keep it one by @PathGao in #28
- A hardcoded radius, point size or colour in the panel now fails the build by @PathGao in #29
- Clearing website data asks first, and says what it costs by @PathGao in #30
- Off means off, loading says so, and a new install opens with a wallpaper by @PathGao in #31
- One display's answer stops being the whole app's by @PathGao in #32
- The wallpaper from an unplugged display takes the screen it lands on by @PathGao in #33
- Settings is four panes about four different questions by @PathGao in #34
- Roadmap: carrying a user's settings across an upgrade by @PathGao in #35
- A download the wallpaper starts is not one anybody asked for by @PathGao in #36
- Deleting a website says what it costs by @PathGao in #37
- The unplug setting names both of its states by @PathGao in #38
- Nothing in the app is Plash's any more by @PathGao in #39
- Every explanation is one sentence by @PathGao in #40
- Custom CSS keeps its non-ASCII characters by @PathGao in #41
- A page with no video on it is not scanned sixty times a second by @PathGao in #42
- The favicon fallback asks the site, not the folder by @PathGao in #43
- A half-written thumbnail is forever by @PathGao in #44
- The inspector is switched on through public API by @PathGao in #45
- Zoom Out stops before the page is gone by @PathGao in #46
- Content blocking says what became of the address by @PathGao in #47
- The style observer watches the two nodes it says it watches by @PathGao in #48
- The user agent is decided in one place by @PathGao in #49
- Google no longer gets the one user agent nobody else does by @PathGao in #50
- One rule between Zoom Out and Inspect Element, not two by @PathGao in #51
- The URL field names the placeholders it accepts by @PathGao in #52
- The update check, each website's links, and the strings the menu left by @PathGao in #53
- The menu bar band stops re-encoding itself, and the timers admit some slack by @PathGao in #54
- A page title, a thumbnail cache and what they each cost by @PathGao in #55
- Three hand-rolled mechanisms give way to the API that was already there by @PathGao in #56
- The panel reads the list once, and a region reads the same in both places by @PathGao in #57
- What the menu left behind, and the scan that could not see it by @PathGao in #58
- A website list that survives the update, and the documents that describe what shipped by @PathGao in #59
- Per-display facts stop being kept in app-wide slots by @PathGao in #61
- "Playlist" stops meaning rotation by @PathGao in #62
- Key what a page remembers by website, not by address by @PathGao in #63
- Websites become playlists, and a display picks one by @PathGao in #64
- The swap load can see the flip it is waiting for by @PathGao in #65
- The reload switch joins its own row, and the rule field says what it is for by @PathGao in #71
- One key for Browsing Mode, and how long you hold it says which one you meant by @PathGao in #72
- The panel's controls are the system's again by @PathGao in #73
- A column says what its own display is doing by @PathGao in #66
- Framing ends when the overlay leaves the window, whoever took it by @PathGao in #67
- Advanced settings explains its two quietest rows, and offers the way back last by @PathGao in #69
- The roadmap describes the tree after #66 to #69 by @PathGao in #70
- The page's layout size is read off the window that imposes it by @PathGao in #68
- main does not build: the failure line asks for a colour #73 removed by @PathGao in #74
- Thumbnails of websites that are gone are collected like their stores by @PathGao in #75
- The panel photographs six times a second, and stops when nobody can see it by @PathGao in #76
- The Settings reload interval reaches a page, and a timed reload keeps its process by @PathGao in #77
- Two catalogue entries nothing shows, and the check that worked around them by @PathGao in #79
- Two decisions the roadmap did not have: sync has a route, E22 does not by @PathGao in #81
- The roadmap catches up with the four fixes that landed without it by @PathGao in #82
- The panel is a chooser, and two W entries were never owed by @PathGao in #83
- Six lines nothing could reach, and the comment that would have put one back by @PathGao in #84
- Three places doing the same work twice by @PathGao in #85
- An icon fetch cannot strand its web view any more by @PathGao in #86
- Four things nothing could reach, and a retention that argued for the wrong one by @PathGao in https://github.com/Pa...
v0.1.3
This build is self-signed and not notarized by Apple, so a disk image you download and open yourself will be turned away the first time with "Apple could not verify Nifro is free of malware". That dialog offers only Move to Trash and Cancel — the way through is somewhere else:
- Press Cancel. Do not move it to the trash.
- Open System Settings → Privacy & Security and scroll to the bottom.
- There is a line naming Nifro, with an Open Anyway button. Press it, and confirm with your password or Touch ID.
- Press Open in the last dialog. macOS remembers, and never asks again.
Installing with Homebrew skips all of that — the cask clears the mark for you.
Install:
brew tap PathGao/tap https://github.com/PathGao/Nifro
brew trust --cask PathGao/tap/nifro
brew install --cask nifro
The middle line is not optional. Homebrew refuses to load a cask from outside its own
repositories until you say you trust it, because a cask can run code after installing.
This one runs a single command — it clears the "downloaded from the internet" mark macOS
puts on the app. Read it first: https://github.com/PathGao/Nifro/blob/main/Casks/nifro.rb
Apple silicon Nifro-arm64.dmg sha256 e1d460098a3b18a401f12cbb1f8c9a74b24d2c2af9d3e1b59fab88324c1705d4
Intel Nifro-x86_64.dmg sha256 5b3b92a452ec6f613d7ac6f2767ed5c04572b13e304efd61d144011507f4612b
What's Changed
- cask: bump to 0.1.2 by @github-actions[bot] in #8
- docs: add virtual-window video sources by @PathGao in #9
- Each website gets its own data store by @PathGao in #10
Full Changelog: v0.1.2...v0.1.3
v0.1.2
Nothing in the app changes. If you are on 0.1.1 you are already running this.
This release exists to prove one step. The pipeline that publishes it now creates its own tag, at the
end, only once everything before it has passed — and that is the one step a dry run cannot exercise,
because skipping it is what makes a dry run dry. It is also the step that failed on v0.1.1, after the
tag had already been pushed and frozen. So the first thing put through it is a release that changes
nothing, where failing costs nothing.
Why the order changed
Pushing a v* tag used to start the build. That made one action mean two things: the request to
release, and the promise that a release exists. The promise landed first, on a ref that cannot be
moved or deleted.
v0.1.1 spent one on a run that died at its last step — the build, the signing and the packaging had
all passed, and a shell syntax error killed it there. The tag was frozen at a tree whose own pipeline
could not parse, so it could be neither moved onto the fix nor deleted, and the release had to be
finished from a branch instead.
before push tag ─→ build ─→ sign ─→ package ─→ Release
└── promise made before anything was checked
after dispatch ─→ build ─→ sign ─→ package ─→ tag + Release
└── created together, at the end
A failed run now leaves no tag, no Release and no spent version number.
What else came out of that
- A dry run. The whole pipeline — both architectures built, signed, packaged, checksummed —
stopping before the tag. - No run had ever produced an artifact. Everything this workflow uploads lives under
.release,
and a leading dot makes it hidden, which the upload action skips by default while still reporting
success. The build log the handbook tells you to open when a release fails had never existed. The
first dry run found it. - CI parses every workflow's shell. A
run:block is only checked when it runs, and nothing but
a release runs the release workflow, so an unterminated quote sat behind every green CI run until a
real release found it.
The documentation describes an app that exists
A large power feature — two rendering backends, occlusion measurement, automatic still detection —
was built and then removed, 811 lines. The code went; the descriptions of it did not. The two lead
bullets of the README described measuring how much of the wallpaper is visible and photographing
pages that hold still. Neither had existed for some time. The same claim about test coverage was
written in six files.
Also corrected, each because it sent somebody somewhere wrong:
- Installing needs three commands, not two. The cask has a
postflightblock, so Homebrew
refuses to load it untrusted. The install block on the v0.1.1 page was missingbrew trust— anyone
following it hit a refusal. It is on this page, and in the cask's own comments. - The framing interaction was rebuilt in 0.1.1 and the README still said "drag a rectangle over the
wallpaper". - Twelve entries in the upstream-issue triage were written as pending plans for work that has
shipped, including a page per display and the playlist. - 25 strings no code referenced, 13 of them for the deleted feature, were still in the app's
localization catalogue. - Four issue templates asked reporters to paste a line the app has never emitted.
Known issues
Unchanged from 0.1.1, and tracked in
docs/ROADMAP.md:
- A YouTube video cannot be shrunk back into the YouTube page, so there is no way to sign in.
Bilibili does not have this problem. - Switching website can take a few seconds, with no sign that anything is happening.
- Nothing is done yet to reduce what the app costs while nobody is looking at it.
- The app follows the system language; there is no picker inside it.
- The app cannot tell you a new version exists.
brew upgrade— or this page — is how you find out. - Nothing anywhere handles HDR.
- Multiple displays has never been run on two.
Requires macOS 15 or later.
Install
brew tap PathGao/tap https://github.com/PathGao/Nifro
brew trust --cask PathGao/tap/nifro
brew install --cask nifro
The middle line is not optional. Homebrew refuses to load a cask from outside its own
repositories until you say you trust it, because a cask can run code after installing.
This one runs a single command — it clears the "downloaded from the internet" mark macOS
puts on the app. Read it first: https://github.com/PathGao/Nifro/blob/main/Casks/nifro.rb
Already installed with Homebrew: brew upgrade --cask nifro.
Or download the disk image
Nifro-arm64.dmg on Apple silicon, Nifro-x86_64.dmg on Intel. There is no universal binary, so
nobody downloads the half they cannot run.
This build is self-signed and not notarized by Apple, so a disk image you download and open yourself will be turned away the first time with "Apple could not verify Nifro is free of malware". That dialog offers only Move to Trash and Cancel — the way through is somewhere else:
- Press Cancel. Do not move it to the trash.
- Open System Settings → Privacy & Security and scroll to the bottom.
- There is a line naming Nifro, with an Open Anyway button. Press it, and confirm with your password or Touch ID.
- Press Open in the last dialog. macOS remembers, and never asks again.
Installing with Homebrew skips all of that — the cask clears the mark for you.
Checksums
Apple silicon Nifro-arm64.dmg sha256 79dad42713e221270d400a1c76b3908455e0b889a9eb4bc135557e1827466b6e
Intel Nifro-x86_64.dmg sha256 049e58d99b46c15ee516de53d2616369386a2f50aa6607d7d48af4c019faa23a
What's Changed
- The release step could not be parsed, so v0.1.1 has no Release by @PathGao in #4
- cask: bump to 0.1.1 by @github-actions[bot] in #5
- The tag is created by the release that succeeded, not by the request to start one by @PathGao in #6
- The documentation describes an app that exists by @PathGao in #7
New Contributors
- @github-actions[bot] made their first contribution in #5
Full Changelog: v0.1.1...v0.1.2
v0.1.1
Framing a region is now something you do by moving the wallpaper, the page comes back where you left
it, and each display keeps its own website. Settings and website lists carry over; the signing
identity is unchanged, so a local folder you have already granted access to keeps working.
Choose a region
Rebuilt. It was drawing a rectangle over the wallpaper, which meant aiming at the thing you were
framing instead of looking at the result, no way to adjust a region once it existed, and a frame that
came back a point out from where it was drawn.
- The wallpaper is the thing you move now. Drag or two-finger scroll to move it, pinch or scroll-wheel to
zoom. What you see while framing is what you get, because it is the same view. - It starts from the region the website already has, so this adjusts as well as creates.
- The page holds still and the frame moves, not the other way round — a page that pans itself has two
interpretations otherwise. - Framing no longer reloads the page it is framing, so a panned map or a scrolled dashboard survives
being framed. - The region survives however you leave the mode, including quitting.
- Spreading two fingers makes the frame bigger. It used to do the opposite.
Where a page was
- A page that keeps its position in the address is put back there. floor796 writes
#t0r0,444,443on every move and that was thrown away on every load. Only the part after the#
is remembered, and only when everything before it matches the website's own address. - The scroll position was recorded once at launch and then never again, because the thing watching
the address was watching a web view that had been replaced. It is re-attached now.
Two displays
None of this was reachable on a single screen, which is why all of it shipped in 0.1.0.
- Each screen keeps its own current website. One screen's rotation used to clear the other's mark,
so that screen fell back to the first website in its list and never advanced again. - Next, Previous, Random, Sound, Choose Region and Show on acted on whichever screen last held
that mark, with nothing saying which. They act on the screen Settings points at. - Each screen's page runs its own custom CSS and JavaScript, and its own inverted-colours, print
styles and sound settings. The second display's page was built from the first display's. - Changing one screen's website no longer throws away and reloads the page on every other screen.
Still unverified on real hardware — this was all written and tested on a one-display machine, and
docs/ROADMAP.md §5.6 lists what a second display would confirm.
The wallpaper appearing
- The page goes on screen when it has loaded, not one second after being asked for. The old
one-second guess was measurably short, so the menu bar band went up wearing a colour sampled from a
page that had not arrived — the menu bar changed, and the wallpaper followed a second or two later. - With a region framed, the menu bar now takes its colour from the strip you actually see. It was
sampling the top of the whole page, which with a magnified region is usually not on screen at all.
Menu
- The website's name is a real item with a status dot — green while the wallpaper is showing, hollow
while it is not — sitting directly under the switch that turns it on and off. It was a greyed-out
label, and grey reads as "you cannot have this" where the point is "this is the one you have".
Clicking it opens that website's settings, replacing the separate Edit… four items below its own
subject.
Clearing data
- "Clear all website data" left 315 MB behind. Measured after a clear:
Caches/WebKit/NetworkCachestill held 1158 files, none of them rewritten since. The API it used
only reaches what WebKit can attribute to an origin, and the disk cache mostly is not attributable.
It clears by date now. - The button says what it freed, and "Nothing left to clear" when that is zero rather than "0 bytes".
- It now also removes the zoom level remembered for each page. Two of the three per-page records
were being cleared and the third was not. This is a behaviour change: a page you had zoomed in
comes back at its own size after a clear.
Smaller things
nifro://reloadworks. Onlynifro:reloaddid — with the slashes the command lands in the
URL's host rather than its path — and the other spelling answered with
The command "" is not supported, which names nothing and is what a real typo gets too. All three
spellings work now.- The diagnostic text no longer claims custom code you never wrote. A hand-added website starts
with a starter template of fourteen comment lines, and the copyable diagnostics counted them, so it
reportedCustom CSS: 14 linesfor every website nobody had edited. CONTRIBUTING names that field
as one of the two that settle most reports. - The zh-Hans translation for Edit… came back; the websites list still uses it.
- The documented install command did not work, and the cask printed a deprecation warning on every
tap. Both fixed.
Known issues
Open on purpose, tracked in docs/ROADMAP.md:
- A YouTube video cannot be shrunk back into the YouTube page, so there is no way to sign in.
Bilibili does not have this problem. - Switching website can take a few seconds, with no sign that anything is happening.
- Nothing is done yet to reduce what the app costs while nobody is looking at it.
- The app follows the system language; there is no picker inside it.
- The app cannot tell you a new version exists.
brew upgrade— or this page — is how you find
out. - Nothing anywhere handles HDR.
- Multiple displays has never been run on two.
Requires macOS 15 or later.
Install
brew tap PathGao/tap https://github.com/PathGao/Nifro
brew install --cask nifro
Already installed with Homebrew: brew upgrade --cask nifro.
Or download the disk image
Nifro-arm64.dmg on Apple silicon, Nifro-x86_64.dmg on Intel. There is no universal binary, so
nobody downloads the half they cannot run.
This build is self-signed and not notarized by Apple, so a disk image you download and open yourself will be turned away the first time with "Apple could not verify Nifro is free of malware". That dialog offers only Move to Trash and Cancel — the way through is somewhere else:
- Press Cancel. Do not move it to the trash.
- Open System Settings → Privacy & Security and scroll to the bottom.
- There is a line naming Nifro, with an Open Anyway button. Press it, and confirm with your password or Touch ID.
- Press Open in the last dialog. macOS remembers, and never asks again.
Installing with Homebrew skips all of that — the cask clears the mark for you.
Checksums
Apple silicon Nifro-arm64.dmg sha256 66320b393e0adc27ec0ae0b83cdd582b9efcb616fcd8de9ce9450b167e3ca9b5
Intel Nifro-x86_64.dmg sha256 0d944d0f5b9310d6dccf3ffbf303ef2a7920b2a993f3622f7df0349f05df911a
What's Changed
Full Changelog: v0.1.0...v0.1.1
v0.1.0
The first release. Nifro shows a website as your desktop wallpaper — a fork of Plash, picked up from the last MIT-licensed commit before that project's source was closed.
Install
brew tap PathGao/tap https://github.com/PathGao/Nifro
brew trust --cask PathGao/tap/nifro
brew install --cask nifroHomebrew will not load a cask from outside its own repositories until you say you trust it, because a cask can run code after installing. This one runs a single command — it clears the "downloaded from the internet" mark macOS puts on the app — and you can read the whole thing in Casks/nifro.rb first.
Or download the disk image
Nifro-arm64.dmg on Apple silicon, Nifro-x86_64.dmg on Intel. There is no universal binary, so nobody downloads the half they cannot run.
This build is self-signed and not notarized by Apple, so the first launch will be turned away with "Apple could not verify Nifro is free of malware". That dialog offers only Move to Trash and Cancel — the way through is somewhere else:
- Press Cancel. Do not move it to the trash.
- Open System Settings → Privacy & Security and scroll to the bottom.
- There is a line naming Nifro, with an Open Anyway button. Press it, and confirm with your password or Touch ID.
- Press Open in the last dialog.
macOS remembers, and never asks again. Installing with brew skips all of it.
Requires macOS 15 or later.
What is here
- Any web page as the wallpaper, with per-website settings: sound, clickability, schedule, display, reload interval, custom CSS and JavaScript.
- Choose Region — drag a rectangle over the wallpaper and that part fills the screen. It is stored as a place and a magnification rather than a rectangle, so the same website works on a second display of a different shape.
- The page is kept out of the strip behind the menu bar, and a band there takes the website's colour.
- Browsing Mode and hold-to-interact, for logging in or scrolling to a position.
- 38 sites to start from, in the app rather than on a web page, because each one carries the settings that make it work.
- English and Simplified Chinese throughout.
- Nine keyboard shortcuts, all on ⌃⌥⌘ and all rebindable.
Known issues
Open on purpose, and tracked in docs/ROADMAP.md:
- A YouTube video cannot be shrunk back into the YouTube page, so there is no way to sign in. Bilibili does not have this problem.
- A framed region does not always land exactly where it was framed.
- Switching website can take a few seconds, with no sign that anything is happening.
- Nothing is done yet to reduce what the app costs while nobody is looking at it.
- The app follows the system language; there is no picker inside it.
- The app cannot tell you a new version exists. There is no update check yet, so
brew upgrade— or this page — is how you will find out. - Nothing anywhere handles HDR.
Checksums
Apple silicon Nifro-arm64.dmg sha256 a55538b84f7417f68600bc3544852e136f2d7eace7022d5c7d0e9ce03980b52a
Intel Nifro-x86_64.dmg sha256 5db977383e22d7f7f3925423bca8a093f8e0e73d1a7261de8cc66be9687e0be5