Merge quattro into clock-calendar
Both sides tightened the same center-layout assertion. Taking quattro's: it asserts the weather/update adjacency the test name is about instead of pinning the whole row, which is what kept breaking it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,7 @@ GROUP_DESCRIPTIONS[dev]="Omarchy development tools"
|
||||
GROUP_DESCRIPTIONS[display]="Display and text scaling"
|
||||
GROUP_DESCRIPTIONS[dns]="DNS resolver configuration"
|
||||
GROUP_DESCRIPTIONS[drive]="Drive selection and encryption"
|
||||
GROUP_DESCRIPTIONS[file]="File selection helpers"
|
||||
GROUP_DESCRIPTIONS[font]="Font management"
|
||||
GROUP_DESCRIPTIONS[games]="Game launchers and helpers"
|
||||
GROUP_DESCRIPTIONS[hibernation]="Hibernation setup and removal"
|
||||
@@ -78,6 +79,7 @@ GROUP_DESCRIPTIONS[snapshot]="System snapshots"
|
||||
GROUP_DESCRIPTIONS[style]="Global UI style controls"
|
||||
GROUP_DESCRIPTIONS[sudo]="Sudo configuration helpers"
|
||||
GROUP_DESCRIPTIONS[system]="System status, reboot, shutdown, logout, and lock"
|
||||
GROUP_DESCRIPTIONS[tailscale]="Tailscale helpers"
|
||||
GROUP_DESCRIPTIONS[theme]="Theme management"
|
||||
GROUP_DESCRIPTIONS[tmux]="Tmux session helpers"
|
||||
GROUP_DESCRIPTIONS[toggle]="Toggle Omarchy features"
|
||||
|
||||
+10
-1
@@ -288,6 +288,15 @@ cmd_move() {
|
||||
fi
|
||||
|
||||
local default_section="${PLACEMENT_SECTION:-}"
|
||||
|
||||
# A bare section names the section, not the slot, so let it fall through to
|
||||
# the same anchor placement 'add' uses. Passing it as an explicit target
|
||||
# instead drops the widget on the far end of the row.
|
||||
local target_section="$PLACEMENT_SECTION"
|
||||
if [[ -z $PLACEMENT_INDEX && -z $PLACEMENT_BEFORE && -z $PLACEMENT_AFTER ]]; then
|
||||
target_section=""
|
||||
fi
|
||||
|
||||
local prog
|
||||
prog=$(cat <<JQ
|
||||
$JQ_DEFS $NORMALIZE
|
||||
@@ -302,7 +311,7 @@ JQ
|
||||
commit "$prog" \
|
||||
--arg id "$id" \
|
||||
--arg defaultSection "$default_section" \
|
||||
--arg targetSection "$PLACEMENT_SECTION" \
|
||||
--arg targetSection "$target_section" \
|
||||
--arg targetIndex "$PLACEMENT_INDEX" \
|
||||
--arg before "$PLACEMENT_BEFORE" \
|
||||
--arg after "$PLACEMENT_AFTER" \
|
||||
|
||||
@@ -35,6 +35,7 @@ RESOLUTION=""
|
||||
FULLSCREEN="false"
|
||||
STOP_RECORDING="false"
|
||||
RECORDING_FILE="/tmp/omarchy-screenrecord-filename"
|
||||
REGION_FILE="${XDG_RUNTIME_DIR:-/tmp}/omarchy-screenrecord-region"
|
||||
LOG_FILE=$([[ ${OMARCHY_SCREENRECORD_DEBUG:-false} == "true" ]] && echo "/tmp/omarchy-screenrecord.log" || echo "/dev/null")
|
||||
|
||||
for arg in "$@"; do
|
||||
@@ -89,11 +90,26 @@ start_webcam_overlay() {
|
||||
--title="WebcamOverlay" --wayland-app-id="WebcamOverlay-$WEBCAM_SIZE" \
|
||||
--no-border --no-audio --no-osc --osd-level=0 \
|
||||
--really-quiet &>/dev/null &
|
||||
sleep 1
|
||||
|
||||
# The move has to settle before gpu-screen-recorder starts, or the camera is
|
||||
# recorded sliding into its corner. Waiting for the map is what the blind
|
||||
# second was partly guessing at, so the remainder is trimmed to hold the
|
||||
# pre-capture delay where it was: starting later costs the first words spoken.
|
||||
local waited=0
|
||||
while ((waited < 40)) && ! hyprctl clients -j | jq -e 'any(.[]; .title == "WebcamOverlay")' >/dev/null 2>&1; do
|
||||
sleep 0.05
|
||||
((waited++))
|
||||
done
|
||||
|
||||
[[ ${1:-} == region:* ]] && echo "${1#region:}" >"$REGION_FILE"
|
||||
omarchy-capture-webcam-resize "$WEBCAM_SIZE"
|
||||
|
||||
sleep 0.6
|
||||
}
|
||||
|
||||
cleanup_webcam() {
|
||||
pkill -f "WebcamOverlay" 2>/dev/null
|
||||
rm -f "$REGION_FILE"
|
||||
}
|
||||
|
||||
default_resolution() {
|
||||
@@ -155,7 +171,7 @@ start_screenrecording() {
|
||||
esac
|
||||
fi
|
||||
|
||||
[[ $WEBCAM == "true" ]] && start_webcam_overlay
|
||||
[[ $WEBCAM == "true" ]] && start_webcam_overlay "$target"
|
||||
|
||||
local filename="$OUTPUT_DIR/screenrecording-$(date +'%Y-%m-%d_%H-%M-%S').mp4"
|
||||
local audio_devices=""
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
readonly MARGIN=40
|
||||
readonly REGION_FILE="${XDG_RUNTIME_DIR:-/tmp}/omarchy-screenrecord-region"
|
||||
|
||||
usage() {
|
||||
echo "Usage: omarchy-capture-webcam-resize <smaller|larger|reset|small|medium|large>" >&2
|
||||
@@ -57,13 +58,39 @@ read -r monitor_x monitor_y monitor_width monitor_height < <(
|
||||
|
||||
[[ $monitor_x =~ ^-?[0-9]+$ && $monitor_y =~ ^-?[0-9]+$ && $monitor_width =~ ^[0-9]+$ && $monitor_height =~ ^[0-9]+$ ]] || exit 0
|
||||
|
||||
# Scale the 8:9 portrait presets from monitor height so they occupy the same
|
||||
# Anchor to the recorded region when there is one, so a window picked on a wide
|
||||
# display keeps the camera in its own corner. Full-monitor captures, the portal
|
||||
# backend, and resizes outside a recording publish none and fall back here.
|
||||
anchor_x=$monitor_x
|
||||
anchor_y=$monitor_y
|
||||
anchor_width=$monitor_width
|
||||
anchor_height=$monitor_height
|
||||
|
||||
if [[ -f $REGION_FILE ]] && region=$(<"$REGION_FILE"); then
|
||||
if [[ $region =~ ^([0-9]+)x([0-9]+)\+(-?[0-9]+)\+(-?[0-9]+)$ ]]; then
|
||||
anchor_width=${BASH_REMATCH[1]}
|
||||
anchor_height=${BASH_REMATCH[2]}
|
||||
anchor_x=${BASH_REMATCH[3]}
|
||||
anchor_y=${BASH_REMATCH[4]}
|
||||
fi
|
||||
fi
|
||||
|
||||
# A tall, narrow region can't fit presets scaled from its own height, so cap the
|
||||
# height they scale from to what the width allows — the large preset is the
|
||||
# widest at 3/10 of it. Scaling the ladder as a whole leaves small, medium and
|
||||
# large distinct sizes for smaller and larger to step between.
|
||||
scale_height=$anchor_height
|
||||
available_width=$((anchor_width - 2 * MARGIN))
|
||||
((available_width > 0 && scale_height * 3 / 10 > available_width)) &&
|
||||
scale_height=$((available_width * 10 / 3))
|
||||
|
||||
# Scale the 8:9 portrait presets from that height so they occupy the same
|
||||
# proportion of a 1080p, HiDPI, ultrawide, or 6K recording.
|
||||
small_height=$(((monitor_height * 9 + 25) / 50))
|
||||
small_height=$(((scale_height * 9 + 25) / 50))
|
||||
small_width=$(((small_height * 8 + 4) / 9))
|
||||
medium_height=$(((monitor_height + 2) / 4))
|
||||
medium_height=$(((scale_height + 2) / 4))
|
||||
medium_width=$(((medium_height * 8 + 4) / 9))
|
||||
large_height=$(((monitor_height * 27 + 40) / 80))
|
||||
large_height=$(((scale_height * 27 + 40) / 80))
|
||||
large_width=$(((large_height * 8 + 4) / 9))
|
||||
|
||||
target_width=$current_width
|
||||
@@ -107,11 +134,11 @@ larger)
|
||||
;;
|
||||
esac
|
||||
|
||||
target_x=$((monitor_x + monitor_width - target_width - MARGIN))
|
||||
target_y=$((monitor_y + monitor_height - target_height - MARGIN))
|
||||
target_x=$((anchor_x + anchor_width - target_width - MARGIN))
|
||||
target_y=$((anchor_y + anchor_height - target_height - MARGIN))
|
||||
|
||||
((target_x < monitor_x + MARGIN)) && target_x=$((monitor_x + MARGIN))
|
||||
((target_y < monitor_y + MARGIN)) && target_y=$((monitor_y + MARGIN))
|
||||
((target_x < anchor_x + MARGIN)) && target_x=$((anchor_x + MARGIN))
|
||||
((target_y < anchor_y + MARGIN)) && target_y=$((anchor_y + MARGIN))
|
||||
|
||||
window="address:$address"
|
||||
hypr_dispatch \
|
||||
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# omarchy:summary=Pick files with the desktop file chooser
|
||||
# omarchy:args=[--title <title>] [--multiple]
|
||||
# omarchy:examples=omarchy file select --title "Send with Tailscale" --multiple
|
||||
|
||||
# Python rather than bash, alone among the commands here, because the portal
|
||||
# answers a request with a Response signal addressed to the connection that
|
||||
# asked, and D-Bus delivers a directed signal only to that connection. Every
|
||||
# shell-callable client — gdbus call, busctl call, dbus-send — opens its own
|
||||
# connection and exits before the answer arrives, and gdbus monitor registers
|
||||
# with AddMatch rather than BecomeMonitor, so it never sees one either. Holding
|
||||
# a single connection across both the call and the wait is the whole job, and
|
||||
# bash has no way to hold one.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gio", "2.0")
|
||||
from gi.repository import Gio, GLib
|
||||
|
||||
# A dialog nobody ever answers would otherwise keep this process, and whatever
|
||||
# waits on its output, alive forever.
|
||||
ANSWER_TIMEOUT_SEC = 600
|
||||
|
||||
# Callers act on these: nothing picked is a decision, a chooser that never ran
|
||||
# is a fault, and the two want different handling.
|
||||
EXIT_NOTHING_PICKED = 1
|
||||
EXIT_CHOOSER_FAILED = 2
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
parser.add_argument("--title", default="Select file")
|
||||
parser.add_argument("--multiple", action="store_true")
|
||||
args, unknown = parser.parse_known_args()
|
||||
|
||||
if unknown:
|
||||
print("omarchy-file-select: unknown option %s" % unknown[0], file=sys.stderr)
|
||||
return EXIT_CHOOSER_FAILED
|
||||
|
||||
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
||||
loop = GLib.MainLoop()
|
||||
uris = []
|
||||
|
||||
def on_response(connection, sender, path, interface, signal, params):
|
||||
code, results = params.unpack()
|
||||
if code == 0:
|
||||
uris.extend(results.get("uris", []))
|
||||
loop.quit()
|
||||
|
||||
def subscribe(path):
|
||||
bus.signal_subscribe(
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"org.freedesktop.portal.Request",
|
||||
"Response",
|
||||
path,
|
||||
None,
|
||||
Gio.DBusSignalFlags.NONE,
|
||||
on_response,
|
||||
)
|
||||
|
||||
# The request path is derived from our bus name and the token we pass, so it
|
||||
# can be subscribed to up front. Asking first would race a dialog that gets
|
||||
# answered immediately.
|
||||
token = "omarchy%d" % os.getpid()
|
||||
sender = bus.get_unique_name()[1:].replace(".", "_")
|
||||
predicted = "/org/freedesktop/portal/desktop/request/%s/%s" % (sender, token)
|
||||
subscribe(predicted)
|
||||
|
||||
handle = bus.call_sync(
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.FileChooser",
|
||||
"OpenFile",
|
||||
GLib.Variant("(ssa{sv})", ("", args.title, {
|
||||
"handle_token": GLib.Variant("s", token),
|
||||
"multiple": GLib.Variant("b", args.multiple),
|
||||
})),
|
||||
None,
|
||||
Gio.DBusCallFlags.NONE,
|
||||
-1,
|
||||
None,
|
||||
).unpack()[0]
|
||||
|
||||
# Portals predating the token convention answer on a path of their choosing.
|
||||
if handle != predicted:
|
||||
subscribe(handle)
|
||||
|
||||
GLib.timeout_add_seconds(ANSWER_TIMEOUT_SEC, loop.quit)
|
||||
loop.run()
|
||||
|
||||
for uri in uris:
|
||||
print(GLib.filename_from_uri(uri)[0])
|
||||
|
||||
return 0 if uris else EXIT_NOTHING_PICKED
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
sys.exit(main())
|
||||
except GLib.Error as error:
|
||||
print("omarchy-file-select: %s" % error.message, file=sys.stderr)
|
||||
sys.exit(EXIT_CHOOSER_FAILED)
|
||||
+1
-38
@@ -53,37 +53,6 @@ log_first_run() {
|
||||
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >>"$FIRST_RUN_LOG"
|
||||
}
|
||||
|
||||
notification_server_ready() {
|
||||
if omarchy-cmd-present gdbus; then
|
||||
gdbus call --session \
|
||||
--dest org.freedesktop.Notifications \
|
||||
--object-path /org/freedesktop/Notifications \
|
||||
--method org.freedesktop.Notifications.GetServerInformation >/dev/null 2>&1
|
||||
elif omarchy-cmd-present busctl; then
|
||||
busctl --user call \
|
||||
org.freedesktop.Notifications \
|
||||
/org/freedesktop/Notifications \
|
||||
org.freedesktop.Notifications \
|
||||
GetServerInformation >/dev/null 2>&1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_notifications() {
|
||||
omarchy-cmd-present omarchy-shell || return 0
|
||||
|
||||
for _ in {1..100}; do
|
||||
if omarchy-shell notifications ping >/dev/null 2>&1 && notification_server_ready; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
log_first_run "Timed out waiting for notification service; continuing"
|
||||
return 0
|
||||
}
|
||||
|
||||
run_first_run_step() {
|
||||
local name="$1"
|
||||
shift
|
||||
@@ -98,12 +67,6 @@ run_first_run_step() {
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_notifications
|
||||
|
||||
run_first_run_step "enable migration notification watcher" \
|
||||
systemctl --user enable --now omarchy-update-user-notify.path
|
||||
run_first_run_step "notify about pending migrations" omarchy-migrate-notify
|
||||
|
||||
run_first_run_step "install Voxtype post-update hook" \
|
||||
omarchy-hook-install post-update "$OMARCHY_PATH/install/user/first-run/install-voxtype.hook"
|
||||
run_first_run_step "install fingerprint setup post-update hook" \
|
||||
@@ -118,7 +81,7 @@ run_first_run_step "set GTK primary paste" \
|
||||
run_first_run_step "apply speaker tuning" \
|
||||
bash "$OMARCHY_PATH/install/user/first-run/audio-tuning.sh"
|
||||
|
||||
wait_for_notifications
|
||||
omarchy-notification-wait || log_first_run "Timed out waiting for notification service; continuing"
|
||||
run_first_run_step "show welcome notification" \
|
||||
bash "$OMARCHY_PATH/install/user/first-run/welcome.sh"
|
||||
# The first-run notification scripts register action callbacks in background
|
||||
|
||||
@@ -13,6 +13,9 @@ sudo tailscale up --accept-routes
|
||||
echo -e "\nAllowing $USER to manage Tailscale..."
|
||||
sudo tailscale set --operator="$USER"
|
||||
|
||||
echo -e "\nReceiving Taildrop files in $HOME/Downloads..."
|
||||
systemctl --user enable --now omarchy-tailscale-receive.service
|
||||
|
||||
echo -e "\nAdding Tailscale to the bar..."
|
||||
omarchy-bar-plugin add omarchy.tailscale
|
||||
|
||||
|
||||
+3
-2
@@ -96,6 +96,7 @@ while IFS=$'\t' read -r name file marker; do
|
||||
fi
|
||||
done < <(migration_entries)
|
||||
|
||||
# Clear notifications queued while an update was installing migrations. The
|
||||
# substring matches both the current and legacy notification titles.
|
||||
# Clear a login-time notification the user left sitting there and then resolved
|
||||
# by running migrations some other way. The substring matches both the current
|
||||
# and legacy notification titles.
|
||||
omarchy-notification-dismiss "Omarchy Migrations" >/dev/null 2>&1 || true
|
||||
|
||||
@@ -15,11 +15,17 @@ fi
|
||||
|
||||
notify_command=$(printf 'if [[ -n $(omarchy-notification-send -u critical -g "Pending Omarchy Migrations" %q -a) ]]; then omarchy-launch-floating-terminal-with-presentation omarchy-migrate; fi' "$message")
|
||||
|
||||
if omarchy-cmd-present systemd-run; then
|
||||
unit="omarchy-migrations-notification-$(date +%Y%m%d%H%M%S)"
|
||||
systemd-run --user --scope --unit="$unit" bash -lc "$notify_command" >/dev/null 2>&1 && exit 0
|
||||
fi
|
||||
# This runs from omarchy-migrate-notify.service at graphical-session.target,
|
||||
# which the session can reach before the shell has claimed
|
||||
# org.freedesktop.Notifications. Without the wait the toast is sent into the
|
||||
# void and the user never learns about their pending migrations.
|
||||
omarchy-notification-wait || true
|
||||
|
||||
unit="omarchy-migrations-notification-$(date +%Y%m%d%H%M%S)"
|
||||
systemd-run --user --scope --unit="$unit" bash -lc "$notify_command" >/dev/null 2>&1 && exit 0
|
||||
|
||||
# Reached when there is no user manager to run the scope under, such as a
|
||||
# non-graphical shell, so fall back to telling the user in the terminal.
|
||||
print_pending_migrations() {
|
||||
echo "Omarchy has pending migrations. Run omarchy-migrate in a terminal to apply them:"
|
||||
while IFS= read -r migration; do
|
||||
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Wait for the desktop notification server to accept notifications
|
||||
# omarchy:args=[timeout-seconds]
|
||||
# omarchy:hidden=true
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
timeout=${1:-10}
|
||||
|
||||
notification_server_ready() {
|
||||
busctl --user call \
|
||||
org.freedesktop.Notifications \
|
||||
/org/freedesktop/Notifications \
|
||||
org.freedesktop.Notifications \
|
||||
GetServerInformation >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# The shell has to be up to serve the IPC, and it has to have claimed the
|
||||
# notification bus name before notify-send has anywhere to deliver.
|
||||
attempts=$((timeout * 10))
|
||||
while (( attempts > 0 )); do
|
||||
if omarchy-shell notifications ping >/dev/null 2>&1 && notification_server_ready; then
|
||||
exit 0
|
||||
fi
|
||||
attempts=$((attempts - 1))
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
exit 1
|
||||
@@ -4,6 +4,7 @@
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
tailscale down 2>/dev/null || true
|
||||
systemctl --user disable --now omarchy-tailscale-receive.service 2>/dev/null || true
|
||||
sudo systemctl disable --now tailscaled.service 2>/dev/null || true
|
||||
omarchy-bar-plugin remove omarchy.tailscale
|
||||
omarchy-webapp-remove "Tailscale" 2>/dev/null || true
|
||||
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Save incoming Taildrop files and announce them
|
||||
# omarchy:args=[--once] [directory]
|
||||
# omarchy:examples=omarchy tailscale receive | omarchy tailscale receive --once ~/Desktop
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
once=false
|
||||
if [[ ${1:-} == "--once" ]]; then
|
||||
once=true
|
||||
shift
|
||||
fi
|
||||
|
||||
dir="${1:-${XDG_DOWNLOAD_DIR:-$HOME/Downloads}}"
|
||||
|
||||
# Taildrop lands in a staging directory next door rather than straight in the
|
||||
# downloads directory: waiting for a delivery can take hours, and everything
|
||||
# else that shows up meanwhile is somebody else's file. Same filesystem, so
|
||||
# handing the finished file over is a rename.
|
||||
staging="$dir/.omarchy-taildrop"
|
||||
mkdir -p "$staging"
|
||||
|
||||
# Take the name by linking to it rather than by looking and then renaming.
|
||||
# link(2) refuses an existing name, so nothing can land on the chosen one in
|
||||
# the gap between the two. Staging shares the filesystem with the downloads
|
||||
# directory, so the link always resolves and unlinking the staged name
|
||||
# finishes the move. Prints the name it took.
|
||||
claim_path() {
|
||||
local staged="$1" name="${staged##*/}" base ext candidate index=0
|
||||
|
||||
base="${name%.*}"
|
||||
ext="${name#"$base"}"
|
||||
[[ -z $base ]] && { base="$name"; ext=""; }
|
||||
|
||||
while (( index < 1000 )); do
|
||||
if (( index == 0 )); then
|
||||
candidate="$dir/$name"
|
||||
else
|
||||
candidate="$dir/$base-$index$ext"
|
||||
fi
|
||||
|
||||
if ln -- "$staged" "$candidate" 2>/dev/null; then
|
||||
rm -f -- "$staged"
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Only a taken name is worth another spin. Anything else failed the link
|
||||
# itself, and the file keeps its place in staging for the next run.
|
||||
[[ -e $candidate ]] || return 1
|
||||
|
||||
((index++))
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
announce() {
|
||||
local path="$1"
|
||||
local name="${path##*/}"
|
||||
local args=("Received $name" "Saved to ${dir/#$HOME/~}")
|
||||
|
||||
case "${name,,}" in
|
||||
*.png | *.jpg | *.jpeg | *.gif | *.webp | *.avif | *.bmp | *.tif | *.tiff)
|
||||
args+=(--image "$path")
|
||||
;;
|
||||
*)
|
||||
args+=(-g )
|
||||
;;
|
||||
esac
|
||||
|
||||
# Clicking the notification opens the file, so this waits for the toast to
|
||||
# go away. Callers background it to keep receiving in the meantime.
|
||||
if [[ -n $(omarchy-notification-send "${args[@]}" -a) ]]; then
|
||||
xdg-open "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
deliver() {
|
||||
local staged target
|
||||
|
||||
while IFS= read -r staged; do
|
||||
target=$(claim_path "$staged") || continue
|
||||
announce "$target" &
|
||||
done < <(find "$staging" -mindepth 1 -maxdepth 1)
|
||||
}
|
||||
|
||||
# Anything left staged by an interrupted run still deserves delivering.
|
||||
deliver
|
||||
|
||||
while true; do
|
||||
if ! tailscale file get --wait --conflict=rename "$staging"; then
|
||||
$once && exit 1
|
||||
sleep 10
|
||||
continue
|
||||
fi
|
||||
|
||||
deliver
|
||||
$once && exit 0
|
||||
done
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Send files to a machine on your tailnet with Taildrop
|
||||
# omarchy:args=<machine> [file...]
|
||||
# omarchy:examples=omarchy tailscale send dhh-fd | omarchy tailscale send dhh-fd ~/Downloads/notes.pdf
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if (($# < 1)); then
|
||||
echo "Usage: omarchy-tailscale-send <machine> [file...]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
machine="$1"
|
||||
shift
|
||||
|
||||
# Address the machine by whatever name we were handed, but talk about it by
|
||||
# its short name, so a MagicDNS name does not spill into every message.
|
||||
name="${machine%%.*}"
|
||||
|
||||
files=("$@")
|
||||
|
||||
if ((${#files[@]} == 0)); then
|
||||
# Command substitution so the chooser's exit status survives: reading it
|
||||
# through a process substitution reports success for a chooser that never
|
||||
# opened, which is indistinguishable here from someone deciding not to send.
|
||||
picked=$(omarchy-file-select --title "Send to $name" --multiple) || status=$?
|
||||
|
||||
if ((${status:-0} > 1)); then
|
||||
omarchy-notification-send -g "" -u critical "Could not send to $name" \
|
||||
"The file chooser did not open"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
readarray -t files <<<"$picked"
|
||||
[[ -n $picked ]] || exit 0
|
||||
fi
|
||||
|
||||
if ((${#files[@]} == 1)); then
|
||||
what=$(basename "${files[0]}")
|
||||
else
|
||||
what="${#files[@]} files"
|
||||
fi
|
||||
|
||||
if error=$(tailscale file cp --update-interval=0 -- "${files[@]}" "$machine:" 2>&1); then
|
||||
omarchy-notification-send -g "" "Sent to $name" "$what"
|
||||
else
|
||||
omarchy-notification-send -g "" -u critical "Could not send to $name" "${error:-Taildrop transfer failed}"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user