Skip to content
gameticharlesPublic

About

Every drive in the Omarchy bar: USB, SD, phones, internal disks and cloud (Google Drive, Mega, OneDrive, Dropbox). Mount, inspect, format, sync and eject safely.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Storage Drives (storage-drives)

USB sticks, SD cards, phones, external HDDs/SSDs, internal system storage, and multi-cloud accounts (Google Drive, Mega, OneDrive, Dropbox) in the Omarchy bar: mount, open, inspect, format, auto-repair NTFS dirty bits, two-way selective sync, and safely eject without leaving the desktop.

Storage Drives: the drive being written pulsing red in the bar, Drive Info, and cloud accounts

Advanced Features in Storage Drives

  • Multi-Cloud Drive Integration: Add multiple Google Drive, Mega, OneDrive, Dropbox, and Nextcloud/WebDAV accounts via rclone directly in the Network & Cloud tab. Features selective folder syncing at any depth (keep only chosen folders on disk; open a folder to keep just some of the folders inside it, or keep it whole and leave parts out), loose files at the top (off unless you turn them on), cleanup of what no longer syncs (each item checked against the cloud first), auto-sync timers via systemd, and read-only on-demand browsing (rclone mount) without consuming local disk space.
  • Cloud account tiles: One full-width row per account, each with a status dot (synced, syncing, needs a look, failed), the signed-in email (Google Drive, OneDrive and now Dropbox), a quota bar that turns urgent when nearly full or over quota, and the last sync time. Sync, open and browse are one click from the tile; the detail view's chips wrap instead of squeezing.
  • Accounts cannot trip over each other: The add form checks, as you type, that the new account's sync and browse folders are not used by — or nested inside — another account's. Accounts saved before that check are flagged on their tile, and the browse folder can be moved from the account's Config drawer. A browse mount only counts as an account's if rclone is serving that account there, so a second account can no longer report "mounted" while showing the first one's files, or unmount it on removal.
  • Sync failures say why: The status line quotes rclone's own error from the sync log rather than "Resync failed", and Config has View sync log and Resync. A brand-new account's local folder is created on its first sync; once an account has synced, a missing folder stops the sync instead of being treated as "delete everything".
  • Disconnect and delete ask twice: Both need a second click within four seconds.
  • Drive Info: Open a drive's settings drawer (cog, or i) for its full spec sheet — kind (thumb drive, external, SD card, internal, system), model, serial, firmware revision, how it is attached and how fast (USB 2.0 (480 Mb/s), NVMe · PCIe 3.0 ×4, SATA · 6.0 Gb/s), solid state or spinning, partition table, TRIM support, hardware write-protect and the exact size in bytes. A volume's drawer has the same for the filesystem: type and version (FAT16 is no longer called FAT32), label, UUID, partition type, PARTUUID, the LUKS mapper behind it, access and mount point. Click any value to copy it. A USB 3 drive that ended up on a USB 2 link is called out, since that is a tenfold slowdown nothing else on screen explains.
  • Whole-Drive & Partition Formatter, built in: Wipe an entire removable drive or a single volume from the panel, over udisks — no root, no extra packages. A whole-drive wipe writes a fresh GPT table and one partition filling the drive, typed as Microsoft basic data for exFAT/NTFS/FAT32 (so Windows sees it) or Linux data for ext4/Btrfs. Both are confirmed by typing the drive's kernel name, and both refuse system drives, mounted volumes and unlocked containers outright.
  • NTFS Dirty-Bit Handling: NTFS volumes that will not mount because Windows hibernated or left them dirty are flagged in the panel. Auto-mount tries a normal mount and, if that fails, sends a notification rather than failing silently; the built-in check → repair flow fixes them over udisks, and if omarchy-ntfs-fix is installed a one-click repair in a terminal is offered too.
  • Disk Usage Inspector (dua): Click the pie chart icon (or press d) on any mounted volume to inspect space utilization interactively via dua i in a floating terminal.
  • Terminal at Mountpoint: Click the terminal icon (or press t) on any mounted volume to immediately launch a terminal in that volume's directory.
  • System Storage Visibility Toggle: Click the disk icon in the header (or press s) to toggle display of internal NVMe storage, Btrfs subvolumes, and root partitions alongside external drives.
  • Sleep / Suspend Auto-Unmount Hook: Safely unmounts removable drives before system suspend to eliminate NTFS dirty bit corruption and ext4 journal desynchronization.

Core Capabilities

  • Mount, open, and eject any removable volume without a password, because udisks2 already lets the logged-in session do it. Ejecting unmounts every volume on the drive, re-locks anything encrypted, then powers it down.
  • Shows what is being written, and how fast. While any drive is being written — a USB stick, an internal disk or the system drive — or a cloud account is syncing, the bar icon becomes that drive's own icon and pulses red, and the panel header shows the live speed. Small background writes to the system disk (the journal, caches) are left out, so it only pulses for a real write.
  • Knows when a removable drive is still being written to. Only a drive that can be unplugged says "do not remove", because a copy dialog reaching 100% is not the moment a stick is safe to pull. An eject asked for mid-copy is held, then fires once the drive goes quiet.
  • Names who is holding a busy mount, instead of stopping at target is busy, and offers a lazy unmount as an explicit second choice.
  • Phones and cameras get their own section: Android over MTP, iPhone over AFC and PTP, with their access labelled Files or Photos.
  • Unlocks an encrypted volume in place, mounts it as the container opens, and closes it again from the same row without ejecting the drive.
  • Renames the volume label. It travels with the stick to every machine that reads it, unlike a nickname, which never leaves this shell. The field counts down against that filesystem's own limit as you type.
  • Checks a filesystem, and repairs it only if you ask twice. Repair is the one thing here that rewrites a filesystem, so it appears only after a check has found something, never one stray click from the mount button. A suspect drive mounts read-only first, so files come off without a byte going back.
  • Formats a volume, once you have said so twice. Erasing is the only thing here that destroys data on purpose, so the button is on unmounted volumes only and the volume's own kernel name has to be typed out before anything runs. Pick exFAT, FAT32, NTFS, ext4 or Btrfs, name it on the way, and zero the drive first when you want the old contents actually gone.
  • A single drive can set its own terms. One drive can mount read-only while the rest mount normally, and one drive can open — or refuse to open — after mounting whatever the global setting says. Both stick to the hardware, the same way a nickname does.
  • Reads a drive's own health, for the drives that report any. udisks does that read over D-Bus and hands the answer over, so no extra package is needed and nothing runs as root. Most USB sticks report nothing at all, and a drive that reports nothing shows nothing — see below before expecting a number.
  • Unmounts before the machine sleeps, optionally, so a drive pulled from a sleeping laptop is not left half-written, and says which one refused when one does.
  • Nicknames stick to the hardware, keyed to the drive's serial rather than whichever /dev/sdb it landed on today. A drive can also run a command of your choosing when it appears, and that command can report its progress back, so a stick running a backup looks like one instead of looking idle.
  • Empties the trash you cannot see: the .Trash-1000 that quietly fills a stick with files you thought were deleted.
  • Never offers to eject the disk you booted from. A USB-booted system disk reports itself as removable just like a thumb drive; anything holding /, /boot or /home is left out entirely.
  • Plus: connect and remove notifications, a warning when a drive is pulled while still mounted, free-space bars, eject-all, and optional text beside the bar icon.

Install

omarchy plugin add https://github.com/gameticharles/drives.git --enable

Needs Omarchy 4 (Quattro) and udisks2, both standard. It calls lsblk, udevadm, udisksctl, busctl, gio, fuser, du, wl-copy and Omarchy's own omarchy-* helpers. Nothing runs as root.

Cloud drives also need rclone (and fuse3 to browse without syncing). The plugin never installs them itself: when rclone is missing, the Network & Cloud tab copies a search for the Omarchy menu (Super + Space) › Install › Package and opens that menu, then notices by itself once rclone is there.

To remove it:

omarchy plugin remove storage-drives
rm ~/.local/state/omarchy/removable-drives.json   # optional: forget nicknames

It never writes to shell.json or your Hyprland config; the bar entry belongs to Omarchy's plugin commands and nicknames live in the file above.

Optional terminal helpers

A few buttons hand work to a terminal helper instead of doing it in the panel, because the job needs elevated rights or an interactive tool. The plugin does not ship these. It checks for each one at startup and on rescan, and hides the button when the helper is not on your PATH:

Helper Button
dua Disk Usage
omarchy-disk-speedtest Speed Test (ships with Omarchy)
omarchy-drive-trim Trim SSD (also needs a drive that accepts discards)
omarchy-drive-scrub Btrfs Scrub
omarchy-ntfs-fix NTFS Fix, and the one-click repair on an unmounted NTFS volume
omarchy-drive-flash Flash Bootable ISO
omarchy-drive-recover Recover & Inspect

Formatting, checking, repairing, renaming, unlocking and the drive info never depend on these: they go through udisks.

Phones

Omarchy ships gvfs-mtp, so Android works out of the box. Apple devices speak AFC and need three packages Omarchy does not ship: usbmuxd, gvfs-afc and gvfs-gphoto2.

A plugin may not install packages itself, so when something is plugged in that gvfs cannot reach, the panel names what is missing and offers to open Omarchy's own installer.

A trusted iPhone appears as two entries: app documents over AFC, and the camera roll over PTP. With iCloud Photos set to "Optimize iPhone Storage" the camera roll can read as empty, because the originals are not on the device.

Using it

Where Action
Bar icon left = open · right = rescan · middle = open first mounted volume
Volume row click = mount and open, or open if mounted · middle-click = copy its path
Phone row click = browse (mounting on demand)
Mount / open / unmount icons mount · open · unmount that volume
Rename / check icons rename the volume · check it for errors
Eraser icon format an unmounted volume: erase it and create a filesystem
Format Entire Drive wipe the whole drive: new partition table and one volume
Info values click any value in a Drive Info or Details grid to copy it
Close-apps icon on a busy unmount: ask the programs holding it to close, then retry
Read-only / repair icons after a failed check: mount read-only · repair
Lock icon locked: type the passphrase to unlock · open: lock it again
Health icon on drives that report health: hover for the reading, click to re-read
Drive row icons open after mounting · mount read-only · nickname · eject
Eject icon in the header eject every attached drive

Keyboard, while the panel is open:

Key Key
j k ↑ ↓ move e x eject the drive
Enter Space mount and open, or eject E eject every drive
m mount or unmount t terminal at this volume
o open y copy its path
n nickname the drive r Esc rescan · close
l rename the volume c check it for errors
f format the volume, or the whole drive on a drive row i open the info drawer

Settings

Set on the widget's entry in ~/.config/omarchy/shell.json, or through Setup > Plugins.

Key Default What it does
alwaysShow false Keep the icon in the bar with nothing attached
openOnMount true Open the file manager once a volume mounts
autoMountOnConnect true Automatically mount removable volumes on plug-in
autoCleanTrashOnEject false Empty drive trash folder before ejecting
showSystemDrives true Display internal storage drives alongside removable media
notifications true Announce drives, warn when one is pulled while mounted
unmountOnSuspend true Unmount every removable volume when the machine suspends
fileManager "" Command used to open a mount point; empty means xdg-open
barLabel "none" Text beside the icon: none, free, name, count
refreshIntervalSec 8 How often free space is re-read while the panel is open
fullWarnPct 90 Fill level at which a volume's bar and free-space line turn urgent
thumbMaxGb 256 USB disks this size or smaller show as thumb drives unless the model says otherwise
healthAlerts true Read drive health in the background and notify when it gets worse
lowSpaceAlerts true Notify once when a mounted volume crosses fullWarnPct

Per-drive settings live in ~/.local/state/omarchy/removable-drives.json, keyed by serial and watched for changes, which is also how you attach a command to a drive:

{
  "version": 1,
  "drives": {
    "serial:0901f8ef1ed9c144": {
      "nickname": "Work backup",
      "onConnect": "rsync -a ~/Documents/ \"$2\"/documents/",
      "autoOpen": false,
      "readOnly": true
    }
  }
}

onConnect runs through bash -c when that drive appears, with $1 as its device path, $2 as its first mount point, and $3 as a file it may write progress to. Nothing writes it for you.

Reporting progress from a hook

Write key=value lines to $3, which means one echo is enough:

percent=42
status=Copying documents

Both keys are optional; done=1 says the hook has finished, and a bare number on its own line is read as a percent. The panel draws a bar on the drive row while the hook runs and shows the status beside it. A hook that reports nothing still counts, it just has no bar.

The drive counts as busy for as long as the hook runs, so an eject asked for mid-copy is held and fires once the hook is done — the same as for a copy the kernel can see. That matters because the kernel cannot see this one: an rsync goes quiet between file batches, and the busy icon goes out with it.

The file lives under $XDG_RUNTIME_DIR, so it never reaches your disk and never outlives the session. The panel watches the hook's process rather than the file, so a hook that dies mid-copy ends the bar instead of leaving it stuck at 42% forever.

A hook that says what it is doing, in one line:

"onConnect": "echo 'status=Backing up' > \"$3\"; rsync -a ~/Documents/ \"$2\"/docs/; echo done=1 > \"$3\""

A hook written before $3 existed is unaffected: it is a new argument, not a changed one.

Drive health

A drive that reports SMART gets a health icon on its row, with its temperature and hours powered on in the tooltip and any concern written out in the row itself. It comes from udisks over D-Bus, which does the privileged read for us — no smartctl, no smartmontools, nothing running as root.

The reading is taken when the set of drives changes, on a rescan, and every six hours in the background — never continuously — so the temperature is a snapshot rather than a live thermometer; open a drive's telemetry row for minute-by-minute readings. With healthAlerts on, a drive whose verdict gets worse is announced in a notification even with the panel closed, and a drive that reports itself failing is announced at the first reading of a session.

Most USB sticks report nothing at all, and that is the ordinary case rather than a fault: a thumb drive carries neither SMART interface, so the icon simply does not appear. External SSDs and hard drives behind a SAT-capable bridge are the ones that answer. Do not read a missing icon as a problem.

No temperature threshold is invented here. The number is shown as udisks gives it, because a figure you can read and look up beats a line drawn on a guess.

autoOpen overrides openOnMount for this drive alone: true always opens, false never does, and leaving it out follows the global setting. readOnly mounts every volume on the drive read-only, which is what an archive disk you never want written to wants. The panel writes both from the drive row, and reads them strictly on the way back in: a readOnly that is neither true nor false is taken as true, because a typo in this file must not be the reason an archive drive came up writable.

Scripting

omarchy-shell storage-drives toggle
omarchy-shell storage-drives refresh                       # re-read what is attached
omarchy-shell storage-drives list                          # drives, as JSON
omarchy-shell storage-drives phones                        # phones, as JSON
omarchy-shell storage-drives network                       # network & cloud mounts, as JSON
omarchy-shell storage-drives cloud                         # configured cloud accounts, as JSON
omarchy-shell storage-drives status                        # {"busy":false,…}
omarchy-shell storage-drives eject /dev/sdb                # or ejectAll
omarchy-shell storage-drives rename /dev/sdb "Work backup" # "" clears it
omarchy-shell storage-drives label /dev/sdb1 "Photos"      # the label on the drive
omarchy-shell storage-drives check /dev/sdb1               # verdict lands in status
omarchy-shell storage-drives smart /dev/sdb                # health, as JSON
omarchy-shell storage-drives mountReadOnly /dev/sdb1       # rescue without writing
omarchy-shell storage-drives lock /dev/mapper/luks-…       # close an open container
omarchy-shell storage-drives format /dev/sdb1 exfat Photos # erases the volume
omarchy-shell storage-drives expandDevice /dev/sdb         # expand drive settings
omarchy-shell storage-drives expandVolume /dev/sdb1        # expand volume drawer
omarchy-shell storage-drives toggleTelemetry /dev/sdb      # toggle telemetry row
omarchy-shell storage-drives setTab network                # switch to "local" or "network"
omarchy-shell storage-drives info /dev/sdb                 # drive or volume details, as JSON
omarchy-shell storage-drives openCloud gdrive              # open a cloud account's detail view
omarchy-shell storage-drives showDrive /dev/sda            # open a drive with its Drive Info shown
omarchy-shell storage-drives unmountAll /dev/sdb           # unmount every volume, no power-off
omarchy-shell storage-drives formatDrive /dev/sdb exfat Photos # wipes the whole drive

format destroys what is on the volume, and formatDrive destroys everything on the drive. Both take the same refusals the panel does — a mounted volume, a drive still being written to, a system drive, or a filesystem udisks will not create are all turned away with the reason — but naming the node, the type and the label in one line is the whole confirmation, so there is no second question the way there is in the panel.

status reports busy: true while the kernel still has I/O in flight or a connect hook is still running, so a backup script can wait for the drive to settle; both eject calls wait by themselves. hooks says which drive is at what percent, and health carries each drive's verdict — unsupported, healthy, warning or failing. check returns straight away and leaves its own verdict in status as healthy: true, false, or null when the answer could not be read.

smart returns the whole record, and answers supported: false with every other field null for the many drives that report no health at all.

Anything other than ok back from these is the reason they did not run, so a script never has to guess whether a refusal happened.

How it works

Panel.qml draws the bar icon and popup, Service.qml owns everything that touches the system, and Model.js is pure parsing and formatting with no QML or processes, which is what makes it testable without a compositor.

Device-supplied strings (labels, vendor names, phone names) are hostile input: whoever formatted a stick chooses its label. Every Text is pinned to Text.PlainText so Qt cannot promote one to rich text and fetch a remote <img>, and strings handed to components whose Text this plugin does not own are stripped of angle brackets first. Paths stay byte-exact, since commands are built from them.

Renaming a filesystem, running its fsck, and creating a new one are things udisks exposes on D-Bus that udisksctl has no verb for, so they go over the bus through busctl. It is still allow_active: yes, the same no-password path mounting takes. The object path is asked for rather than built, since udisks escapes the kernel name into it and an unlocked LUKS volume lands at dm_2d3. The rename and the fsck unmount the filesystem first and mount it back afterwards, whether or not the middle step worked; the format does neither, because a mounted volume is refused outright rather than taken offline on the way to being erased.

A format is checked as one plan — which volume, which type, which label, whether to zero the drive first — rather than as four arguments each looked at somewhere along the way, and the plan names the /dev node it was made for, so swapping the stick between planning it and confirming it retracts the format instead of pointing it at the replacement.

Health comes the same way, off org.freedesktop.UDisks2.Drive.Ata or org.freedesktop.UDisks2.NVMe.Controller on the drive object — one further lookup, since health belongs to the drive rather than to a block device.

Reading those properties does not refresh them: udisks hands back whatever its own last poll cached, which measured ten minutes stale here — the bus said 308 K while every sensor on the same drive said 36.85 °C. So SmartUpdate is called first, which is allow_active: yes in the udisks policy like everything else here. It is still read rarely — once when the attached set changes and again on a rescan, never on the free-space timer — because it is now a round trip to the drive itself, and because every answer but the temperature changes over hours rather than seconds. The update is best-effort: a drive that refuses one is still read, since a stale number beats no number. nowakeup goes to ATA, so a parked external disk is not spun up merely to draw a temperature.

A LUKS passphrase reaches udisks on stdin, never as an argument, because /proc/<pid>/cmdline is readable by every other process you run. udisksctl takes a key only from a file, so it is staged under umask 077 in the RAM-backed runtime directory and a trap removes it however the unlock ends.

Emptying a drive's trash is the only recursive delete here, so the path is re-derived from the live mount list and must exactly match a .Trash-<uid> candidate of a mounted removable volume. The tests assert it refuses /, $HOME, the mount root, and drives it is not tracking.

node test/model.test.js       # 288 tests, no compositor required
omarchy plugin validate .     # the same check the shell applies

License

MIT

About

Every drive in the Omarchy bar: USB, SD, phones, internal disks and cloud (Google Drive, Mega, OneDrive, Dropbox). Mount, inspect, format, sync and eject safely.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages