AI-Created Software

The following AI-generated aka vibe-coded software. The software is specific to my needs and my system and may not work for you or your system. Support is limited, but if you report a bug I'll certainly look into it. All code is provided in a zip file for you to review should you choose.

Download Router

A Chrome extension that files downloads into subfolders automatically, based on where they came from and what kind of file they are. Set a rule like huggingface.co → Models or .pdf → Documents, and matching downloads land in that folder instead of the general pile.

Version 3.0. Manifest V3. Works in Chrome, Vivaldi, Edge, Brave, and other Chromium browsers.

Installing

Not published to the Web Store, so load it unpacked:

  1. Keep the folder structure intact — manifest.json, the HTML, JS and CSS files at the top level, with the icons in an icons subfolder beside them.
  2. Open chrome://extensions (or vivaldi://extensions).
  3. Turn on Developer mode, top right.
  4. Click Load unpacked and pick the folder.
  5. Pin the icon to the toolbar if you want one-click access.

After editing any file, hit the reload arrow on the extension’s card.
Changes to background.js or rules.js need that reload; changes to the interface take effect the next time you open it.

Two ways in

Click the toolbar icon for the popup, or open the full page — right-click the toolbar icon and choose Options, or press Open full page at the bottom of the popup.

Both surfaces are the same interface, built from the same code, so anything you can do in one you can do in the other. The full page just has room to breathe: it shows all 50 activity entries instead of the most recent six, and it isn’t at risk of being dismissed when a file dialog opens.

Using it

Adding a rule. Fill in a source domain (huggingface.co), a list of file types (pdf, zip, tar.gz), or both, then a destination folder, and click Add Rule. The folder is relative to your browser’s download directory and is created on first use.

Editing. Click Edit on any rule.
The form fills in and the button turns green and reads Update Rule. Cancel Edit backs out.

Turning a rule off. The checkbox at the left of each row switches a rule on and off without deleting it. A disabled rule is dimmed, keeps its place in the list, and is skipped entirely when a download arrives — useful for testing which rule is responsible for something.

Deleting. The red X removes a rule immediately, with no confirmation.

Replacing same-name files. Each rule has a checkbox: Replace files with the same name. Off (the default) means a second download of report.pdf is saved as report (1).pdf. On means it overwrites the existing file — silently, with no prompt, and the old file does not go to the trash.
Useful for a daily export that always arrives under the same name; risky for everything else. Each rule shows its current mode underneath in the list.

Recent activity

The Recent activity list records the last 50 downloads and what the extension did with each: the file, the site it came from, where it was saved, and which rule matched — or No rule matched, which is usually the more useful entry when something didn’t get filed where you expected.

Entries store the source hostname only, never the full URL, along with the filename and destination path. Everything stays in chrome.storage.local. Record switches logging off (existing entries are kept until you clear them), and Clear empties the list. The log is never included in an export.

How matching works

A rule can match on a source domain, on file types, or on both. Every enabled rule is tested in list order and the first one that matches wins.

  • Domain only — everything from that site, whatever the file type.
  • File types only — the rule shows as Any source, and catches those extensions from anywhere.
  • Both — the download has to satisfy both.
    github.com + zip routes only the zips from GitHub, and leaves everything else from GitHub alone.

Domain matching is a plain substring test against the source URL and the referrer URL, not a domain parse, and everything is compared in lower case. That has consequences worth knowing:

  • A rule for hub.co also matches github.com, because the text appears inside it. Prefer longer, more specific fragments.
  • Matching the referrer means a file hosted on a CDN still gets routed by the page you downloaded it from.
  • Rules are tested in the order you added them. There’s no priority control in the UI, so when two rules could match the same download, the older one wins. To reorder, delete a rule and add it again to move it to the end.

File types are matched against the end of the filename, so multi-part extensions like tar.gz work. Leading dots and case don’t matter: PDF, .pdf and pdf are the same thing. A filename literally called pdf, with no extension, is not a match.

Downloads that match nothing are left entirely alone, and disabled rules are skipped as though they weren’t there.

Folder names

Subfolders work: AI/Models nests two levels deep. Before use, the folder string is cleaned up — backslashes become forward slashes, the characters : * ? " < > | are stripped, .. is removed so a rule can’t escape the downloads directory, and stray leading or trailing slashes are trimmed. If nothing survives that (a rule of .., say), the download is saved normally rather than to a broken path.

Backup and restore

Export writes every rule to a JSON file named for the moment it was created, in local time:

download-router-settings-2026-09-06-143052.json

The file wraps the whole of the extension’s local storage:

{
  "format": "download-router-settings",
  "formatVersion": 2,
  "exportedAt": "2026-09-06T18:30:52.114Z",
  "settings": {
    "rules": [
      {
        "id": "r-8fk21mq0",
        "domain": "huggingface.co",
        "extensions": [],
        "folder": "Models",
        "overwrite": true,
        "enabled": true
      }
    ]
  }
}

Import opens a file picker, then shows what the file holds — how many rules, how many overlap with yours — before writing anything. Then choose:

  • Merge — adds the incoming rules to your existing ones. Where a rule matches on the same domain and file types, the imported version replaces yours in place, keeping its position in the list.
  • Replace all — discards your current rules entirely and installs the imported set.
  • Cancel — changes nothing.

The importer also accepts an export from any earlier version, a bare {"rules": ...} object, or a hand-written {"domain": "Folder"} map, so you can write a rule set by hand. Anything older is upgraded to the current shape on the way in.
Entries with no folder, or with neither a domain nor a file type, are skipped and reported in the summary rather than silently dropped.

Rules live in chrome.storage.local, which is per-profile and never synced. Export is how you move a setup to another machine, another browser, or a fresh profile.

Upgrading from 2.x

Rules used to be stored as an object keyed by domain, which no longer works now that a rule can have no domain at all. Anything already in storage is converted to the new ordered-array shape the first time it’s read, and rewritten on install and on your next edit. Existing rules keep their folder and overwrite setting, come across enabled, and start with no file-type restriction, so routing behaves exactly as before. Old export files still import.

Files

File Purpose
manifest.json Extension metadata, permissions, icon declarations
background.js Service worker; renames each download as it starts and records it
rules.js Shared model: rule shape, migration, matching, storage and log helpers
ui.js The interface — rule editor, backup, activity log
ui.css Styles for both surfaces
popup.html Toolbar popup shell
options.html Full-page shell
icons/icon16.png, icons/icon32.png, icons/icon48.png, icons/icon128.png Toolbar, extensions page, and store sizes
icons/icon.svg Icon source, for regenerating the PNGs

Permissions

  • downloads — required to watch downloads and change where they land. This is the whole point of the extension.
  • storage — saves your rules and the activity log locally.
  • <all_urls> — carried over from earlier versions so the extension can read the source and referrer of each download. If you want to tighten things up, this is the one to try removing first; test a download from a site with a rule before deciding it’s safe to leave out.

The extension makes no network requests of its own and sends nothing anywhere. Everything stays in local storage.

Known quirks

The file picker can close the popup. Chrome sometimes dismisses an extension popup when the OS file dialog takes focus, which cancels an import mid-flight. If that happens on your system, run the import from the full page instead, where nothing can dismiss the window.

Overwrite is permanent.
conflictAction: 'overwrite' replaces the file in place.
There’s no undo and nothing lands in the trash.

The extension’s own export is never routed.
Downloads originating from the extension’s own URL skip rule matching, so a loose rule can’t file your backup somewhere unexpected.

“Ask where to save each file” still applies. If that browser setting is on, you’ll get a save dialog as usual; the routed path shows up pre-filled in it.

Version history

Version Change
3.0 File-type matching, per-rule enable/disable, activity log, full-page options view. Rules are now stored as an ordered array; older storage is migrated on read
2.7 Icons moved into an icons subfolder
2.6 Custom icon, declared at all four sizes and shown in the popup header
2.5 Export and Import moved to a sticky top bar; paste-JSON option removed
2.4 Export filename time as six digits (143052)
2.3 Time added to the export filename
2.2 Export and import of all settings
2.1 Per-rule option to replace same-name files
2.0 Rule editing UI
(1 votes, average: 5.00 out of 5)

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail.