Compare commits
30 Commits
switch-to-
...
2da8e1a354
| Author | SHA1 | Date | |
|---|---|---|---|
| 2da8e1a354 | |||
| 48b73c40e9 | |||
| a7aa1b2237 | |||
| 37e8efa795 | |||
| 6847a123d5 | |||
| f28788b71a | |||
| 5aec12cd1f | |||
| 6efa5d599c | |||
| 9248f9b33f | |||
| 252c88b869 | |||
| 63f99ff01a | |||
| 586e4b6320 | |||
| c2b28df404 | |||
| 8d95eeb892 | |||
| d0d3fc23e8 | |||
| 98235a8c18 | |||
| e645ea1641 | |||
| 7824d27555 | |||
| 4bc368dfd9 | |||
| ef0efe4ba8 | |||
| 7ac7ced990 | |||
| f16af76867 | |||
| 9fbffb7558 | |||
| 545f8ab328 | |||
| 4093701fca | |||
| 7c918c3c17 | |||
| 4ae85ca347 | |||
| 653a75cc18 | |||
| 42bae035ee | |||
| e0fcb2988f |
@ -14,6 +14,12 @@ set incsearch
|
||||
set hlsearch
|
||||
set scrolloff=10
|
||||
|
||||
let mapleader = " "
|
||||
let maplocalleader = " "
|
||||
|
||||
" Binds
|
||||
nnoremap gr :action ShowUsages<CR>
|
||||
inoremap <C-e> <Esc>i
|
||||
|
||||
nnoremap <C-w>m :action MoveEditorToOppositeTabGroup<CR>
|
||||
nnoremap <leader>m :action MoveEditorToOppositeTabGroup<CR>
|
||||
|
||||
@ -11,6 +11,7 @@ default_roles:
|
||||
# - ssh
|
||||
- hyprland
|
||||
- ghostty
|
||||
- quickshell
|
||||
|
||||
temp:
|
||||
- docker # Container platform
|
||||
@ -67,4 +68,5 @@ aur_packages:
|
||||
- pgformatter-git
|
||||
# - jetbrains-toolbox
|
||||
|
||||
config_dir: "{{ ansible_facts.env.HOME }}/.config"
|
||||
home_dir: "/home/johannes"
|
||||
config_dir: "/home/johannes/.config"
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
[local]
|
||||
localhost ansible_connection=local ansible_user=johannes
|
||||
localhost ansible_connection=local ansible_user=johannes ansible_python_interpreter=/usr/sbin/python
|
||||
|
||||
17
playbook.yml
17
playbook.yml
@ -2,19 +2,20 @@
|
||||
- name: Setup dotfiles configuration
|
||||
hosts: localhost
|
||||
connection: local
|
||||
become: false
|
||||
become: true
|
||||
become_user: johannes
|
||||
vars:
|
||||
dotfiles_dir: "{{ ansible_env.HOME }}/dotfiles"
|
||||
# roles:
|
||||
# - zsh
|
||||
|
||||
pre_tasks:
|
||||
- name: Create btrfs snapshot before changes
|
||||
shell: |
|
||||
sudo btrfs subvolume snapsho / /.snapshots/before-dotfiles-$(date +%Y%m%d-%H%M%S)
|
||||
tags: [snapshot]
|
||||
become: true
|
||||
become_method: sudo
|
||||
# pre_tasks:
|
||||
# - name: Create btrfs snapshot before changes
|
||||
# shell: |
|
||||
# btrfs subvolume snapshot / /.snapshots/before-dotfiles-$(date +%Y%m%d-%H%M%S)
|
||||
# tags: [snapshot]
|
||||
# become: true
|
||||
# become_method: sudo
|
||||
|
||||
tasks:
|
||||
- name: Install enabled roles
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
# Ctrl+Backspace -> backward-kill-word (send ESC + DEL)
|
||||
# keybind = ctrl+delete=text:\x1b\x7f
|
||||
|
||||
shell-integration = zsh
|
||||
shell-integration-features = cursor,sudo,title
|
||||
|
||||
|
||||
@ -14,8 +14,8 @@ source = ./workspaces.conf
|
||||
# See https://wiki.hypr.land/Configuring/Keywords/
|
||||
|
||||
$terminal = ghostty
|
||||
$fileManager = thunar
|
||||
$menu = wofi --show drun
|
||||
$fileManager = ghostty -e yazi
|
||||
$menu = env XDG_CURRENT_DESKTOP=Hyprland wofi --show drun
|
||||
|
||||
#################
|
||||
### AUTOSTART ###
|
||||
@ -23,11 +23,21 @@ $menu = wofi --show drun
|
||||
|
||||
# Autostart necessary processes (like notifications daemons, status bars, etc.)
|
||||
# Or execute your favorite apps at launch like this:
|
||||
exec-once = waybar
|
||||
# exec-once = waybar
|
||||
exec-once = /usr/lib/hyprpolkitagent/hyprpolkitagent
|
||||
# exec-once = hyprpanel
|
||||
exec-once = quickshell
|
||||
exec-once = dunst
|
||||
exec-once = firefox
|
||||
exec-once = hyprpaper
|
||||
exec-once = spotify
|
||||
exec-once = [workspace 9 silent] gtk-launch deezer-pwa
|
||||
# exec-once = ./scripts/close-monokai-popup.sh
|
||||
|
||||
# gnome keyring secret service
|
||||
exec-once = dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE
|
||||
# exec-once = systemctl --user stop xdg-desktop-portal-hyprland xdg-desktop-portal-gtk xdg-desktop-portal
|
||||
# exec-once = systemctl --user start xdg-desktop-portal-hyprland
|
||||
debug:disable_logs = false
|
||||
|
||||
#############################
|
||||
### ENVIRONMENT VARIABLES ###
|
||||
@ -36,8 +46,11 @@ exec-once = spotify
|
||||
# See https://wiki.hypr.land/Configuring/Environment-variables/
|
||||
env = XCURSOR_SIZE,24
|
||||
env = HYPRCURSOR_SIZE,24
|
||||
env = QT_QPA_PLATFORM,waylandl;xcb
|
||||
env = QT_QPA_PLATFORM,wayland
|
||||
env = QT_QPA_PLATFORMTHEME,qt5ct
|
||||
env = QT_WAYLAND_DISABLE_WINDOWDECORATION,1
|
||||
env = QT_AUTO_SCREEN_SCALE_FACTOR,1
|
||||
# env = QT_STYLE_OVERRIDE,kvantum
|
||||
|
||||
env = XDG_CURRENT_DESKTOP,Hyprland
|
||||
env = XDG_SESSION_TYPE,wayland
|
||||
@ -50,6 +63,10 @@ env = GDK_BACKEND,wayland,x11
|
||||
env = _JAVA_AWT_WM_NONREPARENTING, 1
|
||||
# env = AWT_TOOLKIT, MToolkit
|
||||
|
||||
# Force Hyprland to use the RX 9070 XT
|
||||
# env = AQ_DRM_DEVICES,$HOME/.config/hypr/dgpu
|
||||
# env = WLR_DRM_DEVICES,$HOME/.config/hypr/dgpu
|
||||
|
||||
###################
|
||||
### PERMISSIONS ###
|
||||
###################
|
||||
@ -74,18 +91,13 @@ env = _JAVA_AWT_WM_NONREPARENTING, 1
|
||||
# https://wiki.hypr.land/Configuring/Variables/#general
|
||||
general {
|
||||
gaps_in = 5
|
||||
gaps_out = 20
|
||||
gaps_out = 10
|
||||
|
||||
border_size = 2
|
||||
border_size = 3
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#variable-types for info about colors
|
||||
col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
|
||||
col.active_border = rgba(9b1a1aee) rgba(c94040ee) 45deg
|
||||
col.inactive_border = rgba(595959aa)
|
||||
# Active: cosmic purple -> neon cyan gradient
|
||||
# col.active_border = rgba(7f5af0ee) rgba(00d1ffee) 45deg
|
||||
#
|
||||
# # Inactive: deep space slate
|
||||
# col.inactive_border = rgba(0c0e14aa)
|
||||
|
||||
# Set to true enable resizing windows by clicking and dragging on borders and gaps
|
||||
resize_on_border = false
|
||||
@ -128,15 +140,19 @@ decoration {
|
||||
}
|
||||
}
|
||||
|
||||
# wofi
|
||||
layerrule = blur, wofi # apply compositor blur
|
||||
layerrule = ignorezero, wofi # keep blur even on near-opaque colors
|
||||
layerrule = dimaround, wofi # optional: dim background behind wofi
|
||||
layerrule = ignorealpha 0.90, wofi # target overall opacity for the layer
|
||||
cursor {
|
||||
no_hardware_cursors = true
|
||||
}
|
||||
|
||||
# waybar
|
||||
layerrule = blur, waybar
|
||||
layerrule = ignorealpha 0.15, waybar
|
||||
# wofi
|
||||
# layerrule = blur, wofi # apply compositor blur
|
||||
# layerrule = ignorezero, wofi # keep blur even on near-opaque colors
|
||||
# layerrule = dimaround, wofi # optional: dim background behind wofi
|
||||
# layerrule = ignorealpha 0.90, wofi # target overall opacity for the layer
|
||||
#
|
||||
# # waybar
|
||||
# layerrule = blur, waybar
|
||||
# layerrule = ignorealpha 0.15, waybar
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#animations
|
||||
animations {
|
||||
@ -180,7 +196,7 @@ animations {
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
|
||||
dwindle {
|
||||
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
||||
# pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
||||
preserve_split = true # You probably want this
|
||||
}
|
||||
|
||||
@ -203,18 +219,18 @@ misc {
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#input
|
||||
input {
|
||||
kb_layout = us
|
||||
kb_layout = us,de
|
||||
kb_variant = altgr-intl
|
||||
kb_model =
|
||||
kb_options =
|
||||
# kb_options = grp:caps_toggle
|
||||
kb_rules =
|
||||
|
||||
follow_mouse = 2
|
||||
|
||||
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
|
||||
|
||||
scroll_method = on_button_down
|
||||
scroll_button = 274
|
||||
# scroll_method = on_button_down
|
||||
# scroll_button = 274
|
||||
}
|
||||
|
||||
# Example per-device config
|
||||
@ -236,11 +252,13 @@ bind = $mainMod, Q, killactive,
|
||||
bind = $mainMod, N, exec, $fileManager
|
||||
bind = $mainMod, V, togglefloating,
|
||||
bind = $mainMod, Space, exec, $menu
|
||||
bind = $mainMod, G, togglesplit, # dwindle
|
||||
bind = $mainMod, F, fullscreen
|
||||
bind = $mainMod, G, layoutmsg, togglesplit # dwindle
|
||||
bind = $mainMod, F, fullscreen, 1
|
||||
bind = $mainMod SHIFT, F, fullscreen, 0
|
||||
bind = $mainMod, B, exec, firefox
|
||||
bind = $mainMod, M, exec, thunderbird
|
||||
bind = $mainMod, L, exec, hyprlock
|
||||
bind = $mainMod, P, pseudo
|
||||
|
||||
# Move focus with mainMod + arrow keys
|
||||
bind = $mainMod, left, movefocus, l
|
||||
@ -252,6 +270,14 @@ bind = $mainMod SHIFT, left, movewindow, l
|
||||
bind = $mainMod SHIFT, right, movewindow, r
|
||||
bind = $mainMod SHIFT, up, movewindow, u
|
||||
bind = $mainMod SHIFT, down, movewindow, d
|
||||
bind = $mainMod SHIFT, comma, movewindow, mon:+1
|
||||
bind = $mainMod SHIFT, period, movewindow, mon:-1
|
||||
|
||||
# Resize
|
||||
binde = $mainMod ALT, left, resizeactive, -20 0
|
||||
binde = $mainMod ALT, right, resizeactive, 20 0
|
||||
binde = $mainMod ALT, up, resizeactive, 0 -20
|
||||
binde = $mainMod ALT, down, resizeactive, 0 20
|
||||
|
||||
# Example special workspace (scratchpad)
|
||||
bind = $mainMod, S, togglespecialworkspace, magic
|
||||
@ -274,12 +300,12 @@ bindel = ,XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+
|
||||
bindel = ,XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-
|
||||
|
||||
# Requires playerctl
|
||||
# Spotify controls
|
||||
bind = CTRL ALT, K, exec, playerctl --player=spotify play-pause
|
||||
bind = CTRL ALT, Left, exec, playerctl --player=spotify previous
|
||||
bind = CTRL ALT, Right, exec, playerctl --player=spotify next
|
||||
bind = CTRL ALT, Up, exec, playerctl --player=spotify volume 0.05+
|
||||
bind = CTRL ALT, Down, exec, playerctl --player=spotify volume 0.05-
|
||||
# Deezer controls
|
||||
bind = CTRL ALT, K, exec, ~/.config/hypr/scripts/deezer-controls.sh play-pause
|
||||
bind = CTRL ALT, Left, exec, ~/.config/hypr/scripts/deezer-controls.sh previous
|
||||
bind = CTRL ALT, Right, exec, ~/.config/hypr/scripts/deezer-controls.sh next
|
||||
bind = CTRL ALT, Up, exec, ~/.config/hypr/scripts/deezer-controls.sh +5%
|
||||
bind = CTRL ALT, Down, exec, ~/.config/hypr/scripts/deezer-controls.sh -5%
|
||||
|
||||
bind = ,F7,pass,class:^(discord)$
|
||||
bind = ,F8,pass,class:^(discord)$
|
||||
@ -297,11 +323,61 @@ bind = , Print, exec, hyprshot -zm region
|
||||
# See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
|
||||
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
windowrule = suppressevent maximize, class:.*
|
||||
windowrule {
|
||||
name = suppress-maximize-events
|
||||
match:class = .*
|
||||
|
||||
suppress_event = maximize
|
||||
}
|
||||
|
||||
xwayland {
|
||||
enabled = true
|
||||
use_nearest_neighbor = true
|
||||
force_zero_scaling = false
|
||||
create_abstract_socket = false
|
||||
}
|
||||
|
||||
# Fix some dragging issues with XWayland
|
||||
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
windowrule {
|
||||
name = fix-xwayland-drags
|
||||
match:class = ^$
|
||||
match:title = ^$
|
||||
match:xwayland = true
|
||||
match:float = true
|
||||
match:fullscreen = false
|
||||
match:pin = false
|
||||
|
||||
windowrulev2 = tag +jb, class:^jetbrains-.+$,floating:1
|
||||
windowrulev2 = stayfocused, tag:jb
|
||||
windowrulev2 = noinitialfocus, tag:jb
|
||||
no_focus = true
|
||||
}
|
||||
|
||||
windowrule {
|
||||
name = jetbrains-shortcuts
|
||||
match:class = ^(jetbrains-.*|android-studio)$
|
||||
|
||||
no_shortcuts_inhibit = 1
|
||||
}
|
||||
|
||||
windowrule {
|
||||
name = jetbrains-tile
|
||||
match:class = ^(jetbrains-.*|android-studio)$
|
||||
float = 0
|
||||
}
|
||||
|
||||
windowrule {
|
||||
name = jetbrains-tab-drag
|
||||
match:class = ^(jetbrains-.*|android-studio)$
|
||||
match:title = ^(\s*|Delete|Replace All)$
|
||||
|
||||
no_initial_focus = 1
|
||||
stay_focused = 1
|
||||
float = 1
|
||||
center = 1
|
||||
}
|
||||
|
||||
windowrule {
|
||||
name = jetbrains-commit-preview
|
||||
match:class = ^(jetbrains-.*|android-studio)$
|
||||
match:title = ^Commit.*$
|
||||
|
||||
float = 0
|
||||
}
|
||||
|
||||
@ -1,7 +1,24 @@
|
||||
preload = /home/johannes/Pictures/Wallpapers/wallhaven-5g22q5.png
|
||||
preload = /home/johannes/Pictures/Wallpapers/japan-artistic-3840x2160-25406.jpg
|
||||
|
||||
wallpaper = DP-1,/home/johannes/Pictures/Wallpapers/wallhaven-5g22q5.png
|
||||
wallpaper = DP-2,/home/johannes/Pictures/Wallpapers/wallhaven-5g22q5.png
|
||||
wallpaper = HDMI-A-2,/home/johannes/Pictures/Wallpapers/wallhaven-5g22q5.png
|
||||
wallpaper {
|
||||
monitor = DP-1
|
||||
path =/home/johannes/Pictures/Wallpapers/japan-artistic-3840x2160-25406.jpg
|
||||
# fitmode = cover
|
||||
}
|
||||
wallpaper {
|
||||
monitor = DP-2
|
||||
path =/home/johannes/Pictures/Wallpapers/japan-artistic-3840x2160-25406.jpg
|
||||
# fitmode = cover
|
||||
}
|
||||
wallpaper {
|
||||
monitor = HDMI-A-1
|
||||
path =/home/johannes/Pictures/Wallpapers/japan-artistic-3840x2160-25406.jpg
|
||||
# fitmode = cover
|
||||
}
|
||||
wallpaper {
|
||||
monitor = HDMI-A-2
|
||||
path =/home/johannes/Pictures/Wallpapers/japan-artistic-3840x2160-25406.jpg
|
||||
# fitmode = cover
|
||||
}
|
||||
|
||||
splash = false
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
# Generated by nwg-displays on 2025-09-09 at 19:00:43. Do not edit manually.
|
||||
|
||||
monitor=DP-1,2560x1440@165.0,6626x876,1.0
|
||||
monitor=DP-2,2560x1440@165.0,4066x876,1.0
|
||||
monitor=HDMI-A-1,0x0@60.0,-1x-1,1.0
|
||||
monitor=HDMI-A-1,disable
|
||||
monitor=HDMI-A-2,1920x1080@60.0,9186x396,1.0
|
||||
monitor=HDMI-A-2,transform,3
|
||||
3
roles/hyprland/files/hypr/monitors_jk-nb.conf
Normal file
3
roles/hyprland/files/hypr/monitors_jk-nb.conf
Normal file
@ -0,0 +1,3 @@
|
||||
# Generated by nwg-displays on 2026-04-10 at 16:05:43. Do not edit manually.
|
||||
|
||||
monitor=eDP-1,2880x1800@90.0,0x0,1.4999999999999996
|
||||
7
roles/hyprland/files/hypr/monitors_jk-pc.conf
Normal file
7
roles/hyprland/files/hypr/monitors_jk-pc.conf
Normal file
@ -0,0 +1,7 @@
|
||||
# Generated by nwg-displays on 2026-04-20 at 01:17:12. Do not edit manually.
|
||||
|
||||
monitor=DP-1,2560x1440@165.0,6626x876,1.0
|
||||
monitor=DP-2,2560x1440@165.0,4066x876,1.0
|
||||
monitor=HDMI-A-2,1920x1080@60.0,9186x396,1.0,transform,3
|
||||
# monitor=HDMI-A-2,transform,3
|
||||
monitor=HDMI-A-1,disable
|
||||
34
roles/hyprland/files/hypr/scripts/close-monokai-popup.sh
Executable file
34
roles/hyprland/files/hypr/scripts/close-monokai-popup.sh
Executable file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SOCKET="$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock"
|
||||
|
||||
handle_line() {
|
||||
local line="$1"
|
||||
|
||||
# Split into EVENT and DATA (EVENT>>DATA)
|
||||
local event="${line%%>>*}"
|
||||
local data="${line#*>>}"
|
||||
|
||||
echo "$data" > "$HOME/close_popup.tmp.txt"
|
||||
|
||||
case "$event" in
|
||||
openwindow)
|
||||
# Data format:
|
||||
# WINDOWADDRESS,WORKSPACENAME,WINDOWCLASS,WINDOWTITLE
|
||||
local addr ws class title
|
||||
|
||||
IFS=, read -r addr ws class title <<< "$data"
|
||||
# echo "$data" > $HOME/close_popup.tmp.txt
|
||||
|
||||
if [[ "$class" == "jetbrains-webstorm" && "$title" == "Monokai Pro Trial" ]]; then
|
||||
# closewindow expects address with 0x prefix
|
||||
hyprctl dispatch closewindow "address:0x$addr"
|
||||
# echo "$data" > "$HOME/close_popup.tmp.txt"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
socat -U - UNIX-CONNECT:$SOCKET | while read -r line; do
|
||||
handle_line "$line";
|
||||
done
|
||||
95
roles/hyprland/files/hypr/scripts/deezer-controls.sh
Executable file
95
roles/hyprland/files/hypr/scripts/deezer-controls.sh
Executable file
@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
action="$1"
|
||||
|
||||
if [ -z "$action" ]; then
|
||||
echo "Usage: $0 [play-pause|next|previous|+5%|-5%|50%]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Deezer PWA PID
|
||||
root_pid=$(hyprctl clients -j | jq -r '.[] | select(.initialTitle == "Deezer") | .pid' | head -n1)
|
||||
|
||||
if [ -z "$root_pid" ] || [ "$root_pid" = "null" ]; then
|
||||
echo "[ERROR] Deezer window not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$action" in
|
||||
play-pause|next|previous)
|
||||
playerctl --player="chromium.instance${root_pid}" "$action"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
# Volume: find sink-input whose PID is in the process tree
|
||||
pids=$(pstree -p "$root_pid" | grep -oP '\(\K[0-9]+')
|
||||
sink=$(pactl -f json list sink-inputs | jq -r --argjson pids "$(echo "$pids" | jq -R . | jq -s 'map(tonumber)')" \
|
||||
'.[] | select(.properties["application.process.id"] | tonumber | IN($pids[])) | .index' | head -n1)
|
||||
|
||||
if [ -z "$sink" ]; then
|
||||
echo "[ERROR] No audio stream found for Deezer."
|
||||
exit 1
|
||||
fi
|
||||
pactl set-sink-input-volume "$sink" "$action"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
#
|
||||
# # 2) Gather ALL descendant PIDs
|
||||
# declare -a pid_array
|
||||
# pid_array+=("$root_pid")
|
||||
#
|
||||
# # Direct children
|
||||
# children=($(pgrep -P "$root_pid"))
|
||||
# echo "[INFO] Children: ${children[*]}"
|
||||
#
|
||||
# for c in "${children[@]}"; do
|
||||
# pid_array+=("$c")
|
||||
# gc=($(pgrep -P "$c"))
|
||||
# echo "[INFO] Grandchildren of $c: ${gc[*]}"
|
||||
# for g in "${gc[@]}"; do
|
||||
# pid_array+=("$g")
|
||||
# done
|
||||
# done
|
||||
#
|
||||
# echo "[INFO] All collected PIDs: ${pid_array[*]}"
|
||||
#
|
||||
# echo "----------------------------------"
|
||||
# echo "[INFO] Checking sink inputs..."
|
||||
# echo "----------------------------------"
|
||||
#
|
||||
# sink=""
|
||||
#
|
||||
# while IFS= read -r block; do
|
||||
# pid=$(echo "$block" | jq -r '.properties["application.process.id"] // empty')
|
||||
# index=$(echo "$block" | jq -r '.index')
|
||||
#
|
||||
# echo "[SINK] index=$index pid=$pid"
|
||||
#
|
||||
# [ -z "$pid" ] && { echo " -> Skipping"; continue; }
|
||||
#
|
||||
# for candidate in "${pid_array[@]}"; do
|
||||
# if [ "$pid" = "$candidate" ]; then
|
||||
# echo " -> MATCH with PID $candidate"
|
||||
# sink="$index"
|
||||
# break 2
|
||||
# fi
|
||||
# done
|
||||
#
|
||||
# echo " -> No match"
|
||||
#
|
||||
# done < <(pactl -f json list sink-inputs | jq -c '.[]')
|
||||
#
|
||||
# echo "----------------------------------"
|
||||
#
|
||||
# if [ -z "$sink" ]; then
|
||||
# echo "[ERROR] No audio stream found for Deezer."
|
||||
# exit 1
|
||||
# fi
|
||||
#
|
||||
# echo "[INFO] Deezer sink index: $sink"
|
||||
# echo "[INFO] Applying: $change"
|
||||
#
|
||||
# pactl set-sink-input-volume "$sink" "$change"
|
||||
#
|
||||
34
roles/hyprland/files/hypr/workspaces_jk-nb.conf
Normal file
34
roles/hyprland/files/hypr/workspaces_jk-nb.conf
Normal file
@ -0,0 +1,34 @@
|
||||
$mainMod = SUPER # Sets "Super" key as main modifier
|
||||
|
||||
workspace = 1, persistent:true, default:true
|
||||
workspace = 2, persistent:true
|
||||
workspace = 3, persistent:true
|
||||
workspace = 4, persistent:true
|
||||
workspace = 5, persistent:true
|
||||
workspace = 6, persistent:true
|
||||
workspace = 7, persistent:true
|
||||
workspace = 8, persistent:true
|
||||
workspace = 9, persistent:true
|
||||
workspace = 10, persistent:true
|
||||
|
||||
bind = $mainMod, 1, exec, hyprctl --batch "dispatch workspace 1;"
|
||||
bind = $mainMod, 2, exec, hyprctl --batch "dispatch workspace 2;"
|
||||
bind = $mainMod, 3, exec, hyprctl --batch "dispatch workspace 3;"
|
||||
bind = $mainMod, 4, exec, hyprctl --batch "dispatch workspace 4;"
|
||||
bind = $mainMod, 5, exec, hyprctl --batch "dispatch workspace 5;"
|
||||
bind = $mainMod, 6, exec, hyprctl --batch "dispatch workspace 6;"
|
||||
bind = $mainMod, 7, exec, hyprctl --batch "dispatch workspace 7;"
|
||||
bind = $mainMod, 8, exec, hyprctl --batch "dispatch workspace 8;"
|
||||
bind = $mainMod, 9, exec, hyprctl --batch "dispatch workspace 9;"
|
||||
bind = $mainMod, 0, exec, hyprctl --batch "dispatch workspace 10;"
|
||||
|
||||
bind = $mainMod SHIFT, 1, movetoworkspacesilent, 1
|
||||
bind = $mainMod SHIFT, 2, movetoworkspacesilent, 2
|
||||
bind = $mainMod SHIFT, 3, movetoworkspacesilent, 3
|
||||
bind = $mainMod SHIFT, 4, movetoworkspacesilent, 4
|
||||
bind = $mainMod SHIFT, 5, movetoworkspacesilent, 5
|
||||
bind = $mainMod SHIFT, 6, movetoworkspacesilent, 6
|
||||
bind = $mainMod SHIFT, 7, movetoworkspacesilent, 7
|
||||
bind = $mainMod SHIFT, 8, movetoworkspacesilent, 8
|
||||
bind = $mainMod SHIFT, 9, movetoworkspacesilent, 9
|
||||
bind = $mainMod SHIFT, 0, movetoworkspacesilent, 10
|
||||
@ -6,9 +6,9 @@ workspace = 3, monitor:DP-1, persistent:true
|
||||
workspace = 4, monitor:DP-2, persistent:true, default:true
|
||||
workspace = 5, monitor:DP-2, persistent:true
|
||||
workspace = 6, monitor:DP-2, persistent:true
|
||||
workspace = 7, monitor:HDMI-A-2, persistent:true, default:true
|
||||
workspace = 7, monitor:HDMI-A-2, persistent:true
|
||||
workspace = 8, monitor:HDMI-A-2, persistent:true
|
||||
workspace = 9, monitor:HDMI-A-2, persistent:true
|
||||
workspace = 9, monitor:HDMI-A-2, persistent:true, default:true
|
||||
workspace = 10, monitor:HDMI-A-1, persistent:true, default:true
|
||||
|
||||
bind = $mainMod, 1, exec, hyprctl --batch "dispatch focusmonitor DP-1; dispatch workspace 1;"
|
||||
48
roles/hyprland/files/hyprpanel/config.json
Normal file
48
roles/hyprland/files/hyprpanel/config.json
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"bar.customModules.storage.paths": [
|
||||
"/"
|
||||
],
|
||||
"menus.power.showLabel": false,
|
||||
"theme.font.size": "1rem",
|
||||
"theme.font.weight": 500,
|
||||
"scalingPriority": "gdk",
|
||||
"menus.power.lowBatteryNotification": false,
|
||||
"bar.customModules.worldclock.tz": [
|
||||
"Europe/Vienna"
|
||||
],
|
||||
"theme.bar.buttons.modules.power.enableBorder": false,
|
||||
"menus.dashboard.powermenu.avatar.image": "/home/johannes/Pictures/Wallpapers/wallhaven-852q62.png",
|
||||
"menus.dashboard.shortcuts.left.shortcut1.tooltip": "Firefox",
|
||||
"menus.dashboard.shortcuts.left.shortcut1.command": "firefox",
|
||||
"menus.dashboard.shortcuts.left.shortcut1.icon": "",
|
||||
"menus.clock.time.military": true,
|
||||
"menus.clock.weather.location": "Vienna",
|
||||
"menus.clock.weather.unit": "metric",
|
||||
"menus.volume.raiseMaximumVolume": true,
|
||||
"theme.bar.floating": true,
|
||||
"bar.autoHide": "never",
|
||||
"theme.bar.buttons.enableBorders": true,
|
||||
"theme.bar.border.width": "0.1em",
|
||||
"bar.workspaces.show_icons": false,
|
||||
"bar.workspaces.show_numbered": false,
|
||||
"bar.workspaces.workspaceMask": false,
|
||||
"bar.workspaces.showWsIcons": true,
|
||||
"bar.workspaces.showApplicationIcons": true,
|
||||
"bar.workspaces.showAllActive": true,
|
||||
"bar.workspaces.numbered_active_indicator": "highlight",
|
||||
"theme.bar.buttons.workspaces.smartHighlight": true,
|
||||
"theme.bar.buttons.windowtitle.enableBorder": false,
|
||||
"bar.windowtitle.custom_title": true,
|
||||
"bar.windowtitle.class_name": true,
|
||||
"theme.bar.buttons.clock.enableBorder": false,
|
||||
"bar.clock.format": "%a %b %d %H:%M:%S",
|
||||
"theme.bar.menus.menu.battery.scaling": 100,
|
||||
"theme.bar.buttons.battery.visible": false,
|
||||
"bar.battery.label": false,
|
||||
"bar.battery.hideLabelWhenFull": true,
|
||||
"theme.bar.buttons.battery.enableBorder": false,
|
||||
"theme.osd.enable": true,
|
||||
"theme.bar.buttons.y_margins": "0.2em",
|
||||
"theme.bar.dropdownGap": "2.9em",
|
||||
"theme.bar.outer_spacing": "1.6em"
|
||||
}
|
||||
1
roles/hyprland/files/hyprpanel/modules.json
Normal file
1
roles/hyprland/files/hyprpanel/modules.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
||||
0
roles/hyprland/files/hyprpanel/modules.scss
Normal file
0
roles/hyprland/files/hyprpanel/modules.scss
Normal file
@ -8,10 +8,8 @@
|
||||
"margin-right": 10, // gap from the right edge
|
||||
"margin-bottom": 0,
|
||||
|
||||
/* order: 1-2-3 | window-title | tray + clock */
|
||||
// "modules-left": [ "custom/group1", "custom/group2", "custom/group3", "custom/group4","custom/group5","custom/group6","custom/group7","custom/group8","custom/group9","custom/group10"],
|
||||
"modules-left": ["hyprland/workspaces"],
|
||||
"modules-center": [ "hyprland/window" ],
|
||||
"modules-center": ["wlr/taskbar"],
|
||||
"modules-right": [ "tray", "custom/separator", "network", "bluetooth", "custom/separator", "disk", "cpu", "memory", "pulseaudio", "custom/separator", "clock"],
|
||||
|
||||
|
||||
@ -19,7 +17,7 @@
|
||||
"disable-scroll": true,
|
||||
"all-outputs": true,
|
||||
"warp-on-scroll": false,
|
||||
"format": "{icon}",
|
||||
"format": "{icon}{windows}",
|
||||
"format-icons": {
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
@ -32,6 +30,29 @@
|
||||
"9": "9",
|
||||
"10": "0"
|
||||
},
|
||||
"window-rewrite-default": " ",
|
||||
"format-window-separator": "",
|
||||
"window-rewrite": {
|
||||
"class<firefox>": " ",
|
||||
"class<com.mitchellh.ghostty>": " ",
|
||||
"class<com.mitchellh.ghostty> title<vim.*>": " ",
|
||||
"class<Spotify>": " ",
|
||||
"class<QOwnNotes>": " ",
|
||||
"class<thunderbird>": " ",
|
||||
"class<thunar>": " ",
|
||||
"class<jetbrains-webstorm>": " ",
|
||||
"class<jetbrains-pycharm>": " ",
|
||||
"class<steam>": " ",
|
||||
"class<discord>": " "
|
||||
// "": "",
|
||||
// "": "",
|
||||
// "": "",
|
||||
// "": "",
|
||||
// "": "",
|
||||
// "": "",
|
||||
// "": "",
|
||||
// "": ""
|
||||
},
|
||||
"persistent-workspaces": {
|
||||
"1": [],
|
||||
"2": [],
|
||||
@ -47,7 +68,14 @@
|
||||
},
|
||||
|
||||
|
||||
"hyprland/window": { "format": "{title}", "max-length": 60 },
|
||||
// "hyprland/window": { "format": "{title}", "max-length": 60 },
|
||||
"wlr/taskbar": {
|
||||
"format": "{icon}",
|
||||
"icon-theme": "Ars-Dark-Icons",
|
||||
"tooltip-format": "{title}",
|
||||
"on-click": "activate",
|
||||
"on-click-middle": "close"
|
||||
},
|
||||
"tray": { "icon-size": 16, "spacing": 10 },
|
||||
|
||||
"pulseaudio": {
|
||||
|
||||
@ -60,7 +60,7 @@ window#waybar {
|
||||
#tray menu menuitem:hover { background: alpha(@accent1, 0.16); }
|
||||
|
||||
#workspaces button {
|
||||
padding: 0 8px;
|
||||
padding: 0 2px;
|
||||
background-color: transparent;
|
||||
color: #6c7086;
|
||||
border-radius: 3px;
|
||||
|
||||
@ -1,8 +1,46 @@
|
||||
---
|
||||
- name: Symlink hyprland
|
||||
- name: Check if hypr path is a symlink
|
||||
stat:
|
||||
path: "{{ config_dir }}/hypr"
|
||||
register: hypr_stat
|
||||
|
||||
- name: Remove existing hypr symlink if present
|
||||
file:
|
||||
src: "{{ role_path }}/files/hypr"
|
||||
dest: "{{ config_dir }}/hypr"
|
||||
path: "{{ config_dir }}/hypr"
|
||||
state: absent
|
||||
when: hypr_stat.stat.exists and hypr_stat.stat.islnk
|
||||
|
||||
- name: Create hypr config directory
|
||||
file:
|
||||
path: "{{ config_dir }}/hypr"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Symlink hyprland config files
|
||||
file:
|
||||
src: "{{ role_path }}/files/hypr/{{ conf_file }}"
|
||||
dest: "{{ config_dir }}/hypr/{{ conf_file }}"
|
||||
state: link
|
||||
force: true
|
||||
loop:
|
||||
- hyprland.conf
|
||||
- hyprlock.conf
|
||||
- hyprpaper.conf
|
||||
- scripts
|
||||
loop_control:
|
||||
loop_var: conf_file
|
||||
|
||||
- name: Symlink host-specific monitors.conf
|
||||
file:
|
||||
src: "{{ role_path }}/files/hypr/monitors_{{ ansible_facts['hostname'] }}.conf"
|
||||
dest: "{{ config_dir }}/hypr/monitors.conf"
|
||||
state: link
|
||||
force: true
|
||||
|
||||
- name: Symlink host-specific workspaces.conf
|
||||
file:
|
||||
src: "{{ role_path }}/files/hypr/workspaces_{{ ansible_facts['hostname'] }}.conf"
|
||||
dest: "{{ config_dir }}/hypr/workspaces.conf"
|
||||
state: link
|
||||
force: true
|
||||
|
||||
@ -27,3 +65,9 @@
|
||||
state: link
|
||||
force: true
|
||||
|
||||
- name: Symlink hyprpanel
|
||||
file:
|
||||
src: "{{ role_path }}/files/hyprpanel"
|
||||
dest: "{{ config_dir }}/hyprpanel"
|
||||
state: link
|
||||
force: true
|
||||
|
||||
1
roles/nvim/files/after/ftplugin/jsonc.lua
Normal file
1
roles/nvim/files/after/ftplugin/jsonc.lua
Normal file
@ -0,0 +1 @@
|
||||
vim.opt_local.shiftwidth = 2
|
||||
@ -1 +1 @@
|
||||
vim.opt_local.shiftwidth = 4
|
||||
vim.opt_local.shiftwidth = 2
|
||||
|
||||
@ -1,41 +1,42 @@
|
||||
{
|
||||
"LuaSnip": { "branch": "master", "commit": "458560534a73f7f8d7a11a146c801db00b081df0" },
|
||||
"alpha-nvim": { "branch": "main", "commit": "2b3cbcdd980cae1e022409289245053f62fb50f6" },
|
||||
"auto-session": { "branch": "main", "commit": "ee320d7a59c1dfe83df7c9d58150d9f194690d53" },
|
||||
"LuaSnip": { "branch": "master", "commit": "642b0c595e11608b4c18219e93b88d7637af27bc" },
|
||||
"alpha-nvim": { "branch": "main", "commit": "6c6a89d5b068b5251c8bdf0dd57bb921bcfeeb09" },
|
||||
"auto-session": { "branch": "main", "commit": "6cde3874a9283c2db55627acca5f28e4fa402320" },
|
||||
"bufdelete.nvim": { "branch": "master", "commit": "f6bcea78afb3060b198125256f897040538bcb81" },
|
||||
"bufferline.nvim": { "branch": "main", "commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3" },
|
||||
"cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
|
||||
"cmp-nvim-lsp": { "branch": "main", "commit": "a8912b88ce488f411177fc8aed358b04dc246d7b" },
|
||||
"cmp-path": { "branch": "main", "commit": "e52e640b7befd8113b3350f46e8cfcfe98fcf730" },
|
||||
"cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
|
||||
"cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
|
||||
"cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" },
|
||||
"diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" },
|
||||
"dressing.nvim": { "branch": "master", "commit": "2d7c2db2507fa3c4956142ee607431ddb2828639" },
|
||||
"flutter-tools.nvim": { "branch": "main", "commit": "65b7399804315a1160933b64292d3c5330aa4e9f" },
|
||||
"friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" },
|
||||
"lazygit.nvim": { "branch": "main", "commit": "cdd3527e251f96eb0527162b156ad839286fcd97" },
|
||||
"lspkind.nvim": { "branch": "master", "commit": "d79a1c3299ad0ef94e255d045bed9fa26025dab6" },
|
||||
"lualine.nvim": { "branch": "master", "commit": "a94fc68960665e54408fe37dcf573193c4ce82c9" },
|
||||
"mason-lspconfig.nvim": { "branch": "main", "commit": "1a31f824b9cd5bc6f342fc29e9a53b60d74af245" },
|
||||
"mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" },
|
||||
"mason.nvim": { "branch": "main", "commit": "4da89f3ab04783da990f9bd40aaa36c22e59375b" },
|
||||
"monokai-nightasty.nvim": { "branch": "main", "commit": "8182c45dcaf82e7316cfda6d439ee8ad783ae594" },
|
||||
"neodev.nvim": { "branch": "main", "commit": "46aa467dca16cf3dfe27098042402066d2ae242d" },
|
||||
"none-ls-extras.nvim": { "branch": "main", "commit": "924fe88a9983c7d90dbb31fc4e3129a583ea0a90" },
|
||||
"none-ls.nvim": { "branch": "main", "commit": "3ce66bc62eb363f19cceeb1fae2e71ea2bede56d" },
|
||||
"nvim-autopairs": { "branch": "master", "commit": "23320e75953ac82e559c610bec5a90d9c6dfa743" },
|
||||
"nvim-cmp": { "branch": "main", "commit": "b5311ab3ed9c846b585c0c15b7559be131ec4be9" },
|
||||
"nvim-lsp-file-operations": { "branch": "master", "commit": "9744b738183a5adca0f916527922078a965515ed" },
|
||||
"nvim-lspconfig": { "branch": "master", "commit": "4d3b3bb8815fbe37bcaf3dbdb12a22382bc11ebe" },
|
||||
"nvim-surround": { "branch": "main", "commit": "7a7a78a52219a3312c1fcabf880cea07a7956a5f" },
|
||||
"nvim-tree.lua": { "branch": "master", "commit": "a9156c013916d3c8a2905ab2e26e9dd74beb642e" },
|
||||
"nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
|
||||
"nvim-ts-autotag": { "branch": "main", "commit": "a1d526af391f6aebb25a8795cbc05351ed3620b5" },
|
||||
"nvim-web-devicons": { "branch": "master", "commit": "4a8369f4c78ef6f6f895f0cec349e48f74330574" },
|
||||
"plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
|
||||
"telescope-fzf-native.nvim": { "branch": "main", "commit": "1f08ed60cafc8f6168b72b80be2b2ea149813e55" },
|
||||
"telescope.nvim": { "branch": "0.1.x", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" },
|
||||
"todo-comments.nvim": { "branch": "main", "commit": "304a8d204ee787d2544d8bc23cd38d2f929e7cc5" },
|
||||
"venv-selector.nvim": { "branch": "regexp", "commit": "c677caa1030808a9f90092e522de7cc20c1390dd" },
|
||||
"vim-floaterm": { "branch": "master", "commit": "fd4bdd66eca56c6cc59f2119e4447496d8cde2ea" },
|
||||
"which-key.nvim": { "branch": "main", "commit": "370ec46f710e058c9c1646273e6b225acf47cbed" }
|
||||
"flutter-tools.nvim": { "branch": "main", "commit": "7d1acfd139215e02d2784733af69a61aaebe06e8" },
|
||||
"friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
|
||||
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
|
||||
"lazydev.nvim": { "branch": "main", "commit": "ff2cbcba459b637ec3fd165a2be59b7bbaeedf0d" },
|
||||
"lazygit.nvim": { "branch": "main", "commit": "a04ad0dbc725134edbee3a5eea29290976695357" },
|
||||
"lspkind.nvim": { "branch": "master", "commit": "c7274c48137396526b59d86232eabcdc7fed8a32" },
|
||||
"lualine.nvim": { "branch": "master", "commit": "221ce6b2d999187044529f49da6554a92f740a96" },
|
||||
"mason-lspconfig.nvim": { "branch": "main", "commit": "21c5b3ebeaa0412e28096bb0701434c51c1fbf76" },
|
||||
"mason-tool-installer.nvim": { "branch": "main", "commit": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" },
|
||||
"mason.nvim": { "branch": "main", "commit": "2a6940af80375532e5e9e7c1f2fc6319a1b7a69d" },
|
||||
"mini.nvim": { "branch": "main", "commit": "a59a9b7fb0a42cbcf022938ee5f0724320b66f63" },
|
||||
"monokai-nightasty.nvim": { "branch": "main", "commit": "1e9b92006782a1217d0a7a871b871768f1cbf5ed" },
|
||||
"none-ls-extras.nvim": { "branch": "main", "commit": "27681d797a26f1b4d6119296df42f5204c88a2dc" },
|
||||
"none-ls.nvim": { "branch": "main", "commit": "01f8e62ea11603e59ad9ff7afcfa94fd183f76d6" },
|
||||
"nvim-autopairs": { "branch": "master", "commit": "7b9923abad60b903ece7c52940e1321d39eccc79" },
|
||||
"nvim-cmp": { "branch": "main", "commit": "a1d504892f2bc56c2e79b65c6faded2fd21f3eca" },
|
||||
"nvim-lsp-file-operations": { "branch": "master", "commit": "b9c795d3973e8eec22706af14959bc60c579e771" },
|
||||
"nvim-lspconfig": { "branch": "master", "commit": "a683e0ddf0cf64c6cd689e18ffb480ade3c162b7" },
|
||||
"nvim-surround": { "branch": "main", "commit": "2e93e154de9ff326def6480a4358bfc149d5da2c" },
|
||||
"nvim-tree.lua": { "branch": "master", "commit": "fb343438d49fba8c35ecc4829d66fca7a1f0ed3d" },
|
||||
"nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
|
||||
"nvim-ts-autotag": { "branch": "main", "commit": "88c1453db4ba7dd24131086fe51fdf74e587d275" },
|
||||
"nvim-web-devicons": { "branch": "master", "commit": "dfbfaa967a6f7ec50789bead7ef87e336c1fa63c" },
|
||||
"plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
|
||||
"render-markdown.nvim": { "branch": "main", "commit": "5adf0895310c1904e5abfaad40a2baad7fe44a07" },
|
||||
"telescope-fzf-native.nvim": { "branch": "main", "commit": "b25b749b9db64d375d782094e2b9dce53ad53a40" },
|
||||
"telescope.nvim": { "branch": "master", "commit": "7d324792b7943e4aa16ad007212e6acc6f9fe335" },
|
||||
"vim-floaterm": { "branch": "master", "commit": "bb4ba7952e906408e1f83b215f55ffe57efcade6" },
|
||||
"which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }
|
||||
}
|
||||
|
||||
@ -20,6 +20,10 @@ keymap.set('n', 'j', 'gj', opts)
|
||||
keymap.set('n', '<UP>', 'gk', opts)
|
||||
keymap.set('n', '<DOWN>', 'gj', opts)
|
||||
|
||||
-- bind tabswitching to Tab and Shift-Tab
|
||||
keymap.set('n', '<Tab>', ':tabnext<CR>', opts)
|
||||
keymap.set('n', '<S-Tab>', ':tabprev<CR>', opts)
|
||||
|
||||
-- COMMANDS
|
||||
|
||||
-- print current working directory
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
local opt = vim.opt
|
||||
|
||||
vim.filetype.add({ extension = { mdx = 'mdx' } })
|
||||
|
||||
-- numbers
|
||||
opt.relativenumber = true
|
||||
opt.number = true
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
local lazypath = vim.fn.stdpath('data') .. 'lazy/lazy.nvim'
|
||||
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
|
||||
|
||||
-- bootstrap lazy
|
||||
if not (vim.uv or vim.loop).fs_stat(lazypath) then
|
||||
|
||||
@ -29,12 +29,37 @@ return {
|
||||
|
||||
local opts = { silent = true, noremap = true }
|
||||
|
||||
local function git_branch()
|
||||
local branch = vim.trim(vim.fn.system('git branch --show-current 2>/dev/null'))
|
||||
if vim.v.shell_error ~= 0 or branch == '' then return nil end
|
||||
return branch
|
||||
end
|
||||
|
||||
local function session_save()
|
||||
local branch = git_branch()
|
||||
if branch then
|
||||
vim.cmd('AutoSession save ' .. branch)
|
||||
else
|
||||
vim.cmd('AutoSession save')
|
||||
end
|
||||
end
|
||||
|
||||
local function session_restore()
|
||||
local branch = git_branch()
|
||||
if branch then
|
||||
vim.cmd('AutoSession restore ' .. branch)
|
||||
else
|
||||
vim.cmd('AutoSession restore')
|
||||
end
|
||||
end
|
||||
|
||||
local keymap = vim.keymap
|
||||
keymap.set('n', '<leader>ss', '<cmd>SessionSave<cr>', opts)
|
||||
keymap.set('n', '<leader>sq', '<cmd>SessionSave<cr><cmd>wa<cr><cmd>qa<cr>', opts)
|
||||
keymap.set('n', '<leader>sl', '<cmd>SessionSearch<cr>', opts)
|
||||
keymap.set('n', '<leader>sr', '<cmd>SessionRestore<cr>', opts)
|
||||
keymap.set('n', '<leader>sd', '<cmd>Autosession delete<cr>', opts)
|
||||
keymap.set('n', '<leader>ss', session_save, opts)
|
||||
keymap.set('n', '<leader>sq', function() session_save(); vim.cmd('wa | qa') end, opts)
|
||||
keymap.set('n', '<leader>sl', '<cmd>AutoSession search<cr>', opts)
|
||||
keymap.set('n', '<leader>sr', session_restore, opts)
|
||||
keymap.set('n', '<leader>sd', '<cmd>AutoSession delete<cr>', opts)
|
||||
keymap.set('n', '<leader>qa', '<cmd>qa<cr>', opts)
|
||||
|
||||
local function named_save()
|
||||
vim.ui.input({ prompt = 'Enter Session name: ' }, function(input)
|
||||
@ -46,12 +71,14 @@ return {
|
||||
local wk = require('which-key')
|
||||
wk.add({
|
||||
{ '<leader>s', group = 'auto-session' },
|
||||
{ '<leader>q', group = 'quit' },
|
||||
{ '<leader>ss', desc = 'SessionSave' },
|
||||
{ '<leader>sS', desc = 'NamedSessionSave' },
|
||||
{ '<leader>sq', desc = 'Save and Quit' },
|
||||
{ '<leader>sl', desc = 'SessionList (SessionSearch)'},
|
||||
{ '<leader>sr', desc = 'SessionRestore'},
|
||||
{ '<leader>sd', desc = 'SessionDelete'},
|
||||
{ '<leader>qa', desc = 'Quit All'},
|
||||
})
|
||||
end,
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@ return {
|
||||
['<Enter>'] = cmp.mapping.confirm({ select = true }),
|
||||
}),
|
||||
sources = cmp.config.sources({
|
||||
{ name = 'lazydev', group_index = 0 },
|
||||
{ name = 'nvim_lsp' },
|
||||
{ name = 'luasnip' },
|
||||
{ name = 'buffer' },
|
||||
|
||||
@ -1,3 +1,28 @@
|
||||
return {
|
||||
'sindrets/diffview.nvim'
|
||||
'sindrets/diffview.nvim',
|
||||
config = function ()
|
||||
require('diffview').setup({
|
||||
enhanced_diff_hl = true
|
||||
})
|
||||
|
||||
-- Fix text color on fully added/deleted lines
|
||||
vim.api.nvim_set_hl(0, "DiffAdd", { bg = "#2d4a2d", fg = "NONE" })
|
||||
vim.api.nvim_set_hl(0, "DiffDelete", { bg = "#4a2d2d", fg = "NONE" })
|
||||
vim.api.nvim_set_hl(0, "DiffChange", { bg = "#2d3a4a", fg = "NONE" })
|
||||
vim.api.nvim_set_hl(0, "DiffText", { bg = "#1a5a7a", fg = "NONE" })
|
||||
|
||||
local wk = require('which-key')
|
||||
|
||||
wk.add({
|
||||
{ '<leader>d', group = 'diffview' },
|
||||
{ '<leader>do', '<cmd>DiffviewOpen<cr>', desc = 'Open diff view' },
|
||||
{ '<leader>dh', '<cmd>DiffviewFileHistory<cr>', desc = 'File history (repo)' },
|
||||
{ '<leader>df', '<cmd>DiffviewFileHistory %<cr>', desc = 'File history (current)' },
|
||||
{ '<leader>dc', '<cmd>DiffviewClose<cr>', desc = 'Close diff view' },
|
||||
|
||||
-- Toggle panels
|
||||
{ '<leader>dt', '<cmd>DiffviewToggleFiles<cr>', desc = 'Toggle file panel' },
|
||||
{ '<leader>dr', '<cmd>DiffviewRefresh<cr>', desc = 'Refresh' },
|
||||
})
|
||||
end
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ return {
|
||||
'LazyGitFilter',
|
||||
'LazyGitFilterCurrentFile',
|
||||
},
|
||||
depedencies = {
|
||||
dependencies = {
|
||||
'nvim-lua/plenary.nvim',
|
||||
},
|
||||
}
|
||||
|
||||
@ -2,14 +2,21 @@ return {
|
||||
'neovim/nvim-lspconfig',
|
||||
event = { 'BufReadPre', 'BufNewFile' },
|
||||
dependencies = {
|
||||
'williamboman/mason.nvim',
|
||||
'mason-org/mason.nvim',
|
||||
'mason-org/mason-lspconfig.nvim',
|
||||
'hrsh7th/cmp-nvim-lsp',
|
||||
{ 'antosha417/nvim-lsp-file-operations', config = true },
|
||||
{ 'folke/neodev.nvim', opts = {} },
|
||||
{
|
||||
'folke/lazydev.nvim',
|
||||
ft = 'lua',
|
||||
opts = {
|
||||
library = {
|
||||
{ path = '${3rd}/luv/library', words = { 'vim%.uv' } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
config = function()
|
||||
local lspconfig = require('lspconfig')
|
||||
local mason_lspconfig = require('mason-lspconfig')
|
||||
local cmp_nvim_lsp = require('cmp_nvim_lsp')
|
||||
local keymap = vim.keymap
|
||||
local wk = require('which-key')
|
||||
@ -21,13 +28,12 @@ return {
|
||||
return { desc = desc, buffer = ev.buf, silent = true }
|
||||
end
|
||||
|
||||
keymap.set('n', 'gr', vim.lsp.buf.rename, opts('Rename'))
|
||||
keymap.set('n', 'K', vim.lsp.buf.hover, opts('Show documentation'))
|
||||
keymap.set('n', 'gR', '<cmd>Telescope lsp_references<cr>', opts('Show LSP references'))
|
||||
keymap.set('n', 'gd', '<cmd>Telescope lsp_definitions<cr>', opts('Go to definition'))
|
||||
keymap.set('n', 'gr', '<cmd>Telescope lsp_references<cr>', opts('Show LSP references'))
|
||||
keymap.set('n', 'gD', vim.lsp.buf.declaration, opts('Go to declaration'))
|
||||
keymap.set('n', 'gd', '<cmd>Telescope lsp_definitions<cr>', opts('Show LSP definitions'))
|
||||
keymap.set('n', 'gi', '<cmd>Telescope lsp_implementations<cr>', opts('Show LSP implementations'))
|
||||
keymap.set('n', 'gt', '<cmd>Telescope lsp_type_definitions<cr>', opts('Show LSP type definitions'))
|
||||
keymap.set('n', 'K', vim.lsp.buf.hover, opts('Show documentation'))
|
||||
keymap.set('n', 'gE', vim.diagnostic.open_float, opts('Show diagnostics'))
|
||||
keymap.set({ 'n', 'v' }, 'ga', vim.lsp.buf.code_action, opts('Show available code actions'))
|
||||
wk.add({
|
||||
@ -37,89 +43,78 @@ return {
|
||||
end,
|
||||
})
|
||||
|
||||
-- Apply default capabilities to all servers
|
||||
local capabilities = cmp_nvim_lsp.default_capabilities()
|
||||
mason_lspconfig.setup_handlers({
|
||||
-- default handler for installed servers
|
||||
function(server_name)
|
||||
lspconfig[server_name].setup({
|
||||
capabilities = capabilities,
|
||||
})
|
||||
end,
|
||||
-- TODO add lsp configs
|
||||
['lua_ls'] = function()
|
||||
lspconfig['lua_ls'].setup({
|
||||
capabilities = capabilities,
|
||||
settings = {
|
||||
Lua = {
|
||||
diagnostics = {
|
||||
globals = { 'vim' },
|
||||
},
|
||||
completion = {
|
||||
callSnippet = 'Replace'
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
end,
|
||||
['emmet_ls'] = function()
|
||||
-- capabilities.textDocument.completion.completionItem.snippetSupport = true
|
||||
lspconfig['emmet_ls'].setup({
|
||||
capabilities = capabilities,
|
||||
filetypes = {
|
||||
'html',
|
||||
'css',
|
||||
'sass',
|
||||
'scss',
|
||||
'less',
|
||||
'javascript',
|
||||
'javascriptreact',
|
||||
'typescript',
|
||||
'typescriptreact',
|
||||
'svelte',
|
||||
'vue',
|
||||
}
|
||||
})
|
||||
end,
|
||||
['ts_ls'] = function()
|
||||
lspconfig['ts_ls'].setup({
|
||||
capabilities = capabilities,
|
||||
cmd = { "typescript-language-server", "--stdio" },
|
||||
init_options = {
|
||||
hostInfo = "neovim",
|
||||
-- preferences = {
|
||||
-- includePackagesJsonAutoImports = "on",
|
||||
-- }
|
||||
}
|
||||
})
|
||||
end,
|
||||
-- ['pyright'] = function()
|
||||
-- lspconfig['pyright'].setup({
|
||||
-- -- cmd = { "pyright-langserver", "--stdio" },
|
||||
-- -- filetypes = { "python" },
|
||||
-- -- root_dir = function(filename)
|
||||
-- -- return util.root_pattern(unpack(root_files))(filename) or util.path.dirname(filename)
|
||||
-- -- end,
|
||||
-- -- settings = {
|
||||
-- -- python = {
|
||||
-- -- analysis = {
|
||||
-- -- autoSearchPaths = true,
|
||||
-- -- diagnosticMode = "workspace",
|
||||
-- -- useLibraryCodeForTypes = true
|
||||
-- -- }
|
||||
-- -- }
|
||||
-- -- }
|
||||
-- capabilities = capabilities,
|
||||
-- settings = {
|
||||
-- python = {
|
||||
-- analysis = {
|
||||
-- useLibraryCodeForTypes = true,
|
||||
-- typeCheckingMode = "basic",
|
||||
-- -- configPath = vim.fn.expand('./pyrightconfig.json')
|
||||
-- }
|
||||
-- }
|
||||
-- },
|
||||
-- })
|
||||
-- end,
|
||||
vim.lsp.config('*', { capabilities = capabilities })
|
||||
|
||||
-- lua_ls: suppress vim global warnings, improve completion
|
||||
vim.lsp.config('lua_ls', {
|
||||
settings = {
|
||||
Lua = {
|
||||
diagnostics = {
|
||||
globals = { 'vim' },
|
||||
},
|
||||
completion = {
|
||||
callSnippet = 'Replace',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
-- ts_ls: TypeScript/JavaScript/JSX/TSX
|
||||
vim.lsp.config('ts_ls', {
|
||||
cmd = { 'typescript-language-server', '--stdio' },
|
||||
filetypes = {
|
||||
'javascript',
|
||||
'javascriptreact',
|
||||
'typescript',
|
||||
'typescriptreact',
|
||||
},
|
||||
init_options = {
|
||||
hostInfo = 'neovim',
|
||||
},
|
||||
})
|
||||
|
||||
-- eslint: linting for JS/TS/React projects
|
||||
vim.lsp.config('eslint', {
|
||||
settings = {
|
||||
workingDirectories = { mode = 'auto' },
|
||||
},
|
||||
})
|
||||
|
||||
-- Explicitly enable all mason-managed servers.
|
||||
-- This is belt-and-suspenders alongside mason-lspconfig's automatic_enable,
|
||||
-- ensuring servers start regardless of mason-lspconfig version.
|
||||
vim.lsp.enable({ 'lua_ls', 'html', 'cssls', 'bashls', 'ts_ls', 'eslint', 'tailwindcss', 'mdx_analyzer', 'rust_analyzer' })
|
||||
|
||||
-- mdx_analyzer: needs typescript SDK path to find tsserverlibrary.js
|
||||
vim.lsp.config('mdx_analyzer', {
|
||||
init_options = {
|
||||
typescript = {
|
||||
tsdk = vim.fn.stdpath('data') .. '/mason/packages/typescript-language-server/node_modules/typescript/lib',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
-- hyprls: not managed by mason, enable manually
|
||||
vim.lsp.config('hyprls', {
|
||||
cmd = { 'hyprls' },
|
||||
filetypes = { 'hyprlang' },
|
||||
root_dir = function(fname)
|
||||
return vim.fn.getcwd()
|
||||
end,
|
||||
})
|
||||
vim.lsp.enable('hyprls')
|
||||
|
||||
vim.lsp.config('qmlls', {
|
||||
cmd = { 'qmlls6' },
|
||||
})
|
||||
vim.lsp.enable('qmlls')
|
||||
|
||||
-- vim.lsp.config('postgres_lsp', {
|
||||
-- cmd = { 'postgres-language-server', 'lsp-proxy' },
|
||||
-- })
|
||||
vim.lsp.enable('postgres_lsp')
|
||||
|
||||
end,
|
||||
}
|
||||
|
||||
@ -1,45 +1,52 @@
|
||||
return {
|
||||
{ 'williamboman/mason.nvim', commit = '4da89f3' },
|
||||
{ 'williamboman/mason-lspconfig.nvim', commit = '1a31f82' },
|
||||
{ 'WhoIsSethDaniel/mason-tool-installer.nvim' },
|
||||
config = function()
|
||||
local mason = require('mason')
|
||||
local mason_lspconfig = require('mason-lspconfig')
|
||||
local mason_tool_installer = require('mason-tool-installer')
|
||||
{
|
||||
'mason-org/mason.nvim',
|
||||
dependencies = {
|
||||
'mason-org/mason-lspconfig.nvim',
|
||||
'WhoIsSethDaniel/mason-tool-installer.nvim',
|
||||
},
|
||||
config = function()
|
||||
local mason = require('mason')
|
||||
local mason_lspconfig = require('mason-lspconfig')
|
||||
local mason_tool_installer = require('mason-tool-installer')
|
||||
|
||||
mason.setup({
|
||||
ui = {
|
||||
icons = {
|
||||
package_installed = "✓",
|
||||
package_pending = "➜",
|
||||
package_uninstalled = "✗",
|
||||
mason.setup({
|
||||
ui = {
|
||||
icons = {
|
||||
package_installed = '✓',
|
||||
package_pending = '➜',
|
||||
package_uninstalled = '✗',
|
||||
},
|
||||
},
|
||||
},
|
||||
pip = {
|
||||
upgrade_pip = true,
|
||||
}
|
||||
})
|
||||
pip = {
|
||||
upgrade_pip = true,
|
||||
},
|
||||
})
|
||||
|
||||
mason_lspconfig.setup({
|
||||
ensure_installed = {
|
||||
'lua_ls',
|
||||
'html',
|
||||
'cssls',
|
||||
-- 'pyright',
|
||||
-- 'emmet_ls',
|
||||
'bashls',
|
||||
'ts_ls',
|
||||
},
|
||||
})
|
||||
mason_lspconfig.setup({
|
||||
ensure_installed = {
|
||||
'lua_ls',
|
||||
'html',
|
||||
'cssls',
|
||||
-- 'pyright',
|
||||
-- 'emmet_ls',
|
||||
'bashls',
|
||||
'ts_ls',
|
||||
'eslint',
|
||||
'tailwindcss',
|
||||
'rust_analyzer',
|
||||
},
|
||||
})
|
||||
|
||||
mason_tool_installer.setup({
|
||||
ensure_installed = {
|
||||
'prettier',
|
||||
'isort',
|
||||
-- 'black',
|
||||
-- 'pylint',
|
||||
-- 'eslint_d',
|
||||
}
|
||||
})
|
||||
end,
|
||||
mason_tool_installer.setup({
|
||||
ensure_installed = {
|
||||
'prettier',
|
||||
'isort',
|
||||
-- 'black',
|
||||
-- 'pylint',
|
||||
-- 'eslint_d',
|
||||
},
|
||||
})
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
@ -8,7 +8,17 @@ return {
|
||||
|
||||
local sources = {
|
||||
null_ls.builtins.formatting.prettier.with({
|
||||
filetypes = { 'javascript', 'typescript', 'typescriptreact', 'css', 'html', 'json', 'markdown', 'yaml' }
|
||||
filetypes = {
|
||||
'javascript',
|
||||
'javascriptreact',
|
||||
'typescript',
|
||||
'typescriptreact',
|
||||
'css',
|
||||
'html',
|
||||
'json',
|
||||
'markdown',
|
||||
'yaml',
|
||||
},
|
||||
}),
|
||||
-- null_ls.builtins.formatting.black,
|
||||
-- null_ls.builtins.formatting.djlint,
|
||||
@ -19,16 +29,26 @@ return {
|
||||
|
||||
null_ls.setup({
|
||||
sources = sources,
|
||||
-- on_attach = function(client, bufnr)
|
||||
-- if client.server_capabilities.documentFormattingProvider then
|
||||
-- vim.
|
||||
-- end
|
||||
-- end,
|
||||
})
|
||||
|
||||
local keymap = vim.keymap
|
||||
local fmt_func = function()
|
||||
vim.lsp.buf.format({ async = true })
|
||||
local null_ls_active = false
|
||||
for _, client in ipairs(vim.lsp.get_clients({ bufnr = 0 })) do
|
||||
if client.name == "null-ls" then
|
||||
null_ls_active = true
|
||||
break
|
||||
end
|
||||
end
|
||||
vim.lsp.buf.format({
|
||||
async = true,
|
||||
filter = function(client)
|
||||
if null_ls_active then
|
||||
return client.name == "null-ls"
|
||||
end
|
||||
return true
|
||||
end,
|
||||
})
|
||||
end
|
||||
keymap.set('n', '<leader>ii', fmt_func, { noremap = true, silent = true })
|
||||
|
||||
|
||||
8
roles/nvim/files/lua/config/plugins/render-markdown.lua
Normal file
8
roles/nvim/files/lua/config/plugins/render-markdown.lua
Normal file
@ -0,0 +1,8 @@
|
||||
return {
|
||||
'MeanderingProgrammer/render-markdown.nvim',
|
||||
dependencies = {
|
||||
'nvim-treesitter/nvim-treesitter',
|
||||
'nvim-mini/mini.nvim',
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
return {
|
||||
'nvim-telescope/telescope.nvim',
|
||||
branch = '0.1.x',
|
||||
'nvim-telescope/telescope.nvim', -- 0.1.x dropped; master has nvim 0.12 compat (ft_to_lang removal)
|
||||
dependencies = {
|
||||
'nvim-lua/plenary.nvim',
|
||||
{ "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
|
||||
@ -13,9 +12,12 @@ return {
|
||||
-- local trouble = require('trouble')
|
||||
-- local trouble_telescope = require('trouble.sources.telescope')
|
||||
|
||||
local ignore_patterns = { "node_modules/" }
|
||||
|
||||
telescope.setup({
|
||||
defaults = {
|
||||
path_display = { 'smart' },
|
||||
file_ignore_patterns = ignore_patterns,
|
||||
},
|
||||
})
|
||||
|
||||
@ -26,9 +28,13 @@ return {
|
||||
local keymap = vim.keymap
|
||||
local builtin = require('telescope.builtin')
|
||||
|
||||
keymap.set('n', '<leader>ff', builtin.find_files, opts)
|
||||
keymap.set('n', '<leader>ff', function() builtin.find_files({ hidden = true }) end, opts)
|
||||
keymap.set('n', '<leader>fh', builtin.oldfiles, opts)
|
||||
keymap.set('n', '<leader>fs', builtin.live_grep, opts)
|
||||
keymap.set('n', '<leader>fs', function ()
|
||||
builtin.live_grep({
|
||||
file_ignore_patterns = vim.list_extend(vim.deepcopy(ignore_patterns), { "supabase/migrations/" })
|
||||
})
|
||||
end, opts)
|
||||
keymap.set('n', '<leader>fc', builtin.grep_string, opts)
|
||||
|
||||
local wk = require('which-key')
|
||||
|
||||
@ -1,23 +1,18 @@
|
||||
return {
|
||||
"nvim-treesitter/nvim-treesitter",
|
||||
branch = "main", -- master is frozen; main required for nvim 0.12+
|
||||
event = { "BufReadPre", "BufNewFile" },
|
||||
build = ":TSUpdate",
|
||||
dependencies = {
|
||||
"windwp/nvim-ts-autotag",
|
||||
},
|
||||
config = function()
|
||||
local treesitter = require('nvim-treesitter.configs')
|
||||
|
||||
treesitter.setup({
|
||||
require('nvim-treesitter').setup({
|
||||
auto_install = true,
|
||||
sync_install = false,
|
||||
highlight = {
|
||||
enable = true,
|
||||
additional_vim_regex_highlighting = false,
|
||||
},
|
||||
prefer_git = true, -- tarball downloads fail for some parsers on 0.12
|
||||
highlight = { enable = true },
|
||||
indent = { enable = true },
|
||||
autotag = { enable = true },
|
||||
ensure_installed = {
|
||||
install = {
|
||||
'json',
|
||||
'javascript',
|
||||
'typescript',
|
||||
@ -35,17 +30,10 @@ return {
|
||||
'python',
|
||||
'gitignore',
|
||||
'c',
|
||||
},
|
||||
ignore_install = {},
|
||||
incremental_selection = {
|
||||
enable = true,
|
||||
keymaps = {
|
||||
init_selection = '<C-Space>',
|
||||
node_incremental = '<C-Space>',
|
||||
scope_incremental = false,
|
||||
node_decremental = '<bs>',
|
||||
},
|
||||
'mdx',
|
||||
},
|
||||
})
|
||||
|
||||
require('nvim-ts-autotag').setup()
|
||||
end,
|
||||
}
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
return {
|
||||
"linux-cultist/venv-selector.nvim",
|
||||
dependencies = {
|
||||
"neovim/nvim-lspconfig",
|
||||
-- "mfussenegger/nvim-dap", "mfussenegger/nvim-dap-python", --optional
|
||||
{ "nvim-telescope/telescope.nvim", branch = "0.1.x", dependencies = { "nvim-lua/plenary.nvim" } },
|
||||
},
|
||||
lazy = false,
|
||||
branch = "regexp", -- This is the regexp branch, use this for the new version
|
||||
config = function()
|
||||
require("venv-selector").setup({
|
||||
-- settings = {
|
||||
-- search = {
|
||||
-- my_envs = {
|
||||
-- command = "fd python$ ~/git/cerenim/backend/"
|
||||
-- }
|
||||
-- }
|
||||
-- }
|
||||
})
|
||||
end,
|
||||
keys = {
|
||||
{ ",v", "<cmd>VenvSelect<cr>" },
|
||||
},
|
||||
}
|
||||
@ -1,11 +1,13 @@
|
||||
---
|
||||
- name: Install neovim
|
||||
package:
|
||||
package:
|
||||
name: neovim
|
||||
state: present
|
||||
become: yes
|
||||
become_user: root
|
||||
|
||||
- name: Symlink neovim
|
||||
file:
|
||||
file:
|
||||
src: "{{ role_path }}/files"
|
||||
dest: "{{ config_dir }}/nvim"
|
||||
state: link
|
||||
|
||||
140
roles/quickshell/files/bar/Bar.qml
Normal file
140
roles/quickshell/files/bar/Bar.qml
Normal file
@ -0,0 +1,140 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import "../components"
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
implicitHeight: 40
|
||||
color: Theme.bg
|
||||
|
||||
readonly property string screenName: modelData.name
|
||||
property string activePopup: ""
|
||||
|
||||
readonly property int bw: Theme.borderWidth // 2
|
||||
readonly property int pad: Theme.enclosureMargin // 3
|
||||
|
||||
// ── Bar bottom border ─────────────────────────────────────────────
|
||||
Rectangle {
|
||||
anchors { bottom: parent.bottom; left: parent.left; right: parent.right }
|
||||
height: root.bw
|
||||
color: Theme.border
|
||||
}
|
||||
|
||||
// ── Bar content ───────────────────────────────────────────────────
|
||||
Item {
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 8
|
||||
rightMargin: 8
|
||||
topMargin: root.bw
|
||||
bottomMargin: root.bw
|
||||
}
|
||||
|
||||
Workspaces {
|
||||
anchors { left: parent.left; verticalCenter: parent.verticalCenter }
|
||||
screenName: root.screenName
|
||||
}
|
||||
|
||||
MusicPlayer {
|
||||
id: musicChip
|
||||
anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
|
||||
onClicked: root.activePopup = root.activePopup === "controls" ? "" : "controls"
|
||||
}
|
||||
|
||||
Row {
|
||||
id: rightRow
|
||||
anchors { right: parent.right; verticalCenter: parent.verticalCenter }
|
||||
spacing: 8
|
||||
|
||||
SysTray {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
barWindow: root
|
||||
}
|
||||
Battery {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
NetworkStatus {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
VolumeControl {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClickedLeft: root.activePopup = root.activePopup === "mixer" ? "" : "mixer"
|
||||
}
|
||||
Clock {
|
||||
id: clockDisp
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: root.activePopup = root.activePopup === "calendar" ? "" : "calendar"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Music controls popup ──────────────────────────────────────────
|
||||
PopoutWindow {
|
||||
popupName: "controls"
|
||||
activePopup: root.activePopup
|
||||
|
||||
anchor.window: root
|
||||
anchor.rect.y: root.implicitHeight - root.bw - Theme.radius
|
||||
anchor.rect.x: Math.round((root.width - implicitWidth) / 2)
|
||||
|
||||
implicitWidth: Math.max(musicChip.implicitWidth + 2 * root.pad,
|
||||
ctrlContent.implicitWidth + 2 * root.bw)
|
||||
implicitHeight: ctrlContent.implicitHeight + root.bw + Theme.radius
|
||||
|
||||
MusicPlayerControls {
|
||||
id: ctrlContent
|
||||
anchors { left: parent.left; right: parent.right }
|
||||
player: musicChip.player
|
||||
}
|
||||
}
|
||||
|
||||
// ── Calendar popup ────────────────────────────────────────────────
|
||||
PopoutWindow {
|
||||
popupName: "calendar"
|
||||
activePopup: root.activePopup
|
||||
|
||||
anchor.window: root
|
||||
anchor.rect.y: root.implicitHeight - root.bw - Theme.radius
|
||||
readonly property real pw: Math.max(rightRow.width + 2 * root.pad,
|
||||
calContent.implicitWidth + 2 * root.bw)
|
||||
anchor.rect.x: root.width - pw
|
||||
|
||||
implicitWidth: pw
|
||||
implicitHeight: calContent.implicitHeight + root.bw + Theme.radius
|
||||
|
||||
CalendarContent {
|
||||
id: calContent
|
||||
anchors { left: parent.left; right: parent.right }
|
||||
now: clockDisp.now
|
||||
}
|
||||
}
|
||||
|
||||
// ── Volume mixer popup ────────────────────────────────────────────
|
||||
PopoutWindow {
|
||||
popupName: "mixer"
|
||||
activePopup: root.activePopup
|
||||
|
||||
anchor.window: root
|
||||
anchor.rect.y: root.implicitHeight - root.bw - Theme.radius
|
||||
readonly property real pw: Math.max(rightRow.width + 2 * root.pad,
|
||||
mixContent.implicitWidth + 2 * root.bw)
|
||||
anchor.rect.x: root.width - pw
|
||||
|
||||
implicitWidth: pw
|
||||
implicitHeight: mixContent.implicitHeight + root.bw + Theme.radius
|
||||
|
||||
VolumeMixerContent {
|
||||
id: mixContent
|
||||
anchors { left: parent.left; right: parent.right }
|
||||
}
|
||||
}
|
||||
}
|
||||
84
roles/quickshell/files/bar/Battery.qml
Normal file
84
roles/quickshell/files/bar/Battery.qml
Normal file
@ -0,0 +1,84 @@
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitWidth: hasBattery ? row.implicitWidth + 10 : 0
|
||||
implicitHeight: 24
|
||||
visible: hasBattery
|
||||
|
||||
property bool hasBattery: false
|
||||
property int percent: 0
|
||||
property string status: "Unknown" // "Charging", "Discharging", "Full", "Unknown"
|
||||
|
||||
// \uf0e7 = bolt (charging), \uf240-\uf244 = FA battery full…empty
|
||||
readonly property string battIcon: {
|
||||
if (status === "Charging" || status === "Full") return "\uf0e7"
|
||||
if (percent <= 10) return "\uf244"
|
||||
if (percent <= 25) return "\uf243"
|
||||
if (percent <= 50) return "\uf242"
|
||||
if (percent <= 75) return "\uf241"
|
||||
return "\uf240"
|
||||
}
|
||||
|
||||
readonly property bool isLow: percent <= 20 && status === "Discharging"
|
||||
|
||||
Timer {
|
||||
interval: 30000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: battProc.running = true
|
||||
}
|
||||
|
||||
Process {
|
||||
id: battProc
|
||||
// Find first BAT* supply; print "NONE" if none exists
|
||||
command: ["sh", "-c",
|
||||
"BAT=$(ls /sys/class/power_supply/ 2>/dev/null | grep -m1 '^BAT'); " +
|
||||
"[ -z \"$BAT\" ] && echo NONE && exit 0; " +
|
||||
"echo \"$(cat /sys/class/power_supply/$BAT/capacity):$(cat /sys/class/power_supply/$BAT/status)\""
|
||||
]
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: data => {
|
||||
const line = data.trim()
|
||||
if (line === "" || line === "NONE") {
|
||||
root.hasBattery = false
|
||||
return
|
||||
}
|
||||
const sep = line.indexOf(":")
|
||||
if (sep < 0) return
|
||||
root.percent = parseInt(line.substring(0, sep)) || 0
|
||||
root.status = line.substring(sep + 1)
|
||||
root.hasBattery = true
|
||||
}
|
||||
}
|
||||
onExited: running = false
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.battIcon
|
||||
font.family: "JetBrainsMono Nerd Font Mono"
|
||||
font.pixelSize: 14
|
||||
color: root.status === "Charging" ? Theme.accent
|
||||
: root.isLow ? "#cc3333"
|
||||
: Theme.text
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.percent + "%"
|
||||
font.pixelSize: 11
|
||||
color: root.isLow ? "#cc3333" : Theme.textDim
|
||||
}
|
||||
}
|
||||
}
|
||||
139
roles/quickshell/files/bar/CalendarContent.qml
Normal file
139
roles/quickshell/files/bar/CalendarContent.qml
Normal file
@ -0,0 +1,139 @@
|
||||
import QtQuick
|
||||
import "../components"
|
||||
|
||||
// Calendar grid – a plain Item so it can live inside an expanding section border.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var now: new Date()
|
||||
|
||||
property int viewYear: now.getFullYear()
|
||||
property int viewMonth: now.getMonth() // 0-based
|
||||
|
||||
readonly property var monthNames: [
|
||||
"January","February","March","April","May","June",
|
||||
"July","August","September","October","November","December"
|
||||
]
|
||||
readonly property int daysInMonth: new Date(viewYear, viewMonth + 1, 0).getDate()
|
||||
// First weekday of month: Mon=0 … Sun=6
|
||||
readonly property int startOffset: (new Date(viewYear, viewMonth, 1).getDay() + 6) % 7
|
||||
readonly property int totalCells: Math.ceil((startOffset + daysInMonth) / 7) * 7
|
||||
|
||||
readonly property int todayYear: now.getFullYear()
|
||||
readonly property int todayMonth: now.getMonth()
|
||||
readonly property int todayDay: now.getDate()
|
||||
|
||||
// All widths derived from this constant — never from parent.width —
|
||||
// to avoid Column polish loops when the popup is wider than the grid.
|
||||
readonly property int cellW: 28
|
||||
readonly property int cellGap: 2
|
||||
readonly property int gridW: 7 * cellW + 6 * cellGap // 208
|
||||
|
||||
implicitWidth: gridW + 24
|
||||
implicitHeight: calLayout.implicitHeight + 16
|
||||
|
||||
Column {
|
||||
id: calLayout
|
||||
// Centre the fixed-width grid inside the (potentially wider) popup.
|
||||
// No left+right anchors → implicitWidth stays at gridW, no resize loop.
|
||||
anchors { top: parent.top; topMargin: 8; horizontalCenter: parent.horizontalCenter }
|
||||
spacing: 6
|
||||
|
||||
// Month navigation — all child widths are explicit constants
|
||||
Row {
|
||||
spacing: 0
|
||||
|
||||
Text {
|
||||
text: "‹"
|
||||
color: Theme.text
|
||||
font.pixelSize: 16
|
||||
width: 20
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.viewMonth === 0) { root.viewMonth = 11; root.viewYear-- }
|
||||
else root.viewMonth--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.monthNames[root.viewMonth] + " " + root.viewYear
|
||||
color: Theme.text
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
width: root.gridW - 40 // fixed constant, not parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "›"
|
||||
color: Theme.text
|
||||
font.pixelSize: 16
|
||||
width: 20
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.viewMonth === 11) { root.viewMonth = 0; root.viewYear++ }
|
||||
else root.viewMonth++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Weekday headers
|
||||
Row {
|
||||
spacing: root.cellGap
|
||||
Repeater {
|
||||
model: ["Mo","Tu","We","Th","Fr","Sa","Su"]
|
||||
Text {
|
||||
text: modelData
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 10
|
||||
width: root.cellW
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Day grid
|
||||
Grid {
|
||||
columns: 7
|
||||
spacing: root.cellGap
|
||||
Repeater {
|
||||
model: root.totalCells
|
||||
delegate: Item {
|
||||
width: root.cellW
|
||||
height: 22
|
||||
readonly property int dayNum: index - root.startOffset + 1
|
||||
readonly property bool valid:
|
||||
index >= root.startOffset &&
|
||||
index < root.startOffset + root.daysInMonth
|
||||
readonly property bool isToday:
|
||||
valid &&
|
||||
root.viewYear === root.todayYear &&
|
||||
root.viewMonth === root.todayMonth &&
|
||||
dayNum === root.todayDay
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 3
|
||||
color: isToday ? Theme.accent : "transparent"
|
||||
visible: isToday
|
||||
}
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: parent.valid ? String(parent.dayNum) : ""
|
||||
color: parent.isToday ? Theme.text : Theme.textDim
|
||||
font.pixelSize: 11
|
||||
font.bold: parent.isToday
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
roles/quickshell/files/bar/Clock.qml
Normal file
47
roles/quickshell/files/bar/Clock.qml
Normal file
@ -0,0 +1,47 @@
|
||||
import QtQuick
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
signal clicked
|
||||
|
||||
implicitWidth: timeLabel.implicitWidth + 16
|
||||
implicitHeight: 24
|
||||
|
||||
property var now: new Date()
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: root.now = new Date()
|
||||
}
|
||||
|
||||
readonly property string display: {
|
||||
const d = now
|
||||
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
|
||||
const dd = String(d.getDate()).padStart(2, "0")
|
||||
const mm = String(d.getMonth() + 1).padStart(2, "0")
|
||||
const yyyy = d.getFullYear()
|
||||
const h = String(d.getHours()).padStart(2, "0")
|
||||
const m = String(d.getMinutes()).padStart(2, "0")
|
||||
const s = String(d.getSeconds()).padStart(2, "0")
|
||||
return days[d.getDay()] + " " + dd + "." + mm + "." + yyyy + " " + h + ":" + m + ":" + s
|
||||
}
|
||||
|
||||
Text {
|
||||
id: timeLabel
|
||||
anchors.centerIn: parent
|
||||
text: root.display
|
||||
color: Theme.text
|
||||
font.pixelSize: 12
|
||||
font.family: "JetBrainsMono Nerd Font Mono"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
}
|
||||
38
roles/quickshell/files/bar/MusicPlayer.qml
Normal file
38
roles/quickshell/files/bar/MusicPlayer.qml
Normal file
@ -0,0 +1,38 @@
|
||||
import Quickshell.Services.Mpris
|
||||
import QtQuick
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
signal clicked
|
||||
|
||||
// Pick the first playing player, fall back to first available
|
||||
readonly property var player: {
|
||||
const vals = Mpris.players.values
|
||||
for (const p of vals) {
|
||||
if (p.isPlaying) return p
|
||||
}
|
||||
return vals.length > 0 ? vals[0] : null
|
||||
}
|
||||
|
||||
visible: player !== null
|
||||
implicitWidth: player ? Math.min(240, titleLabel.implicitWidth + 20) : 0
|
||||
implicitHeight: 24
|
||||
|
||||
Text {
|
||||
id: titleLabel
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 20
|
||||
text: root.player?.trackTitle ?? ""
|
||||
color: Theme.text
|
||||
font.pixelSize: 12
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
}
|
||||
151
roles/quickshell/files/bar/MusicPlayerControls.qml
Normal file
151
roles/quickshell/files/bar/MusicPlayerControls.qml
Normal file
@ -0,0 +1,151 @@
|
||||
import Quickshell.Services.Mpris
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import "../components"
|
||||
|
||||
// Plain Item – lives inside the expanding center section border in Bar.qml.
|
||||
Item {
|
||||
id: root
|
||||
required property var player
|
||||
|
||||
implicitWidth: 280
|
||||
implicitHeight: layout.implicitHeight + 28
|
||||
|
||||
// Keep the progress binding live while playing, pause while seeking.
|
||||
FrameAnimation {
|
||||
running: (root.player?.isPlaying ?? false) && !progressArea.pressed
|
||||
onTriggered: root.player.positionChanged()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors { fill: parent; margins: 14 }
|
||||
spacing: 8
|
||||
|
||||
// Track info
|
||||
Text {
|
||||
text: root.player?.trackTitle ?? ""
|
||||
color: Theme.text
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.player?.trackArtist ?? ""
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 11
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
visible: (root.player?.trackArtist ?? "") !== ""
|
||||
Layout.topMargin: -4
|
||||
}
|
||||
|
||||
// Progress bar
|
||||
Item {
|
||||
visible: root.player?.positionSupported && root.player?.lengthSupported
|
||||
&& (root.player?.length ?? 0) > 0
|
||||
Layout.fillWidth: true
|
||||
height: 12
|
||||
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
height: 3
|
||||
radius: 2
|
||||
color: Theme.progressTrack
|
||||
|
||||
Rectangle {
|
||||
width: root.player?.length > 0
|
||||
? parent.width * (root.player.position / root.player.length)
|
||||
: 0
|
||||
height: parent.height
|
||||
radius: parent.radius
|
||||
color: Theme.accent
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: progressArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
function seek(mx) {
|
||||
if (!(root.player?.canSeek)) return
|
||||
const ratio = Math.max(0, Math.min(1, mx / width))
|
||||
root.player.position = ratio * (root.player?.length ?? 0)
|
||||
}
|
||||
|
||||
onClicked: seek(mouseX)
|
||||
onPositionChanged: if (pressed) seek(mouseX)
|
||||
}
|
||||
}
|
||||
|
||||
// Media controls
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
ControlButton {
|
||||
icon: "⏮"
|
||||
iconSize: 18
|
||||
enabled: root.player?.canGoPrevious ?? false
|
||||
onActivated: root.player.previous()
|
||||
}
|
||||
|
||||
Item { width: 8 }
|
||||
|
||||
ControlButton {
|
||||
icon: root.player?.isPlaying ? "⏸" : "▶"
|
||||
iconSize: 22
|
||||
enabled: true
|
||||
onActivated: root.player?.togglePlaying()
|
||||
}
|
||||
|
||||
Item { width: 8 }
|
||||
|
||||
ControlButton {
|
||||
icon: "⏭"
|
||||
iconSize: 18
|
||||
enabled: root.player?.canGoNext ?? false
|
||||
onActivated: root.player.next()
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
|
||||
component ControlButton: Rectangle {
|
||||
id: btn
|
||||
required property string icon
|
||||
required property int iconSize
|
||||
required property bool enabled
|
||||
signal activated
|
||||
|
||||
implicitWidth: label.implicitWidth + 16
|
||||
implicitHeight: label.implicitHeight + 10
|
||||
radius: Theme.radius
|
||||
color: btnArea.containsMouse ? Qt.rgba(1, 1, 1, 0.08) : 'transparent'
|
||||
Behavior on color { ColorAnimation { duration: 80 } }
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
text: btn.icon
|
||||
font.pixelSize: btn.iconSize
|
||||
color: btn.enabled ? Theme.text : Theme.textDisabled
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: btnArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: if (btn.enabled) btn.activated()
|
||||
}
|
||||
}
|
||||
}
|
||||
86
roles/quickshell/files/bar/NetworkStatus.qml
Normal file
86
roles/quickshell/files/bar/NetworkStatus.qml
Normal file
@ -0,0 +1,86 @@
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitWidth: row.implicitWidth + 10
|
||||
implicitHeight: 24
|
||||
|
||||
property string connType: "none" // "wifi", "ethernet", "none"
|
||||
property string connName: ""
|
||||
|
||||
// accumulate results here, flush to displayed props on exit
|
||||
property string _pendingType: "none"
|
||||
property string _pendingName: ""
|
||||
|
||||
// \uf1eb = FA wifi, \uf0e8 = FA sitemap (wired proxy), \uf127 = FA chain-broken
|
||||
readonly property string netIcon:
|
||||
connType === "wifi" ? "\uf1eb" :
|
||||
connType === "ethernet" ? "\uf0e8" : "\uf127"
|
||||
|
||||
function parseLine(line) {
|
||||
const idx1 = line.indexOf(":")
|
||||
if (idx1 < 0) return
|
||||
const idx2 = line.indexOf(":", idx1 + 1)
|
||||
if (idx2 < 0) return
|
||||
const type = line.substring(0, idx1)
|
||||
const state = line.substring(idx1 + 1, idx2)
|
||||
const conn = line.substring(idx2 + 1).trim()
|
||||
if (state === "connected" && (type === "wifi" || type === "ethernet")) {
|
||||
if (root._pendingType === "none" || type === "wifi") {
|
||||
root._pendingType = type
|
||||
root._pendingName = conn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 5000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
root._pendingType = "none"
|
||||
root._pendingName = ""
|
||||
netProc.running = true
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: netProc
|
||||
command: ["nmcli", "-t", "-f", "TYPE,STATE,CONNECTION", "dev"]
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: data => root.parseLine(data)
|
||||
}
|
||||
onExited: {
|
||||
root.connType = root._pendingType
|
||||
root.connName = root._pendingName
|
||||
running = false
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.netIcon
|
||||
font.family: "JetBrainsMono Nerd Font Mono"
|
||||
font.pixelSize: 14
|
||||
color: root.connType !== "none" ? Theme.text : Theme.textDim
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.connName.length > 16 ? root.connName.substring(0, 15) + "…" : root.connName
|
||||
font.pixelSize: 11
|
||||
color: Theme.textDim
|
||||
visible: root.connName !== ""
|
||||
}
|
||||
}
|
||||
}
|
||||
162
roles/quickshell/files/bar/PopoutWindow.qml
Normal file
162
roles/quickshell/files/bar/PopoutWindow.qml
Normal file
@ -0,0 +1,162 @@
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import "../components"
|
||||
|
||||
PopupWindow {
|
||||
id: self
|
||||
|
||||
property string popupName: ""
|
||||
property string activePopup: ""
|
||||
|
||||
visible: false
|
||||
color: "transparent"
|
||||
|
||||
default property alias content: contentSlot.data
|
||||
|
||||
readonly property int bw: Theme.borderWidth
|
||||
readonly property real r: Theme.radius
|
||||
|
||||
readonly property bool _open: activePopup === popupName
|
||||
on_OpenChanged: {
|
||||
if (_open) {
|
||||
hideTimer.stop();
|
||||
self.visible = true;
|
||||
fade.opacity = 1.0;
|
||||
} else {
|
||||
fade.opacity = 0.0;
|
||||
hideTimer.start();
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: 200
|
||||
onTriggered: self.visible = false
|
||||
}
|
||||
|
||||
Item {
|
||||
id: fade
|
||||
anchors.fill: parent
|
||||
opacity: 0.0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 180
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
}
|
||||
|
||||
// Background + border in one Shape so fill matches stroke exactly
|
||||
Shape {
|
||||
id: pbs
|
||||
anchors.fill: parent
|
||||
|
||||
ShapePath {
|
||||
strokeWidth: self.bw
|
||||
strokeColor: Theme.border
|
||||
fillColor: Theme.bg
|
||||
|
||||
// Start flush at top-left, hidden under bar
|
||||
startX: 0
|
||||
startY: self.r
|
||||
|
||||
// Left side down to bottom-left corner
|
||||
PathLine {
|
||||
x: 0
|
||||
y: pbs.height - self.r
|
||||
}
|
||||
|
||||
// Bottom-left corner — Counterclockwise for outward curve
|
||||
PathArc {
|
||||
x: self.r
|
||||
y: pbs.height
|
||||
radiusX: self.r
|
||||
radiusY: self.r
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
|
||||
// Bottom edge
|
||||
PathLine {
|
||||
x: pbs.width - self.r
|
||||
y: pbs.height
|
||||
}
|
||||
|
||||
// Bottom-right corner — Counterclockwise for outward curve
|
||||
PathArc {
|
||||
x: pbs.width
|
||||
y: pbs.height - self.r
|
||||
radiusX: self.r
|
||||
radiusY: self.r
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
|
||||
// Right side up, flush to top, hidden under bar
|
||||
PathLine {
|
||||
x: pbs.width
|
||||
y: self.r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // Top-left concave corner
|
||||
// Shape {
|
||||
// x: 0
|
||||
// y: 0
|
||||
// width: self.r
|
||||
// height: self.r
|
||||
// ShapePath {
|
||||
// fillColor: Theme.bg
|
||||
// strokeColor: "transparent"
|
||||
// startX: 0
|
||||
// startY: 0
|
||||
// PathLine {
|
||||
// x: self.r
|
||||
// y: 0
|
||||
// }
|
||||
// PathArc {
|
||||
// x: 0
|
||||
// y: self.r
|
||||
// radiusX: self.r
|
||||
// radiusY: self.r
|
||||
// direction: PathArc.Clockwise
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Top-right concave corner
|
||||
// Shape {
|
||||
// x: parent.width - self.r
|
||||
// y: 0
|
||||
// width: self.r
|
||||
// height: self.r
|
||||
// ShapePath {
|
||||
// fillColor: Theme.bg
|
||||
// strokeColor: "transparent"
|
||||
// startX: self.r
|
||||
// startY: 0
|
||||
// PathLine {
|
||||
// x: 0
|
||||
// y: 0
|
||||
// }
|
||||
// PathArc {
|
||||
// x: self.r
|
||||
// y: self.r
|
||||
// radiusX: self.r
|
||||
// radiusY: self.r
|
||||
// direction: PathArc.Counterclockwise
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Item {
|
||||
id: contentSlot
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: self.r + self.bw
|
||||
left: parent.left
|
||||
leftMargin: self.bw
|
||||
right: parent.right
|
||||
rightMargin: self.bw
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
65
roles/quickshell/files/bar/SysTray.qml
Normal file
65
roles/quickshell/files/bar/SysTray.qml
Normal file
@ -0,0 +1,65 @@
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property var barWindow
|
||||
implicitWidth: trayRow.width
|
||||
implicitHeight: 24
|
||||
|
||||
Row {
|
||||
id: trayRow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 3
|
||||
|
||||
Repeater {
|
||||
model: SystemTray.items
|
||||
|
||||
delegate: Item {
|
||||
id: trayDelegate
|
||||
required property var modelData
|
||||
|
||||
width: 24
|
||||
height: 24
|
||||
|
||||
// Hover highlight
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Theme.radius
|
||||
color: hoverArea.containsMouse ? Qt.rgba(1,1,1,0.08) : "transparent"
|
||||
Behavior on color { ColorAnimation { duration: 80 } }
|
||||
}
|
||||
|
||||
TrayIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: trayDelegate.modelData.icon
|
||||
size: 16
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: hoverArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: mouse => {
|
||||
const item = trayDelegate.modelData
|
||||
const wantsMenu = mouse.button === Qt.RightButton || item.onlyMenu
|
||||
if (wantsMenu && item.hasMenu) {
|
||||
// display() needs the quickshell PanelWindow (not QQuickWindow).
|
||||
// mapToItem(null) gives scene/window-local coordinates.
|
||||
const pos = trayDelegate.mapToItem(null, 0, trayDelegate.height)
|
||||
item.display(root.barWindow, Math.round(pos.x), Math.round(pos.y))
|
||||
} else if (!item.onlyMenu) {
|
||||
item.activate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
roles/quickshell/files/bar/VolumeControl.qml
Normal file
74
roles/quickshell/files/bar/VolumeControl.qml
Normal file
@ -0,0 +1,74 @@
|
||||
import Quickshell.Services.Pipewire
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitWidth: chip.implicitWidth + 10
|
||||
implicitHeight: 24
|
||||
|
||||
signal clickedLeft
|
||||
|
||||
PwObjectTracker {
|
||||
objects: Pipewire.nodes.values
|
||||
}
|
||||
|
||||
function safeVolume(node) {
|
||||
if (!node || !node.ready || !node.audio) return 0
|
||||
const v = node.audio.volume
|
||||
return (v !== undefined && !isNaN(v)) ? v : 0
|
||||
}
|
||||
function setVolume(node, v) {
|
||||
if (!node || !node.ready || !node.audio) return
|
||||
node.audio.volume = Math.max(0, Math.min(1, v))
|
||||
}
|
||||
function volIcon(vol, muted) {
|
||||
if (muted) return "\uf6a9"
|
||||
if (vol > 0.6) return "\uf028"
|
||||
if (vol > 0.2) return "\uf027"
|
||||
return "\uf026"
|
||||
}
|
||||
|
||||
readonly property var defaultSink: Pipewire.defaultAudioSink
|
||||
readonly property real defaultVolume: safeVolume(defaultSink)
|
||||
readonly property bool defaultMuted: defaultSink?.audio?.muted ?? false
|
||||
|
||||
Row {
|
||||
id: chip
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.volIcon(root.defaultVolume, root.defaultMuted)
|
||||
font.family: "JetBrainsMono Nerd Font Mono"
|
||||
font.pixelSize: 14
|
||||
color: root.defaultMuted ? Theme.textDim : Theme.text
|
||||
}
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.defaultMuted ? "mute" : Math.round(root.defaultVolume * 100) + "%"
|
||||
font.pixelSize: 11
|
||||
color: Theme.textDim
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
if (root.defaultSink?.ready && root.defaultSink?.audio)
|
||||
root.defaultSink.audio.muted = !root.defaultSink.audio.muted
|
||||
} else {
|
||||
root.clickedLeft()
|
||||
}
|
||||
}
|
||||
onWheel: wheel => {
|
||||
if (!root.defaultSink?.ready) return
|
||||
root.setVolume(root.defaultSink, root.defaultVolume + wheel.angleDelta.y / 120 * 0.05)
|
||||
}
|
||||
}
|
||||
}
|
||||
143
roles/quickshell/files/bar/VolumeMixerContent.qml
Normal file
143
roles/quickshell/files/bar/VolumeMixerContent.qml
Normal file
@ -0,0 +1,143 @@
|
||||
import Quickshell.Services.Pipewire
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import "../components"
|
||||
|
||||
// Volume mixer – a plain Item so it can live inside an expanding section border.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: 300
|
||||
implicitHeight: mixerCol.implicitHeight + 24
|
||||
|
||||
PwObjectTracker {
|
||||
objects: Pipewire.nodes.values
|
||||
}
|
||||
|
||||
function safeVolume(node) {
|
||||
if (!node || !node.ready || !node.audio) return 0
|
||||
const v = node.audio.volume
|
||||
return (v !== undefined && !isNaN(v)) ? v : 0
|
||||
}
|
||||
function setVolume(node, v) {
|
||||
if (!node || !node.ready || !node.audio) return
|
||||
node.audio.volume = Math.max(0, Math.min(1, v))
|
||||
}
|
||||
function volIcon(vol, muted) {
|
||||
if (muted) return "\uf6a9"
|
||||
if (vol > 0.6) return "\uf028"
|
||||
if (vol > 0.2) return "\uf027"
|
||||
return "\uf026"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: mixerCol
|
||||
anchors { fill: parent; margins: 12 }
|
||||
spacing: 4
|
||||
|
||||
Text {
|
||||
text: "Output Devices"
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 10
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 2
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Pipewire.nodes
|
||||
delegate: NodeRow {
|
||||
required property var modelData
|
||||
Layout.fillWidth: true
|
||||
node: modelData
|
||||
visible: modelData.isSink && !modelData.isStream && modelData.ready
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Applications"
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 10
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 6
|
||||
Layout.bottomMargin: 2
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Pipewire.nodes
|
||||
delegate: NodeRow {
|
||||
required property var modelData
|
||||
Layout.fillWidth: true
|
||||
node: modelData
|
||||
visible: modelData.isStream && !modelData.isSink && modelData.ready
|
||||
&& !(modelData.description ?? "").toLowerCase().includes("monitor")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component NodeRow: RowLayout {
|
||||
id: row
|
||||
required property var node
|
||||
spacing: 8
|
||||
implicitHeight: 28
|
||||
|
||||
Text {
|
||||
text: root.volIcon(root.safeVolume(row.node), row.node.audio?.muted ?? false)
|
||||
font.family: "JetBrainsMono Nerd Font Mono"
|
||||
font.pixelSize: 13
|
||||
color: (row.node.audio?.muted ?? false) ? Theme.textDim : Theme.text
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (row.node.ready && row.node.audio)
|
||||
row.node.audio.muted = !row.node.audio.muted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: row.node.description || row.node.nickname || row.node.name || "?"
|
||||
font.pixelSize: 11
|
||||
color: Theme.textDim
|
||||
Layout.preferredWidth: 90
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 16
|
||||
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
height: 3
|
||||
radius: 2
|
||||
color: Theme.progressTrack
|
||||
|
||||
Rectangle {
|
||||
width: parent.width * Math.min(1, root.safeVolume(row.node))
|
||||
height: parent.height
|
||||
radius: parent.radius
|
||||
color: Theme.accent
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors { fill: parent; topMargin: -4; bottomMargin: -4 }
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
function seek(mx) { root.setVolume(row.node, mx / width) }
|
||||
onClicked: seek(mouseX)
|
||||
onPositionChanged: if (pressed) seek(mouseX)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: Math.round(root.safeVolume(row.node) * 100) + "%"
|
||||
font.pixelSize: 10
|
||||
color: Theme.textDim
|
||||
Layout.preferredWidth: 30
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
}
|
||||
}
|
||||
70
roles/quickshell/files/bar/WorkspaceButton.qml
Normal file
70
roles/quickshell/files/bar/WorkspaceButton.qml
Normal file
@ -0,0 +1,70 @@
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
// import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import "../components"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
required property var workspace
|
||||
required property string screenName
|
||||
|
||||
visible: workspace?.lastIpcObject?.monitor === screenName
|
||||
|
||||
implicitWidth: Math.max(32, iconsRow.implicitWidth + 14)
|
||||
implicitHeight: 18
|
||||
radius: 6
|
||||
color: workspace?.active ? Theme.accent : 'transparent'
|
||||
|
||||
Row {
|
||||
id: iconsRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 3
|
||||
|
||||
Repeater {
|
||||
model: root.workspace?.toplevels
|
||||
|
||||
delegate: Item {
|
||||
id: iconItem
|
||||
required property var modelData
|
||||
|
||||
implicitWidth: 16
|
||||
implicitHeight: 16
|
||||
|
||||
property string appClass: ""
|
||||
|
||||
Component.onCompleted: {
|
||||
var cls = modelData?.lastIpcObject?.["class"] ?? ""
|
||||
if (cls !== "") {
|
||||
appClass = cls
|
||||
} else if (modelData) {
|
||||
modelData.lastIpcObjectChanged.connect(function() {
|
||||
var c = iconItem.modelData?.lastIpcObject?.["class"] ?? ""
|
||||
if (c !== "") iconItem.appClass = c
|
||||
})
|
||||
Qt.callLater(Hyprland.refreshToplevels)
|
||||
}
|
||||
}
|
||||
|
||||
TrayIcon {
|
||||
anchors.fill: parent
|
||||
icon: iconItem.appClass
|
||||
size: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Workspace number shown when no windows are open
|
||||
Text {
|
||||
visible: (root.workspace?.toplevels?.values?.length ?? 0) === 0
|
||||
text: root.workspace?.id ?? ""
|
||||
color: root.workspace?.active ? Theme.text : Theme.textDim
|
||||
font.pixelSize: 11
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: Hyprland.dispatch("workspace " + root.workspace.id)
|
||||
}
|
||||
}
|
||||
22
roles/quickshell/files/bar/Workspaces.qml
Normal file
22
roles/quickshell/files/bar/Workspaces.qml
Normal file
@ -0,0 +1,22 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell.Hyprland
|
||||
import QtQuick
|
||||
|
||||
Row {
|
||||
id: root
|
||||
required property string screenName
|
||||
spacing: 4
|
||||
|
||||
Repeater {
|
||||
id: workspacesRepeater
|
||||
model: Hyprland.workspaces
|
||||
property string screenName: root.screenName
|
||||
|
||||
delegate: WorkspaceButton {
|
||||
required property var modelData
|
||||
workspace: modelData
|
||||
screenName: workspacesRepeater.screenName
|
||||
}
|
||||
}
|
||||
}
|
||||
13
roles/quickshell/files/bar/qmldir
Normal file
13
roles/quickshell/files/bar/qmldir
Normal file
@ -0,0 +1,13 @@
|
||||
Bar 1.0 Bar.qml
|
||||
Battery 1.0 Battery.qml
|
||||
PopoutWindow 1.0 PopoutWindow.qml
|
||||
CalendarContent 1.0 CalendarContent.qml
|
||||
Clock 1.0 Clock.qml
|
||||
MusicPlayer 1.0 MusicPlayer.qml
|
||||
MusicPlayerControls 1.0 MusicPlayerControls.qml
|
||||
NetworkStatus 1.0 NetworkStatus.qml
|
||||
SysTray 1.0 SysTray.qml
|
||||
VolumeControl 1.0 VolumeControl.qml
|
||||
VolumeMixerContent 1.0 VolumeMixerContent.qml
|
||||
Workspaces 1.0 Workspaces.qml
|
||||
WorkspaceButton 1.0 WorkspaceButton.qml
|
||||
11
roles/quickshell/files/components/Enclosure.qml
Normal file
11
roles/quickshell/files/components/Enclosure.qml
Normal file
@ -0,0 +1,11 @@
|
||||
import QtQuick
|
||||
import Quickshell.Widgets
|
||||
import "."
|
||||
|
||||
WrapperRectangle {
|
||||
radius: Theme.radius
|
||||
color: 'transparent'
|
||||
border.color: Theme.border
|
||||
border.width: Theme.borderWidth
|
||||
margin: Theme.enclosureMargin
|
||||
}
|
||||
19
roles/quickshell/files/components/Theme.qml
Normal file
19
roles/quickshell/files/components/Theme.qml
Normal file
@ -0,0 +1,19 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
// Core palette
|
||||
readonly property color bg: '#D9000000' // bar background (semi-transparent)
|
||||
readonly property color bgPopup: '#000000' // popup background (fully opaque)
|
||||
readonly property color accent: '#9B1A1A'
|
||||
readonly property color border: '#FFFFFF'
|
||||
readonly property color text: '#FFFFFF'
|
||||
readonly property color textDim: '#888888'
|
||||
readonly property color textDisabled: '#444444'
|
||||
readonly property color progressTrack: '#333333'
|
||||
|
||||
// Shape / sizing
|
||||
readonly property int radius: 16
|
||||
readonly property int borderWidth: 2
|
||||
readonly property int enclosureMargin: 3
|
||||
}
|
||||
103
roles/quickshell/files/components/TrayIcon.qml
Normal file
103
roles/quickshell/files/components/TrayIcon.qml
Normal file
@ -0,0 +1,103 @@
|
||||
// import QtQuick
|
||||
// import Quickshell.Widgets
|
||||
//
|
||||
// Item {
|
||||
// id: root
|
||||
//
|
||||
// property string icon: ""
|
||||
// property int size: 16
|
||||
//
|
||||
// implicitWidth: size
|
||||
// implicitHeight: size
|
||||
//
|
||||
// readonly property url resolvedSource: {
|
||||
// if (!icon) return ""
|
||||
// if (icon.startsWith("/") || icon.startsWith("file://") || icon.startsWith("image://"))
|
||||
// return icon
|
||||
// if (icon.includes("?path=")) {
|
||||
// const [name, path] = icon.split("?path=")
|
||||
// const baseName = name.slice(name.lastIndexOf("/") + 1)
|
||||
// return `file://${path}/${baseName}`
|
||||
// }
|
||||
// return `image://icon/${icon}`
|
||||
// }
|
||||
//
|
||||
// readonly property bool isIconTheme: resolvedSource.toString().startsWith("image://icon/")
|
||||
// readonly property bool hasSource: resolvedSource.toString() !== ""
|
||||
//
|
||||
// IconImage {
|
||||
// anchors.fill: parent
|
||||
// source: root.resolvedSource
|
||||
// visible: root.hasSource && root.isIconTheme
|
||||
// asynchronous: true
|
||||
// }
|
||||
//
|
||||
// Image {
|
||||
// anchors.fill: parent
|
||||
// source: root.hasSource && !root.isIconTheme ? root.resolvedSource : ""
|
||||
// visible: root.hasSource && !root.isIconTheme
|
||||
// asynchronous: true
|
||||
// fillMode: Image.PreserveAspectFit
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
// import Quickshell
|
||||
import Quickshell.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string icon: ""
|
||||
property int size: 16
|
||||
|
||||
implicitWidth: size
|
||||
implicitHeight: size
|
||||
|
||||
readonly property url resolvedSource: {
|
||||
if (!icon) return ""
|
||||
if (icon.startsWith("/") || icon.startsWith("file://") || icon.startsWith("image://"))
|
||||
return icon
|
||||
if (icon.includes("?path=")) {
|
||||
const [name, path] = icon.split("?path=")
|
||||
const baseName = name.slice(name.lastIndexOf("/") + 1)
|
||||
return `file://${path}/${baseName}`
|
||||
}
|
||||
// Use Quickshell's icon image provider instead of iconPath()
|
||||
return `image://icon/${icon}`
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
// asynchronous: true
|
||||
sourceComponent: root.resolvedSource && root.resolvedSource.toString() !== ""
|
||||
? root.resolvedSource.toString().startsWith("image://icon/")
|
||||
? iconComponent
|
||||
: imageComponent
|
||||
: null
|
||||
}
|
||||
|
||||
Component {
|
||||
id: iconComponent
|
||||
IconImage {
|
||||
source: root.resolvedSource
|
||||
asynchronous: true
|
||||
// mipmap: true
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: imageComponent
|
||||
Image {
|
||||
source: root.resolvedSource
|
||||
asynchronous: true
|
||||
// mipmap: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
roles/quickshell/files/components/qmldir
Normal file
3
roles/quickshell/files/components/qmldir
Normal file
@ -0,0 +1,3 @@
|
||||
singleton Theme 1.0 Theme.qml
|
||||
Enclosure 1.0 Enclosure.qml
|
||||
TrayIcon 1.0 TrayIcon.qml
|
||||
13
roles/quickshell/files/shell.qml
Normal file
13
roles/quickshell/files/shell.qml
Normal file
@ -0,0 +1,13 @@
|
||||
//@ pragma UseQApplication
|
||||
import Quickshell
|
||||
import "bar"
|
||||
|
||||
ShellRoot {
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
Bar { }
|
||||
// // modelData injected by Variants; Bar.qml binds screen: modelData internally
|
||||
// }
|
||||
}
|
||||
}
|
||||
51
roles/quickshell/tasks/main.yml
Normal file
51
roles/quickshell/tasks/main.yml
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
- name: Check if quickshell path is a symlink
|
||||
stat:
|
||||
path: "{{ config_dir }}/quickshell"
|
||||
register: quickshell_stat
|
||||
|
||||
- name: Remove existing quickshell symlink if present
|
||||
file:
|
||||
path: "{{ config_dir }}/quickshell"
|
||||
state: absent
|
||||
when: quickshell_stat.stat.exists and quickshell_stat.stat.islnk
|
||||
|
||||
- name: Create quickshell config directories
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
loop:
|
||||
- "{{ config_dir }}/quickshell"
|
||||
- "{{ config_dir }}/quickshell/bar"
|
||||
- "{{ config_dir }}/quickshell/components"
|
||||
|
||||
- name: Symlink quickshell config files
|
||||
file:
|
||||
src: "{{ role_path }}/files/{{ item.src }}"
|
||||
dest: "{{ config_dir }}/quickshell/{{ item.dest }}"
|
||||
state: link
|
||||
force: true
|
||||
loop:
|
||||
# Root
|
||||
- { src: shell.qml, dest: shell.qml }
|
||||
# Shared components module
|
||||
- { src: components/Theme.qml, dest: components/Theme.qml }
|
||||
- { src: components/Enclosure.qml, dest: components/Enclosure.qml }
|
||||
- { src: components/TrayIcon.qml, dest: components/TrayIcon.qml }
|
||||
- { src: components/qmldir, dest: components/qmldir }
|
||||
# Bar module (imported as "bar" in shell.qml)
|
||||
- { src: bar/Bar.qml, dest: bar/Bar.qml }
|
||||
- { src: bar/Battery.qml, dest: bar/Battery.qml }
|
||||
- { src: bar/CalendarContent.qml, dest: bar/CalendarContent.qml }
|
||||
- { src: bar/Clock.qml, dest: bar/Clock.qml }
|
||||
- { src: bar/MusicPlayer.qml, dest: bar/MusicPlayer.qml }
|
||||
- { src: bar/MusicPlayerControls.qml, dest: bar/MusicPlayerControls.qml }
|
||||
- { src: bar/NetworkStatus.qml, dest: bar/NetworkStatus.qml }
|
||||
- { src: bar/PopoutWindow.qml, dest: bar/PopoutWindow.qml }
|
||||
- { src: bar/SysTray.qml, dest: bar/SysTray.qml }
|
||||
- { src: bar/VolumeControl.qml, dest: bar/VolumeControl.qml }
|
||||
- { src: bar/VolumeMixerContent.qml, dest: bar/VolumeMixerContent.qml }
|
||||
- { src: bar/Workspaces.qml, dest: bar/Workspaces.qml }
|
||||
- { src: bar/WorkspaceButton.qml, dest: bar/WorkspaceButton.qml }
|
||||
- { src: bar/qmldir, dest: bar/qmldir }
|
||||
@ -2,14 +2,16 @@
|
||||
- name: Update package cache (Arch-based systems)
|
||||
pacman:
|
||||
update_cache: yes
|
||||
when: ansible_os_family == "Archlinux"
|
||||
when: ansible_facts.os_family == "Archlinux"
|
||||
become: yes
|
||||
become_user: root
|
||||
|
||||
- name: Install system packages
|
||||
package:
|
||||
name: "{{ system_packages }}"
|
||||
state: present
|
||||
become: yes
|
||||
become_user: root
|
||||
|
||||
- name: Ensure .config directory exists
|
||||
file:
|
||||
|
||||
8
roles/zsh/files/.zshenv
Normal file
8
roles/zsh/files/.zshenv
Normal file
@ -0,0 +1,8 @@
|
||||
export PATH="/home/johannes/.local/bin:/home/johannes/.cargo/bin:$PATH"
|
||||
# pnpm
|
||||
export PNPM_HOME="$HOME/.local/share/pnpm"
|
||||
case ":$PATH:" in
|
||||
*":$PNPM_HOME:"*) ;;
|
||||
*) export PATH="$PNPM_HOME:$PATH" ;;
|
||||
esac
|
||||
# pnpm end
|
||||
@ -49,3 +49,10 @@ fi
|
||||
|
||||
alias drun='docker run -it --network=host --device=/dev/kfd --device=/dev/dri --group-add=video --ipc=host --cap-add=SYS_PTRACE --security-opt seccomp=unconfined'
|
||||
|
||||
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/gcr/ssh"
|
||||
|
||||
bindkey '^H' backward-kill-word
|
||||
bindkey '^R' fzf-history-widget
|
||||
|
||||
eval "$(zoxide init zsh)"
|
||||
|
||||
|
||||
@ -18,3 +18,7 @@ rm() {
|
||||
print "⚠️ To use real rm, use /bin/rm"
|
||||
trash-put "$@"
|
||||
}
|
||||
|
||||
alias lg='lazygit'
|
||||
|
||||
alias tt='taskwarrior-tui'
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
# export DJANGO_SETTINGS_MODULE=config.settings.dev
|
||||
export DATABASE_HOST=127.0.0.1
|
||||
export DATABASE_NAME=cerenim
|
||||
export DATABASE_USER=brainhero
|
||||
export DATABASE_PORT=5432
|
||||
export DATABASE_DJANGO_SCHEMA=django
|
||||
export SECRET_KEY=abasdjkasd
|
||||
export DATABASE_PASSWORD=abcdefghijk
|
||||
@ -1 +1 @@
|
||||
source /usr/share/autojump/autojump.zsh
|
||||
source <(fzf --zsh)
|
||||
|
||||
@ -3,22 +3,25 @@
|
||||
package:
|
||||
name: zsh
|
||||
state: present
|
||||
become: yes
|
||||
become_user: root
|
||||
|
||||
- name: Install oh-my-zsh
|
||||
shell: |
|
||||
sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended
|
||||
args:
|
||||
creates: "{{ ansible_env.HOME }}/.oh-my-zsh"
|
||||
creates: "{{ home_dir }}/.oh-my-zsh"
|
||||
|
||||
- name: Check for custom zshrc
|
||||
stat:
|
||||
path: "{{ role_path }}/files/.zshrc"
|
||||
register: custom_zshrc
|
||||
|
||||
- name: Symlink custom zshrc
|
||||
- name: Symlink .zshenv
|
||||
file:
|
||||
src: "{{ role_path }}/files/.zshrc"
|
||||
dest: "{{ ansible_env.HOME }}/.zshrc"
|
||||
src: "{{ role_path }}/files/.zshenv"
|
||||
dest: "{{ home_dir }}/.zshenv"
|
||||
state: link
|
||||
force: yes
|
||||
|
||||
- name: Symlink .zshrc
|
||||
file:
|
||||
src: "{{ role_path }}/files/.zshrc"
|
||||
dest: "{{ home_dir }}/.zshrc"
|
||||
state: link
|
||||
force: yes
|
||||
when: custom_zshrc.stat.exists
|
||||
|
||||
Reference in New Issue
Block a user