Compare commits
No commits in common. "496eacca674e91225b269559731bdbc023a47ddc" and "353fbd99ec9832bae723fe19d3e677745e6be5fc" have entirely different histories.
496eacca67
...
353fbd99ec
|
|
@ -9,7 +9,6 @@ import { getIconName } from "./utils";
|
||||||
import Wp from "gi://AstalWp";
|
import Wp from "gi://AstalWp";
|
||||||
import Battery from "gi://AstalBattery";
|
import Battery from "gi://AstalBattery";
|
||||||
import GLib from "gi://GLib";
|
import GLib from "gi://GLib";
|
||||||
import Gio from "gi://Gio";
|
|
||||||
|
|
||||||
const battery = Battery.get_default();
|
const battery = Battery.get_default();
|
||||||
const sensorsAvailable = await execAsync(["sensors"])
|
const sensorsAvailable = await execAsync(["sensors"])
|
||||||
|
|
@ -183,53 +182,34 @@ function Right() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Papirus encodes the battery level as a 35%-opacity overlay inside its
|
|
||||||
// *-symbolic icons. GTK4's symbolic recoloring flattens that opacity, so every
|
|
||||||
// level renders as a solid, full battery. The Papirus *-symbolic icons are just
|
|
||||||
// symlinks to non-symbolic panel SVGs which keep the opacity when rendered
|
|
||||||
// normally — so we resolve each name to that underlying file and load it
|
|
||||||
// directly (as a non-symbolic file icon), which renders the correct level.
|
|
||||||
function batteryGicon(): Gio.Icon {
|
|
||||||
const percentage = battery.percentage;
|
|
||||||
const thresholds = [...Array(11).keys()].map((i) => i * 10);
|
|
||||||
const icon = thresholds.find((threshold) => threshold >= percentage * 100);
|
|
||||||
const name = battery.charging
|
|
||||||
? `battery-level-${icon}-${percentage >= 0.99 ? "charged" : "charging"}-symbolic`
|
|
||||||
: `battery-level-${icon}-symbolic`;
|
|
||||||
const symbolic = Gtk.IconTheme.get_for_display(Gdk.Display.get_default()!)
|
|
||||||
.lookup_icon(name, null, 16, 1, Gtk.TextDirection.NONE, 0)
|
|
||||||
.get_file();
|
|
||||||
if (!symbolic) return Gio.ThemedIcon.new(name);
|
|
||||||
try {
|
|
||||||
const target = symbolic
|
|
||||||
.query_info(
|
|
||||||
"standard::symlink-target",
|
|
||||||
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
|
|
||||||
null,
|
|
||||||
)
|
|
||||||
.get_symlink_target();
|
|
||||||
if (target) {
|
|
||||||
return new Gio.FileIcon({
|
|
||||||
file: symbolic.get_parent()!.resolve_relative_path(target),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
return new Gio.FileIcon({ file: symbolic });
|
|
||||||
}
|
|
||||||
|
|
||||||
function BatteryIcon(): JSX.Element {
|
function BatteryIcon(): JSX.Element {
|
||||||
if (battery.get_state() == 0) return <box />;
|
if (battery.get_state() == 0) return <box />;
|
||||||
// AstalBattery does not emit notify::percentage/charging/state on this
|
let batteryPercentage = createBinding(battery, "percentage");
|
||||||
// system, so a signal binding stays frozen at its initial value (e.g.
|
|
||||||
// showing "charged" forever). Poll the live values instead.
|
|
||||||
const gicon = createPoll<Gio.Icon>(batteryGicon(), 10000, batteryGicon);
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="battery-item"
|
class="battery-item"
|
||||||
onClicked={() => execAsync(["gnome-power-statistics"])}
|
onClicked={() => execAsync(["gnome-power-statistics"])}
|
||||||
>
|
>
|
||||||
<box>
|
<box>
|
||||||
<image gicon={gicon} />
|
<With value={batteryPercentage}>
|
||||||
|
{(percentage) => {
|
||||||
|
const thresholds = [...Array(11).keys()].map((i) => i * 10);
|
||||||
|
const icon = thresholds.find(
|
||||||
|
(threshold) => threshold >= percentage * 100,
|
||||||
|
);
|
||||||
|
const charging_name =
|
||||||
|
battery.percentage >= 0.99 ? "charged" : "charging";
|
||||||
|
return (
|
||||||
|
<image
|
||||||
|
iconName={
|
||||||
|
battery.charging
|
||||||
|
? `battery-level-${icon}-${charging_name}-symbolic`
|
||||||
|
: `battery-level-${icon}-symbolic`
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</With>
|
||||||
</box>
|
</box>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,4 @@
|
||||||
if [ -n "$REMOTE_PATH" ]; then
|
if [ "$PREVIOUS_SESSION_ID" != "" ]; then
|
||||||
# Fresh `s <host>` connection: the initial working directory is handed over in
|
|
||||||
# REMOTE_PATH (an env var is the only channel that survives waypipe's command
|
|
||||||
# transport intact — shell grouping like `cd …; exec …` does not). cd into it,
|
|
||||||
# then unset so it does NOT leak to child shells: a lingering exported
|
|
||||||
# REMOTE_PATH is what made every new zellij pane jump back to this same path
|
|
||||||
# instead of inheriting the pane's own cwd.
|
|
||||||
[ -d "$REMOTE_PATH" ] && cd "$REMOTE_PATH"
|
|
||||||
unset REMOTE_PATH
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$PREVIOUS_SESSION_ID" ]; then
|
|
||||||
# Spawned as a new window/terminal for an existing remote session (by the
|
# Spawned as a new window/terminal for an existing remote session (by the
|
||||||
# niri/hyprland keybind). Inherit that session's working directory: find a
|
# niri/hyprland keybind). Inherit that session's working directory: find a
|
||||||
# live shell whose SSH_SESSION_ID (carried in its environment) matches and
|
# live shell whose SSH_SESSION_ID (carried in its environment) matches and
|
||||||
|
|
@ -23,16 +12,7 @@ if [ -n "$PREVIOUS_SESSION_ID" ]; then
|
||||||
# fail quietly into the pipe instead of raising a shell error.
|
# fail quietly into the pipe instead of raising a shell error.
|
||||||
if cat "$_environ" 2>/dev/null | tr '\0' '\n' | grep -Fxq "SSH_SESSION_ID=$PREVIOUS_SESSION_ID"; then
|
if cat "$_environ" 2>/dev/null | tr '\0' '\n' | grep -Fxq "SSH_SESSION_ID=$PREVIOUS_SESSION_ID"; then
|
||||||
[ "$(cat /proc/$_pid/comm 2>/dev/null)" = zsh ] || continue
|
[ "$(cat /proc/$_pid/comm 2>/dev/null)" = zsh ] || continue
|
||||||
# SSH_SESSION_ID is exported, so transient `zsh -c …` subshells (prompt
|
|
||||||
# hooks, zellij helpers) inherit it too — but they aren't real session
|
|
||||||
# panes and often sit at /. Skip anything invoked in command mode; only
|
|
||||||
# interactive login/pane shells should count.
|
|
||||||
cat /proc/$_pid/cmdline 2>/dev/null | tr '\0' '\n' | grep -Fxq -- -c && continue
|
|
||||||
_c=$(readlink -e /proc/$_pid/cwd 2>/dev/null) || continue
|
_c=$(readlink -e /proc/$_pid/cwd 2>/dev/null) || continue
|
||||||
# A login shell still sitting at / is a helper forked during startup
|
|
||||||
# (e.g. p10k) that never ran the REMOTE_PATH cd, not a shell the user
|
|
||||||
# works in. Real panes have always cd'd somewhere; skip root.
|
|
||||||
[ "$_c" = / ] && continue
|
|
||||||
if [ -n "$_c" ] && [ "$_pid" -gt "$_best_pid" ]; then
|
if [ -n "$_c" ] && [ "$_pid" -gt "$_best_pid" ]; then
|
||||||
_best_pid=$_pid
|
_best_pid=$_pid
|
||||||
_prev_cwd=$_c
|
_prev_cwd=$_c
|
||||||
|
|
@ -50,25 +30,18 @@ function run_waypipe() {
|
||||||
fi
|
fi
|
||||||
local SSH_SESSION_ID=$RANDOM REMOTE_PATH
|
local SSH_SESSION_ID=$RANDOM REMOTE_PATH
|
||||||
# Map the current path relative to $HOME so it resolves to the remote user's
|
# Map the current path relative to $HOME so it resolves to the remote user's
|
||||||
# home even when the username differs (the remote shell expands $HOME while
|
# home even when the username differs (the remote shell expands $HOME).
|
||||||
# parsing the command below).
|
|
||||||
if [[ "$PWD" == "$HOME" || "$PWD" == "$HOME"/* ]]; then
|
if [[ "$PWD" == "$HOME" || "$PWD" == "$HOME"/* ]]; then
|
||||||
REMOTE_PATH="\$HOME${PWD#$HOME}"
|
REMOTE_PATH="\$HOME${PWD#$HOME}"
|
||||||
else
|
else
|
||||||
REMOTE_PATH="$PWD"
|
REMOTE_PATH="$PWD"
|
||||||
fi
|
fi
|
||||||
# Hand off the initial cwd and session token as a plain `env VAR=val … zsh`
|
# cd into the mirrored path before the interactive shell starts, so the cwd
|
||||||
# argv. This must stay free of shell grouping/metacharacters: waypipe rejoins
|
# is never carried in an inherited env var (which would leak to every child
|
||||||
# and re-splits the command on whitespace and runs the leading word as a
|
# shell and override the cwd of new zellij panes). SSH_SESSION_ID stays in
|
||||||
# program, so `env` (a real binary) works but `cd …; exec …` would not.
|
# the environment on purpose: it is the token the WM keybind above uses to
|
||||||
#
|
# locate this session's shells later.
|
||||||
# SSH_SESSION_ID is placed in TWO environments on purpose:
|
waypipe -n ssh -t "$1" "cd \"$REMOTE_PATH\" 2>/dev/null; exec env SSH_SESSION_ID=$SSH_SESSION_ID zsh --login"
|
||||||
# - the leading prefix exports it into the LOCAL waypipe/ssh process, where
|
|
||||||
# the niri/hyprland "duplicate window" keybind reads it out of
|
|
||||||
# /proc/<ssh-pid>/environ to learn which session to clone;
|
|
||||||
# - the `env SSH_SESSION_ID=…` sets it on the REMOTE zsh, where the scan
|
|
||||||
# block above locates this session's shells by matching it.
|
|
||||||
SSH_SESSION_ID=$SSH_SESSION_ID waypipe -n ssh -t "$1" env REMOTE_PATH=\"$REMOTE_PATH\" SSH_SESSION_ID=$SSH_SESSION_ID zsh --login
|
|
||||||
}
|
}
|
||||||
|
|
||||||
alias s='run_waypipe'
|
alias s='run_waypipe'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue