Skip to content

Releases: SysAdminDoc/Aviary

Aviary v1.49.5

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 12 Sep 23:26

Aviary 1.49.5. Download the ZIP for a self-hosted extension install.

Aviary v1.49.4

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 09 Sep 21:36

Aviary v1.49.4 makes installation easier to follow and fixes two problems found while checking the shipped packages.

  • The README now starts with release downloads and current extension screenshots. Firefox's temporary installation and userscript-manager limits are explained before setup.
  • Library storage no longer stays on Measuring. It counts the active profile's collections and reports unavailable measurements explicitly.
  • The compact source ZIP includes the icons required for a clean build. The standalone README's image and guide links work outside a checkout.
  • The original bird icon remains. All 769 original files, including 352 images, are preserved in the repository's concept archive alongside 68 review PNGs.

Use the Chromium ZIP for developer loading. Firefox remains an unsigned temporary add-on with a placeholder ID. The secondary CRX3 is self-signed, not browser-store approval. Neither route removes browser installation restrictions.

Local checks cover 1,167 unit tests, 15 visual tests and all seven browser smoke lanes. Chrome 151, Firefox 155.0.1, Tampermonkey 5.5.0 and Violentmonkey 2.47.0 were exercised in disposable headless profiles. A fresh source extraction rebuilt matching extension ZIPs and userscript files. SHA-256 checksums and a release manifest accompany the downloads.

Screenshots use synthetic X fixtures. No authenticated live-X session or production provider account was tested. The DOM reference remains May 19, 2026, with its existing September 30 freshness-waiver expiry. Back up your library before removing the extension; ordinary removal deletes its stored records.

Aviary v1.47.0

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 22 Aug 21:36

Aviary 1.47.0 makes downloading media the primary path on X. Videos and images use the best available quality, including original-image fallbacks. The redesigned Control Center, permission setup, Wide layout, and mobile feed controls use a flatter and more readable visual system. Verified locally with 862 tests, 65 visual baselines, 2,268 release combinations, and Chrome, Firefox, userscript, and provider smoke checks.

v1.45.0

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 22 Aug 09:50

Tests now import TypeScript sources directly through Node's native type stripping, with explicit .ts specifiers and isolated entrypoint imports for browser stubs. Production userscript and extension builds remain bundled. Includes refreshed v1.45.0 userscript and Chrome and Firefox extension packages.

Aviary v1.37.0

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 20 Aug 06:10

Aviary 1.37.0 gives authored X themes a 1120px Wide canvas, flat timeline surfaces, and connected conversation replies. Comfortable now uses 920px while keeping the discovery rail. Download stays visible at the edge of media action rows. Includes Chrome, Firefox, and userscript builds.

Aviary 1.36.0

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 20 Aug 05:26

Aviary 1.36.0 simplifies the Control Center and optional permissions page with a flatter, denser visual system. Feed download actions are easier to find, and late-loading video buttons stay clickable while Aviary resolves the highest-quality direct file. Includes Chrome, Firefox, and userscript builds.

v1.35.0

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 19 Aug 07:31

Added

  • The timeline can stop. Set a number of posts and the feed stops extending there, with a
    control to continue that releases another page of the same size. Off by default; zero leaves X's
    endless scroll exactly as it was. It applies to feeds that extend — home, profile, search — and
    never to a conversation, which is finite already. The property the implementation is built around
    is that nothing already on screen moves: the control is inserted after the last post shown
    rather than above the cut, so continuing extends downward into space that was below the fold, and
    a post the reader was looking at stays exactly where it was. A release granted on one feed does
    not carry to the next.

  • A filtered post can say what caught it. A filter that hides silently is hard to tell from a
    bug, so a suppressed post now names the rule, keyword, pattern, engagement floor or media type
    responsible — by the rule's title where it has one. The reason comes out of the same evaluation
    that made the decision rather than being worked out afterwards, because a reason computed
    separately is a second implementation of the filter and the two would eventually disagree about a
    post the reader is looking at. Nothing is stored: the sentence lives on the article as an
    attribute the stylesheet reads with attr(), and structural reasons are written into the
    stylesheet itself, so no post costs anything extra. The default names reasons on dimmed posts,
    which changes no layout; the third setting also turns a hidden post into a one-line strip that
    opens on hover or when tabbed into — the post stays clipped rather than having its children
    removed, so it remains reachable by keyboard.

  • A filter rule can carry a name and a lifetime. Start a line with [a title] to name a rule,
    and give it for 7d from <instant> to have it apply only for a while. An expired rule stops
    filtering on the next pass with no settings change and no reload — and is never deleted: the
    panel lists what expired and offers one control that restarts each rule's own window from now.
    The window is stored as its duration plus its start rather than as a deadline, which is what
    makes renewing possible without asking the user again what they originally chose. Rules written
    before any of this keep parsing exactly as they did.

Fixed

  • A feature module that nothing registers is now a test failure. The boot test checked a
    hand-written list of ten feature ids, so a new module could be written, wired to a setting, tested
    against directly, and never loaded by the app — which is how the timeline-stop feature first
    landed. The check now reads every FeatureModule id out of src/features and requires the booted
    registry to hold all thirty-four.

  • Eleven sentences in the Control Center were shipping in English in all eight locales while
    translation coverage reported 100%.
    The extractor draws the panel to harvest its copy, so a row
    that only appears under a condition — an expired rule, a callback the harness cannot supply — is
    reached only by the source scan, and that scan read one string per row: the label. Every such
    row's explanatory sentence was invisible to it. The scan now reads the description too, for each
    helper whose second argument is one, and a test drives the harvester over a conditional row so
    the gap cannot reopen.

  • Filter on the shape of a post, not only its words. Two predicates, both off by default.
    Quote posts hides or dims a post that quotes another — structural, so it is a :has() rule
    costing nothing per post, and it tells a quoted post from a link preview by the quoted author's
    name rather than by the role="link" the two share. Low-engagement posts hides or dims a post
    under a minimum number of replies, reposts or likes; the count is read from the action button's
    own accessible name, which carries the exact figure where the visible text is rounded to "11K",
    and which reads zero as zero where the visible text is simply empty. A post whose count cannot be
    read is never filtered on it, and a minimum of zero reads no counts at all.

Changed

  • The media and verified filters are stylesheet rules now, not work done per post. Both were
    questions about the shape of a post's own subtree — does it contain a photo, a video player, a
    GIF, the verified badge — and the engine answered them with five querySelector calls against
    every article on every mutation batch. They are emitted as :has() rules instead, from one table
    that the remaining JS reader shares, so a timeline scroll no longer pays for them at all. Two
    consequences beyond the saved work: media that renders after a post was judged is now caught,
    where the JS pass had stamped that post done and would never look at it again; and a post the
    engine has not judged yet is left alone, so nothing flashes hidden before the allowlist is
    consulted. Precedence is unchanged — an allowlisted author still outranks everything, a rule that
    dims still beats a media rule that would hide, and hiding video still hides GIFs.

v1.34.0

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 19 Aug 06:27

1.34.0 - 2026-08-19

Changed

  • The unpacked extension bundles are no longer carried in git. dist/extension-chrome/ and
    dist/extension-firefox/ are build output that npm run verify regenerates on every commit
    touching src/ — 1.9 MB of incompressible content.js per target, per commit, that nothing read:
    the load-unpacked instructions and the release artifacts both come from a build. They are ignored
    and built on demand; dist/aviary.user.js and dist/aviary.meta.js stay tracked because a
    userscript manager polls them by raw URL. docs/INSTALL.md now says to build first, for both
    browsers.

  • Every release named in this file now has a tag on the commit that bumped its version. Nine
    releases were tagged and thirty-one were documented, so most of the project's history could not be
    checked out, diffed, or bisected by reference — which is the wrong gap for a project whose most
    frequent question is "when did this selector break". Twenty-four tags were added, located by the
    commit that changed package.json rather than by commit subject, since not every release commit
    said "release". The two releases with no entry at all, 1.7.0 and 1.15.0, are written below
    from their own commit ranges and labelled as reconstructed.

Added

  • Copy a post's link for an alternate front-end. Library gains a Copy post links as choice
    (X, fxtwitter, vxtwitter, fixupx, xcancel); pick anything but X and each post grows a Copy
    link
    control that writes that post's address on the chosen host. Deliberately copy-time
    rewriting rather than redirection: redirecting x.com navigation is the shape everyone else
    shipped and has since removed, because logging in through the alternate host now sets an x.com
    cookie and the front-ends people redirected to have been architecturally dead since X removed
    guest tokens. Nothing X rendered is modified, no navigation is redirected, and no request is
    originated — only what you copy changes. Off by default, and the host list is closed so a typo
    cannot produce a link to somewhere you did not mean.

v1.33.0

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 18 Aug 22:30

1.33.0 - 2026-08-18

Fixed

  • Two X tabs no longer erase each other's work. Every whole-state store loaded once into memory
    and persisted that snapshot wholesale, so a hide in one tab and a hide in the other kept only
    whichever wrote second -- silently, with nothing to see afterwards but a missing entry. Hidden
    posts, seen posts, the media dedup index, the audit log and the bookmark library now merge on
    write under a cross-tab Web Lock: each save folds the change it just made into what is actually
    stored, never the whole in-memory list, so a deletion or a "clear" in one tab is not undone by
    the other's next save either. Aria2 history, the semantic index and archive import jobs are
    written under the same lock but deliberately not merged — each is derived, reconciled, or owned
    by the tab running it, and the reasoning is recorded where the write happens.
  • A daily provider budget can no longer be spent once per open tab. The usage ledger checked
    the counter and then wrote it back, which across two tabs is a time-of-check-to-time-of-use race:
    both read the same "bytes used today", both concluded there was room. The whole check-and-spend
    now runs inside one lock and re-reads the stored ledger first, combined with what the tab already
    knows by taking the higher of the two counters — a daily total only goes up, so that can refuse a
    request that would have been allowed but can never allow one that should have been refused.
  • A library restore holds one lock across snapshot, write, and rollback. The window between
    reading a key's current value (kept for the rollback) and overwriting it was where an ordinary
    save from a second tab used to land, leaving the rollback holding a value that was no longer
    current.
  • Bookmark ids were Date.now() plus a counter that restarts at zero in every tab, so two tabs
    saving in the same millisecond produced the same id. Found by the cross-tab merge, which keys on
    id and would have kept one of the two bookmarks.

v1.32.0

Choose a tag to compare

@SysAdminDoc SysAdminDoc released this 18 Aug 22:06

1.32.0 - 2026-08-18

Added

  • Trust -> Find the feature breaking this page. A binary search over the features that are
    actually running: it turns them all off, then back on in halves, asking after each round whether
    the page is still wrong, and names the one responsible in about five rounds instead of a linear
    hunt through thirty-odd settings. Turning a feature off runs its own destroy rather than
    writing a setting, so reloading restores everything however you stop -- including abandoning
    mid-round. The first round turns everything off, so "this is not Aviary" is a result the search
    can reach rather than a feature named at random. The Control Center and its locale stay on
    throughout and can never be named. The outcome travels in Copy diagnostics as one
    content-free line, and the bug report template has a field for it.

Fixed

  • "Aviary's page script did not load" no longer stands in for "something else answered it".
    A script that wins the very first handshake owns the page agent -- the transferred control port
    stops a later one from displacing it, but nothing can stop the first. The real bridge then sat
    out a three-second timeout and reported agent-absent, which reads as a browser compatibility
    problem when what actually happened is that Aviary's default-on network ad guard is answering to
    somebody else. The agent now refuses a late handshake audibly, and Trust says so. The design note
    on installPageAgent states plainly what the boundary defends and what it does not: it is not
    cryptographic, the refusal itself is forgeable by the page, and it downgrades a diagnostic rather
    than a decision.

  • A browser download is only Saved once the browser says it finished.
    chrome.downloads.download() resolves when the browser accepts the request, so an interrupted
    transfer had already been reported as Saved, marked completed in the queue, and written into the
    duplicate index -- which then refused the retry the user wanted. The extension now reports each
    download's terminal state back to the tab that asked. Started and Saved are distinct
    states on the button; an interrupted transfer reads Retry, marks its queue entry failed, and
    is never recorded as a duplicate; and a transfer still running when the wait gives up stays
    Started rather than claiming either outcome. The tracking is persisted, so the answer still
    arrives after the service worker has been suspended and restarted, and a quality-fallback retry
    reports under the id the page is waiting on. Userscript saves are unchanged: GM_download's own
    callback already is the terminal state.

  • Media inside a quoted post is the quoted account's, not the account that quoted them. The
    extractor walked the whole article subtree and filed every photo and player it found under the
    outer post's handle and id, so saving a photo out of a quote wrote it as if the quoting account
    had published it. Each asset now carries its owner. A quoted post's media is saved under the
    quoted account's handle, its own text, and its own post id where the DOM or a captured record
    supplies one; the post-level Download saves only the post's own media and says so, and every
    excluded asset still has its own Save control. Export records mark quoted and card media with
    whose it is instead of listing it among the account's own.

  • A post with no permalink of its own -- a reply shell still building, or a quote-only post -- no
    longer adopts the quoted post's id, handle, or text. The article's identity is read from outside
    the quote it carries.

  • The exporter's "this is a quoted post" test and the media extractor's are now one definition.
    They disagreed: the exporter looked only for two named test ids while X's current Home renders a
    quote as a focusable div with no test id, so a record could carry a quoted photo with no note
    that a quote existed at all.

Changed

  • The browser floors are declared once, with their reason. They lived in three places that did
    not know about each other -- a number in each manifest and a sentence in docs/INSTALL.md -- and
    the floor is what decides whether a platform feature can be used directly or needs a detection
    branch. src/extension/browser-floors.ts now declares both, records per-feature which of the two
    is true, and preflight fails the build if either manifest disagrees. The decision itself: Firefox
    stays at 128. It is an ESR line, Aviary is sideloaded rather than distributed through a store,
    and raising the floor to pick up URLPattern, @scope or the Navigation API without a branch
    would exclude the users most likely to be running ESR.

  • FeatureRegistry gained suspend/resume, and now runs apply in registration order rather
    than initialization order -- the two differ only once a feature has been suspended and resumed,
    and ad protection is registered first precisely so it runs before anything that reads the
    timeline.