Send picker: replace the directory browser with a recursive fuzzy finder.
Type to match files/folders anywhere under $HOME (sahilm/fuzzy, now a direct
dep, compiled in), stage with enter, ctrl+d folders-only, ctrl+s send,
ctrl+u re-root up. Folder send is preserved (staging a dir sends it whole).
Quick-send: `omarchy-send <paths>` opens the TUI on the device list with the
paths pre-staged; selecting a device sends immediately and the window closes
itself when the transfer completes. Runs server-less so it coexists with an
already-running instance (controller Set* methods nil-guard the server).
Nautilus right-click "Send via Omarchy-Send" (desktop-only): a nautilus-python
extension that launches quick-send in a floating terminal with the selection
pre-staged. Resolves omarchy-send to an absolute path since ~/.local/bin is
not on Nautilus's session PATH. install.sh ships it only where Nautilus is
present, so headless boxes skip it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two related additions for using omarchy-send beyond the focused TUI:
Headless send (no TUI / no TTY) — for scripts, cron, SSH sessions:
omarchy-send --to "<alias>" --message "<text>" [--send-pin N] [--wait 15s]
Resolves the peer by alias over multicast (discovery only, not the HTTP
receiver, so it co-exists with a running instance), sends, prints a
one-line result, and exits non-zero on not-found / failure. New
discovery.FindPeer/Snapshot and client.SendMessageSync (returns the error
directly, incl. ErrPinRequired).
Bugfix surfaced by the above: the official LocalSend client answers a
message prepare-upload with HTTP 204 No Content (the text rides in the
preview field, nothing to upload). prepareUpload only accepted 200, so
message sends to official peers failed with "prepare-upload status 204"
in BOTH the new headless path and the existing TUI. Now treats 204 as
success (empty response).
Desktop notifications: a running receiver raises a notify-send
notification on an incoming message or file offer, so mako shows it on
Omarchy/Hyprland even when the TUI isn't focused. New internal/notify
(best-effort; self-disabling on headless boxes with no notify-send /
session bus). Off-switch: --no-notify flag, cfg.NoNotify, and a Settings
'n' toggle wired to a live atomic gate via Controller.SetNotify so it
takes effect without a restart.
Tests: discovery/find_test, client/message_sync_test,
client/prepare_204_test, app/events_test. Race-clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Messages to a PIN-protected peer now prompt for the PIN and retry, like
file sends (was a silent failure). SendMessage takes a pin argument and the
TUI tracks whether a pending send is a message or files.
- Copy a received message to the clipboard with `y` (in the Messages tab or
while reading one).
- Send the clipboard as a message with `v` on a device: it opens the compose
box pre-filled with the clipboard text to review before sending.
- New internal/clipboard package shells out to wl-clipboard / xclip / xsel,
and falls back to tmux's paste buffer when running inside tmux on a headless
box (tmux load-buffer -w also reaches the outer clipboard via set-clipboard,
which omaterm enables by default). No system tool -> "clipboard unavailable".
- Route the standard logger to the debug log (or discard) at startup so stray
stdlib logging — e.g. net/http's "unsolicited response on idle channel"
notice after a peer sends a late response — can't paint over the TUI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds LocalSend-compatible text messaging alongside file transfer.
Wire format (matches the LocalSend app): a message is a single "file" with
fileType "text/plain" whose content rides in the prepare-upload `preview`
field. The receiver returns an empty token set, so nothing is uploaded — the
text is read straight from the preview.
- client: SendMessage builds that single-file prepare-upload (no body upload).
- server: detect a message (one text file with non-empty preview), surface it
on a new Messages() channel instead of saving a file, and respond with an
empty file set. Messages bypass the accept prompt (auto-received); the PIN
gate still applies.
- app: bridge the server's messages channel to the TUI as MessageMsg.
- tui: a new Messages tab lists received messages (enter to read full, d to
delete); press `m` on a device to compose and send one. Incoming messages
show a footer notice.
Tests: end-to-end send→receive (text intact, sender preserved, nothing written
to disk) and unit coverage of the message-detection rule.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The sender only handled regular files: a staged directory would fail at
upload time, so users could only send individual files.
Now a staged directory is walked recursively and each file is advertised
with a name relative to the folder's parent (e.g. "Trip/day1/img.jpg"),
which is the LocalSend-compatible way to carry structure. The receiver
recreates those subdirectories under the receive dir, creating parents as
needed. The path-traversal guard is preserved: names are cleaned against a
leading "/" to collapse "..", and a containment check ensures the result
stays within the receive dir.
In the TUI send picker, "a" stages the folder currently being browsed;
staged folders are tagged in the panel and the help text is updated.
Tests: directory expansion produces relative names; an end-to-end folder
send recreates the structure on the receiver; destPath preserves subdirs,
rejects traversal, and de-duplicates within subfolders.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the advertised device name defaulted to the machine hostname.
Since the alias is broadcast in plaintext over multicast to the whole
subnet every few seconds while the TUI is open, that leaks the hostname
to anyone on the network — a privacy problem on a laptop joining
untrusted Wi-Fi (the reason LocalSend randomises its aliases).
Now first run generates a random "Colour Object" name from public-domain
word lists (e.g. "Crimson Quasar"): 56 colours x 44 celestial objects =
2464 combinations. It is generated once and persisted, so a device keeps
the same name across restarts. The hostname is still carried in
DeviceModel, and the alias remains overridable via --alias or Settings.
Word lists are original and use only generic colours and public-domain
astronomy terms, so there is no copyright or trademark exposure.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>