Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cafaede
open PR
kRHYME7 May 7, 2026
9d9b71b
Merge branch 'master' into rc
kRHYME7 May 7, 2026
ed0e56f
Merge branch 'master' into rc
kRHYME7 May 11, 2026
fb42c88
Merge branch 'master' into rc
kRHYME7 May 14, 2026
e39e818
chore: Release - dev → rc (#1732)
github-actions[bot] Jul 27, 2026
37d7ebb
chore: Release - dev → rc (#1836)
github-actions[bot] Jul 27, 2026
1ea67fb
Remove Configs/.config/hypr/windowrules.conf - no longer needed (reso…
kRHYME7 Jul 27, 2026
09e7450
Changelog
kRHYME7 Jul 27, 2026
f368ab1
Merge branch 'master' into rc
kRHYME7 Jul 28, 2026
a9d9aa6
typo
kRHYME7 Jul 28, 2026
6d0d476
Merge branch 'rc' of https://github.com/HyDE-Project/HyDE into rc
kRHYME7 Jul 28, 2026
62f3404
fix (fonts) cantarell font
kRHYME7 Jul 28, 2026
a8ef508
Fix: Ship hyde's wallbash template
kRHYME7 Jul 28, 2026
05896cf
fix changelog
kRHYME7 Jul 28, 2026
3e921a8
fix! missin lua.dcol
kRHYME7 Jul 28, 2026
94fb7fc
Merge branch 'master' into rc
kRHYME7 Jul 28, 2026
04df289
pr
kRHYME7 Jul 28, 2026
41eb7b1
Merge branch 'master' into rc
kRHYME7 Jul 28, 2026
7b6045c
Merge branch 'rc' of https://github.com/HyDE-Project/HyDE into rc
kRHYME7 Jul 28, 2026
45c9ca0
Merge branch 'master' into rc
kRHYME7 Jul 28, 2026
fd4905e
Merge branch 'master' into rc
kRHYME7 Jul 29, 2026
ac4d075
Merge branch 'master' into rc
kRHYME7 Jul 30, 2026
27bf8e3
chore: Release - dev → rc (#1841)
github-actions[bot] Jul 30, 2026
4ec1509
chore: Release - dev → rc (#1899)
github-actions[bot] Jul 31, 2026
e44ca77
chore: Release - dev → rc (#1905)
github-actions[bot] Aug 1, 2026
dd162fd
Merge branch 'master' into rc
kRHYME7 Aug 1, 2026
f3fda16
chore: Release - dev → rc (#1910)
github-actions[bot] Aug 2, 2026
fd09c57
Merge branch 'master' into rc
kRHYME7 Aug 2, 2026
e427339
chore: Release - dev → rc (#1924)
github-actions[bot] Aug 3, 2026
7b5f9c2
chore: Release - dev → rc (#1928)
github-actions[bot] Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ jobs:
- name: Install test dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends lua5.4 shellcheck
sudo apt-get install -y --no-install-recommends lua5.4 shellcheck fish
sudo ln -sf /usr/bin/lua5.4 /usr/local/bin/lua
sudo ln -sf /usr/bin/luac5.4 /usr/local/bin/luac

- name: Report tool versions
run: |
lua -v
shellcheck --version
fish --version
python3 --version

- name: Run the test suite
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- Hyprland: dropped the legacy hyprlang dot, the files it deployed no longer exist

### Fixed
- Fish: `~/.local/bin` reaches `PATH` again, so `hyde-shell` and everything the keybinds call resolve in a fish session; the directory was handed to `fish_add_path` joined to the rest of `PATH` by colons, which is one path that exists nowhere and is dropped without a word
- Weather Applet: Avoid crashes from unknown weather codes or unavailable wttr.in responses.
- Core: an install no longer ends at the Lua step on a machine that cannot build `lgi`; the introspection headers it compiles against are declared as a dependency, and an optional rock that still fails is reported and skipped instead of taking the run down before any dotfile is deployed
- Hyprland: a session on a machine with a discrete NVIDIA GPU no longer comes up with a timed-out configuration; driver detection reads `/proc` and `/sys`, and the library directories are found by opening the candidate paths, so the budget Hyprland allows the whole configuration is no longer spent waiting on a subprocess
- Core: an install deploys the cursor dots again instead of aborting on an existing theme file; the locked `deez-dots` revision predated the extraction fix, so every install kept running the defect the fix had already closed
- Core: Pyprland commands keep their arguments when `nc`, `socat`, and `ncat` are unavailable, so commands such as `hyde-shell pypr toggle console` work through the CLI fallback again
- Hyprland: a session started without `XDG_CONFIG_HOME`, such as one launched from a TTY, no longer dies with a Lua error before the first window; the unset variable is treated as unset instead of being pasted into a path
- Hyprland: a home directory containing an apostrophe no longer makes the session load its libraries from the system directory instead of the user's own, and an empty `XDG_RUNTIME_DIR` reads as unset rather than resolving against the working directory
- Waybar: the theme module and the HyDE menu call `theme.switch` again; `themeswitch` was removed and the calls failed outright
Expand Down
5 changes: 4 additions & 1 deletion Configs/.config/fish/conf.d/hyde.fish
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ end
end
end

fish_add_path $HOME/.local/bin:$PATH
# One directory per argument: fish_add_path does not split on ":", so a
# colon-joined string is a single path that exists nowhere, and a path that
# does not exist is dropped without a word.
fish_add_path $HOME/.local/bin


if type -q starship
Expand Down
13 changes: 13 additions & 0 deletions Configs/.config/fish/functions/_hyde_editor.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# The editor a selection opens in, discovered rather than chosen. Mirrors
# Configs/.config/zsh/functions/fzf.zsh: the user's own answer first, then an
# order of discovery over what this machine already has. HyDE installs none of
# them.
function _hyde_editor -d "Print the first usable editor on this machine"
for candidate in $EDITOR $VISUAL nvim vim helix hx nano micro emacs
if test -n "$candidate"; and type -q -- $candidate
echo $candidate
return 0
end
end
return 1
end
7 changes: 6 additions & 1 deletion Configs/.config/fish/functions/fzf/ffe.fish
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ function ffe -d "Find file with fzf and open in Editor"
set selected_file (find . -maxdepth $max_depth -type f 2>/dev/null | fzf $fzf_options)

if test -n "$selected_file"; and test -f "$selected_file"
set editor (_hyde_editor)
if test -z "$editor"
echo "No editor found. Install one, or set EDITOR in ~/.config/fish/user.fish."
return 1
end
cd (dirname $selected_file)
nvim (basename $selected_file)
$editor (basename $selected_file)
else
return 1
end
Expand Down
7 changes: 6 additions & 1 deletion Configs/.config/fish/functions/fzf/ffec.fish
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ function ffec -d "Fuzzy search by file content and open in Editor"
set selected_file (grep -irl -- "$grep_pattern" ./ 2>/dev/null | fzf $fzf_options)

if test -n "$selected_file"
set editor (_hyde_editor)
if test -z "$editor"
echo "No editor found. Install one, or set EDITOR in ~/.config/fish/user.fish."
return 1
end
cd (dirname $selected_file)
nvim (basename $selected_file)
$editor (basename $selected_file)
else
echo "No file selected or search returned no results."
end
Expand Down
2 changes: 1 addition & 1 deletion Configs/.config/fish/user.fish
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
# Add your configurations here

# set EDITOR nvim
set EDITOR code
# set EDITOR code

# set aurhelper yay
set aurhelper yay
Expand Down
16 changes: 16 additions & 0 deletions Configs/.config/hypr/hyprland.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
-- Hyprland loads this file when it is started without a config, and it prefers
-- it over hyprland.conf. HyDE loads it too, last, as the override layer below.
-- The block keeps the two apart: hyde.lua sets `hyde` on its first line, so it
-- runs only when this file is the entry point and HyDE has not been loaded.
-- Removing it leaves a session with a cursor and nothing else.
if not hyde then
local share = os.getenv("XDG_DATA_HOME") or (os.getenv("HOME") .. "/.local/share")
local entry = share .. "/hypr/hyde.lua"
local handle = io.open(entry, "r")
if not handle then
error("HyDE is not installed at " .. entry .. ". Run install.sh -r, or point Hyprland at your own config.")
end
handle:close()
dofile(entry)
end

-- Your Hyprland configuration. HyDE never overwrites this file.
--
-- It loads after HyDE's own binds, so settings here take precedence. Replacing
Expand Down
2 changes: 1 addition & 1 deletion Configs/.config/zsh/.zshrc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@
#  This is your file 
# Add your configurations here
# export EDITOR=nvim
export EDITOR=code
# export EDITOR=code

# unset -f command_not_found_handler # Uncomment to prevent searching for commands not found in package manager
35 changes: 27 additions & 8 deletions Configs/.config/zsh/functions/fzf.zsh
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
# best fzf aliases ever

# The editor a selection opens in, discovered rather than chosen. The user's own
# answer comes first; the rest is an order of discovery over what this machine
# already has. HyDE installs none of them, so a machine with no editor gets a
# message naming the fix instead of a guess.
_hyde_editor() {
local candidate
for candidate in "$EDITOR" "$VISUAL" nvim vim helix hx nano micro emacs; do
[[ -n "$candidate" ]] || continue
if command -v "$candidate" &>/dev/null; then
printf '%s' "$candidate"
return 0
fi
done
return 1
}

_fuzzy_change_directory() {
local initial_query="$1"
local selected_dir
Expand Down Expand Up @@ -34,11 +51,12 @@ _fuzzy_edit_search_file_content() {
selected_file=$(grep -irl "${1:-}" ./ | fzf "${fzf_options[@]}")

if [[ -n "$selected_file" ]]; then
if command -v "$EDITOR" &>/dev/null; then
"$EDITOR" "$selected_file"
local editor
if editor=$(_hyde_editor); then
"$editor" "$selected_file"
else
echo "EDITOR is not specified. using vim. (you can export EDITOR in ~/.zshrc)"
vim "$selected_file"
echo "No editor found. Install one, or export EDITOR from \$ZDOTDIR/user.zsh or \$HOME/.user.zsh."
return 1
fi

else
Expand All @@ -61,11 +79,12 @@ _fuzzy_edit_search_file() {
selected_file=$(find . -maxdepth $max_depth -type f 2>/dev/null | fzf "${fzf_options[@]}")

if [[ -n "$selected_file" && -f "$selected_file" ]]; then
if command -v "$EDITOR" &>/dev/null; then
"$EDITOR" "$selected_file"
local editor
if editor=$(_hyde_editor); then
"$editor" "$selected_file"
else
echo "EDITOR is not specified. using vim. (you can export EDITOR in ~/.zshrc)"
vim "$selected_file"
echo "No editor found. Install one, or export EDITOR from \$ZDOTDIR/user.zsh or \$HOME/.user.zsh."
return 1
fi
else
return 1
Expand Down
11 changes: 6 additions & 5 deletions Configs/.local/bin/hyde-shell
Original file line number Diff line number Diff line change
Expand Up @@ -377,11 +377,12 @@ run_pypr() {
fi

if [[ -S "$socket_path" ]] && pgrep -u "$USER" pypr >/dev/null 2>&1; then
message="${*:-"help"}"
if ! printf "%s" "${message[@]}" | nc -N -U "$socket_path" 2>/dev/null; then
if ! printf "%s" "${message[@]}" | socat - UNIX-CONNECT:"$socket_path" 2>/dev/null; then
if ! printf "%s" "${message[@]}" | ncat -U "$socket_path" 2>/dev/null; then
if ! pypr "${message[@]}"; then
local -a msg=("$@")
((${#msg[@]} == 0)) && msg=("help")
if ! printf '%s\n' "${msg[*]}" | nc -N -U "$socket_path" 2>/dev/null; then
if ! printf '%s\n' "${msg[*]}" | socat - UNIX-CONNECT:"$socket_path" 2>/dev/null; then
if ! printf '%s\n' "${msg[*]}" | ncat -U "$socket_path" 2>/dev/null; then
if ! pypr "${msg[@]}"; then
print_log -sec "pypr" "Error communicating with socket: $socket_path"
exit 1
fi
Expand Down
2 changes: 1 addition & 1 deletion Configs/.local/lib/hyde/pyutils/lua_env.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{"name": "dkjson", "version": "2.11-1"},
{"name": "luasocket", "version": "3.1.0-1"},
{"name": "luafilesystem", "version": "1.9.0-1"},
"https://raw.githubusercontent.com/lgi-devs/lgi/master/lgi-scm-1.rockspec"
{"name": "https://raw.githubusercontent.com/lgi-devs/lgi/master/lgi-scm-1.rockspec", "optional": true}
],
"snapshot_exclude": [
{"name": "luasec", "version": "1.3.2-1"},
Expand Down
47 changes: 39 additions & 8 deletions Configs/.local/lib/hyde/pyutils/lua_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,16 @@ def load_bootstrap_config():
return install, snapshot_exclude

def _parse_pkg_entry(entry):
"""Reads one package entry as (name, version, optional).

An entry may mark itself optional, which decides what happens when the
rock refuses to build: a required one stops the caller, an optional one
is reported and skipped.
"""
if isinstance(entry, dict) and "name" in entry:
return (entry["name"], entry.get("version"))
return (entry["name"], entry.get("version"), bool(entry.get("optional")))
elif isinstance(entry, str):
return (entry, None)
return (entry, None, False)
return None

if isinstance(config, dict):
Expand All @@ -79,7 +85,7 @@ def _parse_pkg_entry(entry):
if exclude_cfg is None:
exclude_cfg = config.get("exclude")

# install: list of (name, version) tuples
# install: list of (name, version, optional) tuples
if isinstance(install_cfg, list):
install = []
for item in install_cfg:
Expand All @@ -97,7 +103,7 @@ def _parse_pkg_entry(entry):
else:
snapshot_exclude = set()
# Always exclude bootstrap_install names
snapshot_exclude = set([name for name, _ in install]) | snapshot_exclude
snapshot_exclude = set([entry[0] for entry in install]) | snapshot_exclude

return install, snapshot_exclude

Expand Down Expand Up @@ -201,6 +207,33 @@ def install_rock(name, version=None, force=False):
run(args)


def install_bootstrap(bootstrap_install, force=False):
"""Installs the bootstrap rocks, skipping the optional ones that fail.

A rock that refuses to build stops the caller only when the bootstrap
config declares it required. lgi is the reason this distinction exists: it
is compiled against the GObject introspection headers, and a system without
them used to take the whole installation down at the Lua step, before a
single dotfile had been deployed.
"""
skipped = []

for name, version, optional in bootstrap_install:
try:
install_rock(name, version, force=force)
except subprocess.CalledProcessError:
if not optional:
raise
skipped.append(name)
print(f"[lua_env] {name} did not build; it is optional, continuing without it")

if skipped:
print(f"[lua_env] {len(skipped)} optional package(s) skipped: {', '.join(skipped)}")
print("[lua_env] install their build dependencies and run 'hyde-shell luainit' to retry")

return skipped


def create_env(force=False):
ensure_system_tools()
print(f"[lua_env] Using system Lua: {LUA_BIN}")
Expand All @@ -209,8 +242,7 @@ def create_env(force=False):

bootstrap_install, _ = load_bootstrap_config()
ensure_state_dir()
for name, version in bootstrap_install:
install_rock(name, version, force=force)
install_bootstrap(bootstrap_install, force=force)

restore_saved_rocks(force=force)

Expand All @@ -236,8 +268,7 @@ def sync_env():
ensure_state_dir()
# Reinstall all bootstrap_install packages to latest or pinned version
bootstrap_install, _ = load_bootstrap_config()
for name, version in bootstrap_install:
install_rock(name, version, force=True)
install_bootstrap(bootstrap_install, force=True)
# Now snapshot only user-installed rocks
snapshot_installed_rocks()

Expand Down
2 changes: 1 addition & 1 deletion Configs/.local/lib/hyde/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 42 additions & 8 deletions Configs/.local/lib/hyde/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class WttrResponse(TypedDict):
WEATHER_CODES = {
**dict.fromkeys(["113"], "☀️ "),
**dict.fromkeys(["116"], "⛅ "),
**dict.fromkeys(["119", "122", "143", "248", "260"], "☁️ "),
**dict.fromkeys(["119", "122", "143", "149", "248", "260"], "☁️ "),
**dict.fromkeys(
[
"176",
Expand Down Expand Up @@ -136,7 +136,7 @@ def load_env_file(filepath: Path) -> None:

def get_weather_icon(weatherinstance: CurrentCondition | HourlyPoint) -> str:
"""Returns the appropriate weather icon based on the weather code."""
return WEATHER_CODES[weatherinstance["weatherCode"]]
return WEATHER_CODES.get(weatherinstance["weatherCode"], "☁️ ")


def get_description(weatherinstance: CurrentCondition | HourlyPoint) -> str:
Expand Down Expand Up @@ -325,6 +325,37 @@ def get_default_locale() -> tuple[str, TempUnit, TimeFormat, WindUnit]:
windspeed_unit: WindUnit = "km/h"


def get_weather_data(url: str, headers: dict[str, str]) -> WttrResponse | None:
try:
response = requests.get(url, timeout=10, headers=headers)
response.raise_for_status()
weather = response.json()
except (requests.exceptions.RequestException, json.decoder.JSONDecodeError):
return None

if not isinstance(weather, dict):
return None

current_condition = weather.get("current_condition")
forecast = weather.get("weather")
nearest_area = weather.get("nearest_area")
if not (
isinstance(current_condition, list)
and len(current_condition) > 0
and isinstance(forecast, list)
and len(forecast) > 0
and isinstance(nearest_area, list)
and len(nearest_area) > 0
):
return None

return cast(WttrResponse, weather)


def print_weather_unavailable() -> None:
print(json.dumps({"text": "Weather --", "tooltip": "Weather unavailable: wttr.in did not return usable data", "class": "error"}))


def main() -> None:
global weather_lang, temp_unit, time_format, windspeed_unit

Expand Down Expand Up @@ -391,12 +422,15 @@ def main() -> None:

# Get the weather data
headers = {"User-Agent": "Mozilla/5.0"}
response = requests.get(url, timeout=10, headers=headers)
try:
weather = cast(WttrResponse, response.json())
except json.decoder.JSONDecodeError:
sys.exit(1)
current_weather = weather["current_condition"][0]
weather = get_weather_data(url, headers)
if weather is None:
print_weather_unavailable()
sys.exit(0)
current_conditions = weather.get("current_condition")
if not current_conditions:
print_weather_unavailable()
sys.exit(0)
current_weather = current_conditions[0]

# Get the data to display
# waybar text
Expand Down
Loading
Loading