aboutsummaryrefslogtreecommitdiff
path: root/linux/home
diff options
context:
space:
mode:
Diffstat (limited to 'linux/home')
-rw-r--r--linux/home/.config/Code/User/keybindings.json274
-rw-r--r--linux/home/.config/Code/User/settings.json734
-rw-r--r--linux/home/.config/Code/User/snippets/snippet.code-snippets126
-rw-r--r--linux/home/.config/Code/User/spellright.dict14
-rw-r--r--linux/home/.config/Code/User/vsc.css408
-rw-r--r--linux/home/.config/Code/User/vsc.js100
-rw-r--r--linux/home/.config/X11/.Xresources307
-rw-r--r--linux/home/.config/X11/.xbindkeysrc31
-rwxr-xr-xlinux/home/.config/X11/.xinitrc34
-rw-r--r--linux/home/.config/X11/.xprofile24
-rw-r--r--linux/home/.config/ags/.eslintrc.yml130
-rw-r--r--linux/home/.config/ags/.gitignore6
-rw-r--r--linux/home/.config/ags/assets/arrows-down.svg10
-rw-r--r--linux/home/.config/ags/assets/arrows-left.svg10
-rw-r--r--linux/home/.config/ags/assets/arrows-right.svg10
-rw-r--r--linux/home/.config/ags/assets/arrows-up.svg10
-rw-r--r--linux/home/.config/ags/assets/battery-flash-symbolic.svg4
-rw-r--r--linux/home/.config/ags/assets/bomb-kill.svg36
-rw-r--r--linux/home/.config/ags/assets/chat-bubbles-symbolic.svg5
-rw-r--r--linux/home/.config/ags/assets/controller-symbolic.svg4
-rw-r--r--linux/home/.config/ags/assets/controls-symbolic.svg5
-rw-r--r--linux/home/.config/ags/assets/dark-mode-symbolic.svg4
-rw-r--r--linux/home/.config/ags/assets/float.svg44
-rw-r--r--linux/home/.config/ags/assets/fullscreen.svg43
-rw-r--r--linux/home/.config/ags/assets/hourglass-symbolic.svg4
-rw-r--r--linux/home/.config/ags/assets/light-mode-symbolic.svg4
-rw-r--r--linux/home/.config/ags/assets/mixer-symbolic.svg6
-rw-r--r--linux/home/.config/ags/assets/nix-snowflake-symbolic.svg155
-rw-r--r--linux/home/.config/ags/assets/nixos-symbolic.svg155
-rw-r--r--linux/home/.config/ags/assets/nixos.svg277
-rw-r--r--linux/home/.config/ags/assets/osk.svg132
-rw-r--r--linux/home/.config/ags/assets/pinned.svg36
-rw-r--r--linux/home/.config/ags/assets/preferences-desktop-theme-symbolic.svg321
-rw-r--r--linux/home/.config/ags/assets/processor-symbolic.svg17
-rw-r--r--linux/home/.config/ags/assets/rotation.svg8
-rw-r--r--linux/home/.config/ags/assets/swapnext.svg8
-rw-r--r--linux/home/.config/ags/assets/tbox-close.svg49
-rw-r--r--linux/home/.config/ags/assets/terminal-symbolic.svg5
-rw-r--r--linux/home/.config/ags/assets/togglesplit.svg10
-rw-r--r--linux/home/.config/ags/assets/toolbars-symbolic.svg4
-rw-r--r--linux/home/.config/ags/assets/wp-next.svg10
-rw-r--r--linux/home/.config/ags/assets/wp-prev.svg10
-rw-r--r--linux/home/.config/ags/config.js46
-rw-r--r--linux/home/.config/ags/default.nix104
-rw-r--r--linux/home/.config/ags/greeter.js18
-rw-r--r--linux/home/.config/ags/greeter/auth.ts109
-rw-r--r--linux/home/.config/ags/greeter/greeter.scss64
-rw-r--r--linux/home/.config/ags/greeter/greeter.ts37
-rw-r--r--linux/home/.config/ags/greeter/session.ts20
-rw-r--r--linux/home/.config/ags/greeter/statusbar.ts46
-rw-r--r--linux/home/.config/ags/lib/battery.ts16
-rw-r--r--linux/home/.config/ags/lib/client.js134
-rw-r--r--linux/home/.config/ags/lib/cursorhover.js86
-rw-r--r--linux/home/.config/ags/lib/gtk.ts16
-rw-r--r--linux/home/.config/ags/lib/hyprland.ts80
-rw-r--r--linux/home/.config/ags/lib/iconUtils.js46
-rw-r--r--linux/home/.config/ags/lib/icons.ts186
-rw-r--r--linux/home/.config/ags/lib/init.ts19
-rw-r--r--linux/home/.config/ags/lib/matugen.ts113
-rw-r--r--linux/home/.config/ags/lib/notifications.ts16
-rw-r--r--linux/home/.config/ags/lib/option.ts115
-rw-r--r--linux/home/.config/ags/lib/session.ts16
-rw-r--r--linux/home/.config/ags/lib/tmux.ts14
-rw-r--r--linux/home/.config/ags/lib/utils.ts113
-rw-r--r--linux/home/.config/ags/lib/variables.ts47
-rw-r--r--linux/home/.config/ags/main.ts47
-rw-r--r--linux/home/.config/ags/options.ts261
-rw-r--r--linux/home/.config/ags/package.json18
-rw-r--r--linux/home/.config/ags/service/asusctl.ts52
-rw-r--r--linux/home/.config/ags/service/brightness.ts69
-rw-r--r--linux/home/.config/ags/service/colorpicker.ts56
-rw-r--r--linux/home/.config/ags/service/nix.ts109
-rw-r--r--linux/home/.config/ags/service/powermenu.ts43
-rw-r--r--linux/home/.config/ags/service/screenrecord.ts102
-rw-r--r--linux/home/.config/ags/service/wallpaper.ts127
-rw-r--r--linux/home/.config/ags/service/weather.ts59
-rw-r--r--linux/home/.config/ags/style/extra.scss67
-rw-r--r--linux/home/.config/ags/style/mixins/a11y-button.scss48
-rw-r--r--linux/home/.config/ags/style/mixins/button.scss70
-rw-r--r--linux/home/.config/ags/style/mixins/floating-widget.scss12
-rw-r--r--linux/home/.config/ags/style/mixins/hidden.scss15
-rw-r--r--linux/home/.config/ags/style/mixins/media.scss42
-rw-r--r--linux/home/.config/ags/style/mixins/scrollable.scss42
-rw-r--r--linux/home/.config/ags/style/mixins/slider.scss74
-rw-r--r--linux/home/.config/ags/style/mixins/spacing.scss53
-rw-r--r--linux/home/.config/ags/style/mixins/switch.scss16
-rw-r--r--linux/home/.config/ags/style/mixins/unset.scss9
-rw-r--r--linux/home/.config/ags/style/mixins/widget.scss7
-rw-r--r--linux/home/.config/ags/style/style.ts103
-rw-r--r--linux/home/.config/ags/tsconfig.json19
-rw-r--r--linux/home/.config/ags/widget/PopupWindow.ts156
-rw-r--r--linux/home/.config/ags/widget/RegularWindow.ts3
-rw-r--r--linux/home/.config/ags/widget/bar/Bar.ts57
-rw-r--r--linux/home/.config/ags/widget/bar/PanelButton.ts46
-rw-r--r--linux/home/.config/ags/widget/bar/ScreenCorners.ts25
-rw-r--r--linux/home/.config/ags/widget/bar/bar.scss242
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/BatteryBar.ts94
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/ColorPicker.ts37
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/Date.ts15
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/Launcher.ts49
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/Media.ts92
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/Messages.ts16
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/PowerMenu.ts15
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/ScreenRecord.ts21
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/SysTray.ts39
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/SystemIndicators.ts107
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/Taskbar.ts90
-rw-r--r--linux/home/.config/ags/widget/bar/buttons/Workspaces.ts66
-rw-r--r--linux/home/.config/ags/widget/bar/screencorner.scss51
-rw-r--r--linux/home/.config/ags/widget/datemenu/DateColumn.ts58
-rw-r--r--linux/home/.config/ags/widget/datemenu/DateMenu.ts36
-rw-r--r--linux/home/.config/ags/widget/datemenu/NotificationColumn.ts113
-rw-r--r--linux/home/.config/ags/widget/datemenu/datemenu.scss110
-rw-r--r--linux/home/.config/ags/widget/desktop/Desktop.ts40
-rw-r--r--linux/home/.config/ags/widget/dock/Dock.ts150
-rw-r--r--linux/home/.config/ags/widget/dock/FloatingDock.ts70
-rw-r--r--linux/home/.config/ags/widget/dock/ToolBox.ts122
-rw-r--r--linux/home/.config/ags/widget/dock/ToolBoxDock.ts57
-rw-r--r--linux/home/.config/ags/widget/dock/dock.scss73
-rw-r--r--linux/home/.config/ags/widget/launcher/AppLauncher.ts125
-rw-r--r--linux/home/.config/ags/widget/launcher/Launcher.ts134
-rw-r--r--linux/home/.config/ags/widget/launcher/NixRun.ts118
-rw-r--r--linux/home/.config/ags/widget/launcher/ShRun.ts89
-rw-r--r--linux/home/.config/ags/widget/launcher/launcher.scss143
-rw-r--r--linux/home/.config/ags/widget/notifications/Notification.ts138
-rw-r--r--linux/home/.config/ags/widget/notifications/NotificationPopups.ts90
-rw-r--r--linux/home/.config/ags/widget/notifications/notifications.scss79
-rw-r--r--linux/home/.config/ags/widget/osd/OSD.ts111
-rw-r--r--linux/home/.config/ags/widget/osd/Progress.ts74
-rw-r--r--linux/home/.config/ags/widget/osd/osd.scss26
-rw-r--r--linux/home/.config/ags/widget/overview/Overview.ts41
-rw-r--r--linux/home/.config/ags/widget/overview/Window.ts48
-rw-r--r--linux/home/.config/ags/widget/overview/Workspace.ts76
-rw-r--r--linux/home/.config/ags/widget/overview/overview.scss34
-rw-r--r--linux/home/.config/ags/widget/powermenu/PowerMenu.ts56
-rw-r--r--linux/home/.config/ags/widget/powermenu/Verification.ts47
-rw-r--r--linux/home/.config/ags/widget/powermenu/powermenu.scss110
-rw-r--r--linux/home/.config/ags/widget/quicksettings/QuickSettings.ts84
-rw-r--r--linux/home/.config/ags/widget/quicksettings/ToggleButton.ts154
-rw-r--r--linux/home/.config/ags/widget/quicksettings/quicksettings.scss177
-rw-r--r--linux/home/.config/ags/widget/quicksettings/widgets/Bluetooth.ts61
-rw-r--r--linux/home/.config/ags/widget/quicksettings/widgets/Brightness.ts23
-rw-r--r--linux/home/.config/ags/widget/quicksettings/widgets/DND.ts12
-rw-r--r--linux/home/.config/ags/widget/quicksettings/widgets/DarkMode.ts12
-rw-r--r--linux/home/.config/ags/widget/quicksettings/widgets/Header.ts69
-rw-r--r--linux/home/.config/ags/widget/quicksettings/widgets/Media.ts153
-rw-r--r--linux/home/.config/ags/widget/quicksettings/widgets/MicMute.ts18
-rw-r--r--linux/home/.config/ags/widget/quicksettings/widgets/Network.ts61
-rw-r--r--linux/home/.config/ags/widget/quicksettings/widgets/PowerProfile.ts99
-rw-r--r--linux/home/.config/ags/widget/quicksettings/widgets/Volume.ts161
-rw-r--r--linux/home/.config/ags/widget/settings/Group.ts34
-rw-r--r--linux/home/.config/ags/widget/settings/Page.ts19
-rw-r--r--linux/home/.config/ags/widget/settings/Row.ts55
-rw-r--r--linux/home/.config/ags/widget/settings/Setter.ts93
-rw-r--r--linux/home/.config/ags/widget/settings/SettingsDialog.ts63
-rw-r--r--linux/home/.config/ags/widget/settings/Wallpaper.ts31
-rw-r--r--linux/home/.config/ags/widget/settings/layout.ts147
-rw-r--r--linux/home/.config/ags/widget/settings/settingsdialog.scss144
-rw-r--r--linux/home/.config/betterlockscreen/betterlockscreenrc37
-rwxr-xr-xlinux/home/.config/bspwm/bspwmrc275
-rwxr-xr-xlinux/home/.config/bspwm/scripts/bspdragtofloat46
-rwxr-xr-xlinux/home/.config/bspwm/scripts/bspswallow12
-rwxr-xr-xlinux/home/.config/bspwm/scripts/bspwindows14
-rwxr-xr-xlinux/home/.config/bspwm/scripts/bspwm-monitor-setup59
-rwxr-xr-xlinux/home/.config/bspwm/scripts/bspwm-toggle-visibility.sh23
-rwxr-xr-xlinux/home/.config/bspwm/scripts/close-window11
-rwxr-xr-xlinux/home/.config/bspwm/scripts/drag-float42
-rwxr-xr-xlinux/home/.config/bspwm/scripts/external_rules.sh71
-rwxr-xr-xlinux/home/.config/bspwm/scripts/hide-window38
-rw-r--r--linux/home/.config/dunst/assets/notification/fallback.pngbin0 -> 54750 bytes
-rw-r--r--linux/home/.config/dunst/assets/notification/music.pngbin0 -> 814 bytes
-rw-r--r--linux/home/.config/dunst/assets/notification/scrot.pngbin0 -> 35737 bytes
-rw-r--r--linux/home/.config/dunst/assets/ui/volume-high.svg1
-rw-r--r--linux/home/.config/dunst/assets/ui/volume-low.svg1
-rw-r--r--linux/home/.config/dunst/assets/ui/volume-medium.svg1
-rw-r--r--linux/home/.config/dunst/assets/ui/volume-muted.svg1
-rw-r--r--linux/home/.config/dunst/dunstrc456
-rwxr-xr-xlinux/home/.config/dunst/scripts/openEwwPopup.sh36
-rwxr-xr-xlinux/home/.config/dunst/scripts/songArtLogger.sh17
-rw-r--r--linux/home/.config/eww/eww.scss11
-rw-r--r--linux/home/.config/eww/eww.yuck23
-rwxr-xr-xlinux/home/.config/hypr/autostart34
-rw-r--r--linux/home/.config/hypr/hyprland.conf175
-rwxr-xr-xlinux/home/.config/hypr/scripts/move-scratchpad.sh9
-rw-r--r--linux/home/.config/hypr/user/binds.conf167
-rw-r--r--linux/home/.config/hypr/user/env.conf23
-rw-r--r--linux/home/.config/hypr/user/exec.conf4
-rw-r--r--linux/home/.config/hypr/user/monitors.conf4
-rw-r--r--linux/home/.config/hypr/user/window_rules.conf70
-rw-r--r--linux/home/.config/inputrc86
-rw-r--r--linux/home/.config/jgmenu/jgmenurc32
-rw-r--r--linux/home/.config/jgmenu/menu.csv32
-rw-r--r--linux/home/.config/kitty/kitty.conf827
-rw-r--r--linux/home/.config/mimeapps.list14
-rw-r--r--linux/home/.config/picom/picom.conf494
-rw-r--r--linux/home/.config/plank/dock/launchers/Alacritty.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/com.obsproject.Studio.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/discord.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/firefox.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/gimp.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/google-chrome.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/obsidian.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/org.gnome.Nautilus.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/spotify.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/steam.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/trash.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/virtualbox.dockitem2
-rw-r--r--linux/home/.config/plank/dock/launchers/vlc.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/code-oss.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/com.obsproject.Studio.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/discord.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/firefox.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/obsidian.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/org.qbittorrent.qBittorrent.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/pcmanfm.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/phototonic.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/steam.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/trash.dockitem2
-rw-r--r--linux/home/.config/plank/dock1/launchers/vlc.dockitem2
-rw-r--r--linux/home/.config/polybar/config.ini671
-rwxr-xr-xlinux/home/.config/polybar/launch.sh31
-rwxr-xr-xlinux/home/.config/polybar/scripts/bluetooth.sh12
-rwxr-xr-xlinux/home/.config/polybar/scripts/check-network.sh21
-rwxr-xr-xlinux/home/.config/polybar/scripts/check_updates.sh118
-rwxr-xr-xlinux/home/.config/polybar/scripts/cmus.sh39
-rwxr-xr-xlinux/home/.config/polybar/scripts/get_spotify_status.sh52
-rwxr-xr-xlinux/home/.config/polybar/scripts/menu.sh6
-rwxr-xr-xlinux/home/.config/polybar/scripts/menu.shsave63
-rwxr-xr-xlinux/home/.config/polybar/scripts/menu_full.sh65
-rwxr-xr-xlinux/home/.config/polybar/scripts/now-playing.sh217
-rwxr-xr-xlinux/home/.config/polybar/scripts/polybar_wrapper92
-rwxr-xr-xlinux/home/.config/polybar/scripts/popup-calendar.sh41
-rwxr-xr-xlinux/home/.config/polybar/scripts/rofi-power.sh45
-rwxr-xr-xlinux/home/.config/polybar/scripts/scroll_spotify_status.sh12
-rwxr-xr-xlinux/home/.config/polybar/scripts/sysmenu.sh42
-rwxr-xr-xlinux/home/.config/polybar/scripts/sysmenu.shsave40
-rwxr-xr-xlinux/home/.config/polybar/scripts/system-usb-mount.sh53
-rwxr-xr-xlinux/home/.config/polybar/scripts/temperature.sh18
-rwxr-xr-xlinux/home/.config/polybar/scripts/toggle_bluetooth.sh8
-rwxr-xr-xlinux/home/.config/polybar/scripts/vpn.sh28
-rw-r--r--linux/home/.config/pypoetry/config.toml3
-rw-r--r--linux/home/.config/rofi/Notif.rasi153
-rw-r--r--linux/home/.config/rofi/colors/gruvbox.rasi10
-rw-r--r--linux/home/.config/rofi/colors/nord.rasi10
-rw-r--r--linux/home/.config/rofi/colors/simple.rasi10
-rw-r--r--linux/home/.config/rofi/config.rasi178
-rw-r--r--linux/home/.config/rofi/options_menu.rasi71
-rw-r--r--linux/home/.config/rofi/rofi-network-manager.conf41
-rw-r--r--linux/home/.config/rofi/rofi-network-manager.rasi127
-rw-r--r--linux/home/.config/rofi/styles/appmenu.rasi186
-rw-r--r--linux/home/.config/rofi/styles/powermenu.rasi187
-rw-r--r--linux/home/.config/rofi/themes/colors.rasi18
-rw-r--r--linux/home/.config/rofi/themes/dmenu.rasi38
-rw-r--r--linux/home/.config/rofi/themes/power.rasi34
-rwxr-xr-xlinux/home/.config/sxhkd/show_help.sh3
-rwxr-xr-xlinux/home/.config/sxhkd/sxhkdrc490
-rwxr-xr-xlinux/home/.config/tmux/file_manager.sh108
-rwxr-xr-xlinux/home/.config/tmux/fzf-menu.sh60
-rwxr-xr-xlinux/home/.config/tmux/left-status.sh25
-rwxr-xr-xlinux/home/.config/tmux/notes.sh113
-rwxr-xr-xlinux/home/.config/tmux/right-status.sh27
-rwxr-xr-xlinux/home/.config/tmux/tmux-popup-pane-manager.sh152
-rwxr-xr-xlinux/home/.config/tmux/tmux-toggle-option.sh27
-rw-r--r--linux/home/.config/tmux/tmux.conf835
-rwxr-xr-xlinux/home/.config/tmux/tmux_number.sh11
-rw-r--r--linux/home/.config/tridactyl/tridactylrc170
-rw-r--r--linux/home/.config/user-dirs.dirs15
-rw-r--r--linux/home/.config/user-dirs.locale1
-rw-r--r--linux/home/.config/waybar/config.jsonc292
-rw-r--r--linux/home/.config/waybar/style.css434
-rw-r--r--linux/home/.config/wofi/config17
-rw-r--r--linux/home/.config/wofi/style.css99
-rw-r--r--linux/home/.config/xkb/symbols/custom-us27
-rwxr-xr-xlinux/home/.config/xob/launch.sh10
-rwxr-xr-xlinux/home/.config/xob/manage-brightness16
-rwxr-xr-xlinux/home/.config/xob/manage-microphone56
-rwxr-xr-xlinux/home/.config/xob/manage-volume46
-rw-r--r--linux/home/.config/xob/styles.cfg100
-rw-r--r--linux/home/.config/zathura/zathurarc27
-rwxr-xr-xlinux/home/.local/bin/control-center31
-rwxr-xr-xlinux/home/.local/bin/ewwbin0 -> 22225024 bytes
-rwxr-xr-xlinux/home/.local/bin/ffmpeg24
-rwxr-xr-xlinux/home/.local/bin/nitrogen63
-rwxr-xr-xlinux/home/.local/bin/notification-center26
-rwxr-xr-xlinux/home/.local/bin/xcolor-pick31
-rw-r--r--linux/home/.local/share/applications/com.obsproject.Studio.desktop99
-rw-r--r--linux/home/.local/share/applications/phototonic.desktop17
-rwxr-xr-xlinux/home/.scripts/Heads-Up-Display28
-rw-r--r--linux/home/.scripts/README.md1
-rwxr-xr-xlinux/home/.scripts/bspwm-toggle-visibility.sh23
-rwxr-xr-xlinux/home/.scripts/bspwm_resize.sh67
-rw-r--r--linux/home/.scripts/check-updates.sh0
-rwxr-xr-xlinux/home/.scripts/colors.sh78
-rwxr-xr-xlinux/home/.scripts/cryptocheck31
-rwxr-xr-xlinux/home/.scripts/cryptonotify19
-rwxr-xr-xlinux/home/.scripts/dotfiles.sh37
-rwxr-xr-xlinux/home/.scripts/ffmpeg24
-rwxr-xr-xlinux/home/.scripts/get_zle_keymap_select.sh13
-rwxr-xr-xlinux/home/.scripts/gsettings.sh27
-rwxr-xr-xlinux/home/.scripts/killandnotify4
-rwxr-xr-xlinux/home/.scripts/layer.sh24
-rwxr-xr-xlinux/home/.scripts/neovim.sh421
-rwxr-xr-xlinux/home/.scripts/opacity-change.sh34
-rwxr-xr-xlinux/home/.scripts/powermenu38
-rwxr-xr-xlinux/home/.scripts/qemu-helper.sh172
-rwxr-xr-xlinux/home/.scripts/random_data.py153
-rwxr-xr-xlinux/home/.scripts/scratchpad64
-rwxr-xr-xlinux/home/.scripts/spec73
-rwxr-xr-xlinux/home/.scripts/track-books.sh19
-rw-r--r--linux/home/.scripts/win-nvim.bat37
-rw-r--r--linux/home/.scripts/win-nvim.ps139
-rw-r--r--linux/home/.vim/autoload/autoload/statusline.vim267
-rw-r--r--linux/home/.vim/autoload/statusline.vim267
-rw-r--r--linux/home/.vim/colors/colors/colorscheme.vim247
-rw-r--r--linux/home/.vim/colors/colors/default.vim175
-rw-r--r--linux/home/.vim/colors/colorscheme.vim247
-rw-r--r--linux/home/.vim/colors/default.vim175
-rw-r--r--linux/home/.vim/ftplugin/after/vim.vim12
-rw-r--r--linux/home/.vim/ftplugin/ftplugin/after/vim.vim12
m---------linux/home/.vim/pack/plugins/start/vim-tmux-navigator0
-rw-r--r--linux/home/.vim/vimrc700
-rw-r--r--linux/home/.vnc/config4
-rwxr-xr-xlinux/home/.vnc/xstartup16
323 files changed, 24758 insertions, 0 deletions
diff --git a/linux/home/.config/Code/User/keybindings.json b/linux/home/.config/Code/User/keybindings.json
new file mode 100644
index 0000000..9d04af3
--- /dev/null
+++ b/linux/home/.config/Code/User/keybindings.json
@@ -0,0 +1,274 @@
+[
+ {
+ "key": "ctrl+shift+r",
+ "command": "workbench.action.reloadWindow",
+ "when": "editorTextFocus"
+ },
+ {
+ "key": "ctrl+i",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .py",
+ "args": {
+ "text": "python \"${file}\" < ./i\n"
+ }
+ },
+ {
+ "key": "f5",
+ "command": "python.execInTerminal",
+ "when": "resourceExtname == .py",
+ },
+ {
+ "key": "f5",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .dart",
+ "args": {
+ "text": "dart \"${file}\"\n"
+ }
+ },
+ {
+ "key": "f5",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .js",
+ "args": {
+ "text": "node \"${file}\"\n"
+ }
+ },
+ {
+ "key": "f5",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .java",
+ "args": {
+ "text": "java \"${file}\"\n"
+ }
+ },
+ {
+ "key": "f5",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .sh",
+ "args": {
+ "text": "\"${file}\"\n"
+ }
+ },
+ {
+ "key": "f5",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .cpp",
+ "args": {
+ "text": "g++ -g \"${file}\" -o \"${fileDirname}/${fileBasenameNoExtension}\" && \"${fileDirname}/${fileBasenameNoExtension}\"\n"
+ }
+ },
+ {
+ "key": "f5",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .c",
+ "args": {
+ "text": "gcc -g \"${file}\" -o \"${fileDirname}/${fileBasenameNoExtension}\" && \"${fileDirname}/${fileBasenameNoExtension}\"\n"
+ }
+ },
+ {
+ "key": "f5",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .asm",
+ "args": {
+ "text": "nasm -f elf64 \"${file}\" && ld -o \"${fileDirname}/${fileBasenameNoExtension}\" \"${fileDirname}/${fileBasenameNoExtension}.o\" && \"${fileDirname}/${fileBasenameNoExtension}\"\n"
+ }
+ },
+ {
+ "key": "ctrl+i",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .cpp",
+ "args": {
+ "text": "g++ -g \"${file}\" -o \"${fileDirname}/${fileBasenameNoExtension}\" && \"${fileDirname}/${fileBasenameNoExtension}\" < ./i\n"
+ }
+ },
+ {
+ "key": "ctrl+g",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .cpp",
+ "args": {
+ "text": "g++ -g \"${file}\" -o \"${fileDirname}/${fileBasenameNoExtension}\" -lgraph -lGL -lGLU -lglut && \"${fileDirname}/${fileBasenameNoExtension}\" 2>/dev/null\n"
+ }
+ },
+ // {
+ // "key": "f5",
+ // "command": "workbench.action.tasks.runTask",
+ // "when": "resourceExtname == .cpp",
+ // "args": "Build and Run C++"
+ // },
+ {
+ "key": "shift+f5",
+ "command": "workbench.action.terminal.sendSequence",
+ "when": "resourceExtname == .cpp",
+ "args": {
+ "text": "g++ -g \"${fileDirname}/*.cpp\" -o \"${fileDirname}/${fileBasenameNoExtension}\" && \"${fileDirname}/${fileBasenameNoExtension}\"\n"
+ }
+ },
+ // {
+ // "key": "shift+f5",
+ // "command": "workbench.action.tasks.runTask",
+ // "when": "resourceExtname == .cpp",
+ // "args": "Build and Run C++ (Multiple cpp files)"
+ // },
+ {
+ "key": "f4",
+ "command": "workbench.action.debug.continue",
+ "when": "inDebugMode"
+ },
+ {
+ "key": "f4",
+ "command": "workbench.action.debug.start",
+ "when": "!inDebugMode"
+ },
+ {
+ "key": "f4",
+ "command": "workbench.action.debug.start",
+ "when": "debuggersAvailable && !inDebugMode"
+ },
+ {
+ "key": "f5",
+ "command": "-workbench.action.debug.continue",
+ "when": "inDebugMode"
+ },
+ {
+ "key": "f5",
+ "command": "-workbench.action.debug.start",
+ "when": "!inDebugMode"
+ },
+ {
+ "key": "f5",
+ "command": "-workbench.action.debug.start",
+ "when": "debuggersAvailable && !inDebugMode"
+ },
+ {
+ "key": "shift+f4",
+ "command": "workbench.action.debug.stop",
+ "when": "inDebugMode"
+ },
+ {
+ "key": "shift+f5",
+ "command": "-workbench.action.debug.stop",
+ "when": "inDebugMode"
+ },
+ {
+ "key": "ctrl+v",
+ "command": "workbench.action.terminal.paste",
+ "when": "terminalFocus && terminalProcessSupported"
+ },
+ {
+ "key": "ctrl+shift+v",
+ "command": "-workbench.action.terminal.paste",
+ "when": "terminalFocus && terminalProcessSupported"
+ },
+ {
+ "key": "ctrl+alt+up",
+ "command": "editor.action.copyLinesUpAction",
+ "when": "editorTextFocus && !editorReadonly"
+ },
+ {
+ "key": "ctrl+alt+down",
+ "command": "editor.action.copyLinesDownAction",
+ "when": "editorTextFocus && !editorReadonly"
+ },
+ {
+ "key": "ctrl+shift+alt+up",
+ "command": "-editor.action.copyLinesUpAction",
+ "when": "editorTextFocus && !editorReadonly"
+ },
+ {
+ "key": "ctrl+shift+alt+down",
+ "command": "-editor.action.copyLinesDownAction",
+ "when": "editorTextFocus && !editorReadonly"
+ },
+ {
+ "key": "ctrl+,",
+ "command": "-workbench.action.openSettings"
+ },
+ {
+ "key": "ctrl+,",
+ "command": "workbench.action.openSettingsJson"
+ },
+ {
+ "key": "ctrl+i",
+ "command": "-editor.action.triggerSuggest",
+ "when": "editorHasCompletionItemProvider && textInputFocus && !editorReadonly"
+ },
+ {
+ "key": "ctrl+c",
+ "command": "workbench.action.terminal.copySelection",
+ "when": "terminalFocus && terminalProcessSupported && terminalTextSelected && terminalTextSelected"
+ },
+ {
+ "key": "ctrl+shift+c",
+ "command": "-workbench.action.terminal.copySelection",
+ "when": "terminalFocus && terminalProcessSupported && terminalTextSelected && terminalTextSelected"
+ },
+ {
+ "key": "ctrl+k",
+ "command": "workbench.action.terminal.kill",
+ "when": "terminalIsOpen && terminalFocus"
+ },
+ {
+ "key": "delete",
+ "command": "-workbench.action.terminal.killInstance",
+ "when": "terminalIsOpen && terminalTabsFocus || terminalProcessSupported && terminalTabsFocus"
+ },
+ {
+ "key": "ctrl+shift+i",
+ "command": "-editor.action.formatDocument.none",
+ "when": "editorTextFocus && !editorHasDocumentFormattingProvider && !editorReadonly"
+ },
+ {
+ "key": "ctrl+shift+i",
+ "command": "-notebook.formatCell",
+ "when": "editorHasDocumentFormattingProvider && editorTextFocus && inCompositeEditor && notebookEditable && !editorReadonly && activeEditor == 'workbench.editor.notebook'"
+ },
+ {
+ "key": "ctrl+shift+i",
+ "command": "editor.action.formatDocument",
+ "when": "editorTextFocus"
+ },
+ {
+ "key": "ctrl+shift+i",
+ "command": "-editor.action.formatDocument",
+ "when": "editorHasDocumentFormattingProvider && editorTextFocus && !editorReadonly && !inCompositeEditor"
+ },
+ {
+ "key": "ctrl+shift+i",
+ "command": "workbench.action.toggleDevTools",
+ "when": "!editorTextFocus"
+ },
+ {
+ "key": "ctrl+shift+i",
+ "command": "-workbench.action.toggleDevTools",
+ "when": "isDevelopment"
+ },
+ {
+ "key": "ctrl+t",
+ "command": "-workbench.action.showAllSymbols"
+ },
+ {
+ "key": "ctrl+t",
+ "command": "workbench.action.files.newUntitledFile"
+ },
+ {
+ "key": "ctrl+n",
+ "command": "-workbench.action.files.newUntitledFile"
+ },
+ {
+ "key": "ctrl+shift+m",
+ "command": "github.cweijan.mysql.focus"
+ },
+ {
+ "key": "ctrl+shift+m",
+ "command": "-workbench.actions.view.problems",
+ "when": "workbench.panel.markers.view.active"
+ },
+ {
+ "key": "ctrl+shift+0",
+ "command": "workbench.action.zoomReset"
+ },
+ {
+ "key": "ctrl+numpad0",
+ "command": "-workbench.action.zoomReset"
+ }
+]
diff --git a/linux/home/.config/Code/User/settings.json b/linux/home/.config/Code/User/settings.json
new file mode 100644
index 0000000..8686b1b
--- /dev/null
+++ b/linux/home/.config/Code/User/settings.json
@@ -0,0 +1,734 @@
+{
+ "workbench.colorCustomizations": {
+ "[Chromodynamics]": {
+ "terminal.background": "#060606",
+ "terminal.foreground": "#e0e0e0",
+ "terminal.selectionBackground": "#555555",
+ "titleBar.activeBackground": "#020202",
+ "titleBar.activeForeground": "#e0e0e0",
+ "titleBar.inactiveBackground": "#020202",
+ "titleBar.inactiveForeground": "#e0e0e0",
+ "tab.inactiveBackground": "#0b0b0b",
+ "editor.background": "#060606",
+ "editor.findMatchBorder": "#0c0c0d",
+ "editor.findMatchHighlightBorder": "#0c0c0d",
+ "editor.lineHighlightBackground": "#060606",
+ "editor.selectionBackground": "#222222",
+ "editor.selectionHighlightBackground": "#e4dddd",
+ "editor.selectionHighlightBorder": "#0c0c0d",
+ "editorGroupHeader.tabsBackground": "#0b0b0b",
+ "editorGutter.background": "#000000",
+ "sideBar.background": "#060606",
+ "sideBarSectionHeader.background": "#0b0b0b",
+ "statusBar.background": "#060606"
+ },
+ "[Sweet Dracula]": {
+ "editorGroupHeader.tabsBackground": "#161925",
+ }
+ },
+ "editor.tokenColorCustomizations": {
+ "[Chromodynamics]": {
+ "textMateRules": [
+ {
+ "scope": [
+ "constant.language.python",
+ "variable.parameter.function.language.special.self.python"
+ ],
+ "settings": {
+ "foreground": "#66D9EF"
+ }
+ },
+ {
+ "scope": [
+ "entity.name.function.operator",
+ "keyword.operator"
+ ],
+ "settings": {
+ "foreground": "#E8364F",
+ "fontStyle": ""
+ }
+ },
+ {
+ "scope": [
+ "punctuation.definition.template-expression.begin.js",
+ "punctuation.definition.template-expression.end.js"
+ ],
+ "settings": {
+ "foreground": "#da70d6",
+ "fontStyle": "italic"
+ }
+ },
+ {
+ "scope": [
+ "variable.other.readwrite.js"
+ ],
+ "settings": {
+ "foreground": "#c6c6c6",
+ "fontStyle": ""
+ }
+ }
+ ]
+ },
+ "textMateRules": [
+ {
+ "scope": [
+ "entity.name.type.class",
+ "storage.type.class",
+ "storage.modifier",
+ "storage.type",
+ "constant",
+ "comment",
+ "keyword",
+ "invalid"
+ ],
+ "settings": {
+ "fontStyle": "italic"
+ }
+ }
+ ]
+ },
+ // Window Settings
+ "window.commandCenter": false,
+ "window.menuBarVisibility": "toggle",
+ "window.newWindowDimensions": "maximized",
+ "window.restoreWindows": "all",
+ "window.title": "${activeEditorShort}${separator}${rootName}",
+ "window.titleBarStyle": "native",
+ "window.titleSeparator": " ・ ・",
+ "workbench.editor.enablePreviewFromQuickOpen": false,
+ "workbench.editor.highlightModifiedTabs": true,
+ "workbench.editor.restoreViewState": false,
+ "workbench.editor.sharedViewState": false,
+ "workbench.editor.showTabs": "multiple",
+ "workbench.editor.tabCloseButton": "right",
+ "workbench.editor.tabSizing": "shrink",
+ "workbench.iconTheme": "sweet-vscode-icons",
+ "workbench.layoutControl.type": "menu",
+ "workbench.list.smoothScrolling": true,
+ "workbench.panel.defaultLocation": "right",
+ "workbench.startupEditor": "none",
+ "workbench.statusBar.visible": true,
+ "workbench.editorAssociations": {
+ "*.ipynb": "jupyter-notebook"
+ },
+ "workbench.reduceMotion": "off",
+ // Editor Settings
+ "editor.acceptSuggestionOnEnter": "smart",
+ "editor.autoClosingBrackets": "always",
+ "editor.bracketPairColorization.enabled": true,
+ "editor.codeActionsOnSave": {
+ "source.organizeImports": "explicit"
+ },
+ "editor.colorDecorators": false,
+ "editor.cursorBlinking": "phase",
+ "editor.cursorSmoothCaretAnimation": "on",
+ "editor.cursorStyle": "line-thin",
+ "editor.detectIndentation": false,
+ "editor.emptySelectionClipboard": false,
+ "editor.fontFamily": "'Operator Mono SSm Lig Book', 'FiraCode Nerd Font', 'Hack Nerd Font Mono', 'codicon'",
+ "editor.fontVariations": true,
+ "editor.fontLigatures": "'ss01', 'ss02', 'ss03', 'ss04', 'ss05', 'ss06', 'zero', 'onum'",
+ "editor.fontSize": 12,
+ "editor.fontWeight": "normal",
+ "editor.formatOnSave": true,
+ "editor.formatOnType": true,
+ "editor.glyphMargin": true,
+ "editor.guides.bracketPairs": "active",
+ "editor.guides.bracketPairsHorizontal": "active",
+ "editor.guides.highlightActiveBracketPair": true,
+ "editor.hover.above": false,
+ "editor.inlayHints.enabled": "on",
+ "editor.inlineSuggest.enabled": true,
+ "editor.insertSpaces": true,
+ "editor.linkedEditing": true,
+ "editor.matchBrackets": "never",
+ "editor.maxTokenizationLineLength": 99999999,
+ "editor.minimap.enabled": false,
+ "editor.mouseWheelScrollSensitivity": 1.5,
+ "editor.occurrencesHighlight": "off",
+ "editor.renderControlCharacters": true,
+ "editor.renderFinalNewline": "on",
+ "editor.renderLineHighlight": "gutter",
+ "editor.renderWhitespace": "none",
+ "editor.scrollbar.horizontal": "auto",
+ "editor.scrollbar.vertical": "auto",
+ "editor.selectionHighlight": true,
+ "editor.smoothScrolling": true,
+ "editor.stickyScroll.enabled": true,
+ "editor.stickyScroll.maxLineCount": 5,
+ "editor.stickyTabStops": false,
+ "editor.suggestSelection": "first",
+ "editor.suggest.insertMode": "replace",
+ "editor.suggest.preview": true,
+ "editor.suggest.snippetsPreventQuickSuggestions": false,
+ "editor.tabCompletion": "on",
+ "editor.tabSize": 4,
+ "editor.wordBasedSuggestions": "matchingDocuments",
+ "editor.wordBasedSuggestionsMode": "matchingDocuments",
+ "editor.wordWrap": "on",
+ "editor.quickSuggestions": {
+ "other": "on",
+ "comments": "on",
+ "strings": "on",
+ },
+ // Emmet Settings
+ "emmet.excludeLanguages": [],
+ "emmet.includeLanguages": {
+ "javascript": "javascriptreact",
+ "markdown": "html",
+ "vue-html": "html"
+ },
+ // Explorer Settings
+ "explorer.compactFolders": false,
+ "explorer.confirmDelete": false,
+ "explorer.confirmDragAndDrop": false,
+ // Customize UI
+ // "customizeUI.activityBar": "bottom",
+ // "customizeUI.activityBarHideSettings": true,
+ // "customizeUI.listRowHeight": 20,
+ // "customizeUI.font.monospace": "CaskaydiaCove Nerd Font",
+ // "customizeUI.font.regular": "CaskaydiaCove Nerd Font",
+ // "customizeUI.fontSizeMap": {
+ // "13px": "12px",
+ // "monospace": "12px",
+ // "tab-title": "12px",
+ // "window-title": "12.5px"
+ // },
+ // "customizeUI.titleBar": "inline",
+ // "customizeUI.stylesheet": {
+ // ":root,::after,::before": "--tab-height: 25px;--tab-radius: 8px;--ui-radius: 6px;--base-color: #bd93f9;--gradient-one: #c50ed2;--gradient-two: #8500f7;--side-pane-color: var(--vscode-editor-background);--focus-border: #3f4f81;--sash-size: 2px !important;--sash-hover-size: 2px !important;",
+ // ".editor-container": "-webkit-font-smoothing: antialiased;",
+ // ".monaco-workbench .part.editor > .content > .watermark": "display: none !important;",
+ // ".monaco-workbench .part.titlebar .window-controls-container .layout-dropdown-container": "margin-left: auto !important;",
+ // ".monaco-workbench .part.titlebar > .window-controls-container": "width: unset !important;",
+ // ".monaco-workbench .part.titlebar > .window-controls-container > .window-icon": "width: 30px !important;",
+ // ".monaco-workbench .part.titlebar > .titlebar-container .window-appicon > .home-bar-icon-badge, .monaco-workbench .part.titlebar > .titlebar-container > .window-appicon:not(.codicon)": "background-image: url(./vsc/vsc.svg) !important;",
+ // ".monaco-workbench .part.editor > .content .editor-group-container.empty .editor-group-letterpress": "background-image: url(./vsc/vsc-back.svg) !important;",
+ // ".monaco-workbench .part.editor.has-watermark > .content.empty .editor-group-container > .editor-group-letterpress": "background-position-y: 50% !important;",
+ // ".monaco-sash.hover:before, .monaco-sash.active:before": "background: linear-gradient(to bottom, var(--gradient-one), var(--gradient-two)) !important;",
+ // ".monaco-editor .squiggly-error": "background: none !important;border-bottom: 0.5px solid var(--vscode-editorError-foreground) !important;",
+ // ".monaco-editor .squiggly-warning": "background: none !important;border-bottom: 0.5px solid var(--vscode-editorWarning-foreground) !important;",
+ // ".monaco-editor .squiggly-info": "background: none !important;border-bottom: 0.5px solid var(--vscode-editorInfo-foreground) !important;",
+ // ".monaco-editor .squiggly-hint": "background: none !important;border-bottom: 0.5px solid var(--vscode-editorHint-foreground) !important;",
+ // "body.activity-bar-at-bottom .monaco-workbench .activitybar .active-item-indicator": "display: block !important;",
+ // ".monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before": "border: 0 !important;width: 100% !important;height: 3px !important;position: absolute !important;bottom: 0 !important;left: 0 !important;top: unset !important;background-image: linear-gradient(to top, var(--gradient-one), var(--gradient-two)) !important;border-radius: 100vmax !important;",
+ // ".monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-bottom-container, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-top-container": "background-image: linear-gradient(to left, var(--gradient-one), var(--gradient-two)) !important;height: 3px !important;",
+ // ".monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .badge .badge-content": "background-image: linear-gradient(90deg, var(--gradient-one), var(--gradient-two)) !important;",
+ // ".monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover):before, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover):before": "content: '' !important;",
+ // ".codicon-git-branch:before": "background: linear-gradient(to bottom, var(--gradient-one), var(--gradient-two)) !important;-webkit-background-clip: text !important;background-clip: text !important;color: rgba(255, 255, 255, 0.4) !important;",
+ // ".monaco-editor, .split-view-view .monaco-editor-background": "background: url(./vsc/pek.png) bottom right / 10% auto no-repeat scroll;",
+ // ".monaco-scrollable-element > .scrollbar > .slider": "border-radius: 7px !important;",
+ // ".monaco-scrollable-element > .scrollbar > .slider.active": "background: linear-gradient(to top, var(--gradient-one), var(--gradient-two)) !important;",
+ // "canvas.decorationsOverviewRuler, .monaco-scrollable-element > .scrollbar.vertical, .monaco-scrollable-element > .scrollbar.vertical > .slider": "width: 7px !important;",
+ // ".monaco-scrollable-element > .scrollbar.horizontal, .monaco-scrollable-element > .scrollbar.horizontal > .slider": "height: 7px !important;",
+ // ".monaco-scrollable-element > .scrollbar.horizontal > .slider": "width: 135px !important;",
+ // ".monaco-editor .monaco-hover, .monaco-editor .suggest-widget, .monaco-editor .suggest-details": "border-radius: var(--ui-radius) !important;overflow: hidden !important;box-shadow: rgb(0 0 0 / 36%) 0px 2px calc(var(--ui-radius) + 2px) !important;",
+ // ".pane .pane-body, .pane .pane-body .monaco-list, .pane .pane-body .monaco-list .monaco-scrollable-element, .pane .pane-body .monaco-list .monaco-scrollable-element .monaco-list-rows, .pane .pane-body .monaco-list .monaco-scrollable-element .monaco-list-rows .monaco-list-row": "overflow: visible !important;",
+ // ".open-editors .monaco-list .monaco-list-row": "padding-left: 0px !important;",
+ // ".monaco-list-row:hover, .monaco-list-row.selected, .monaco-list-row.focused": "border-radius: 0 var(--ui-radius) var(--ui-radius) 0 !important;",
+ // ".monaco-workbench .monaco-list:not(.element-focused):focus:before, .monaco-select-box, .monaco-select-box-dropdown-container, .monaco-select-box-dropdown-container .monaco-list-row:hover, .monaco-select-box-dropdown-container .monaco-list-row.selected, .monaco-select-box-dropdown-container .monaco-list-row.focused": "border-radius: var(--ui-radius) !important;",
+ // ".monaco-list-row.selected::before, .monaco-list-row.selected::after": "--ui-radius: 6px;content: '' !important;display: block !important;position: absolute !important;width: var(--ui-radius) !important;height: var(--ui-radius) !important;left: 0 !important;pointer-events: none !important;",
+ // ".monaco-list-row.selected::before": "top: calc(0px - var(--ui-radius)) !important;background: radial-gradient(circle at 100% 0%, transparent 70.71%, var(--side-pane-color) 29.289%) no-repeat;background-position: 100% 0%;",
+ // ".monaco-list-row.selected::after": "bottom: calc(0px - var(--ui-radius)) !important;background: radial-gradient(circle at 100% 100%, transparent 70.71%, var(--side-pane-color) 29.289%) no-repeat;background-position: 100% 100%;",
+ // ".monaco-button.monaco-text-button": "border-radius: var(--ui-radius) !important;",
+ // ".monaco-editor .cursors-layer.cursor-smooth-caret-animation > .cursor": "transition: all 100ms ease-out;",
+ // ".tab:first-child": "margin-left: var(--tab-radius) !important;",
+ // ".tab:last-child": "margin-right: var(--tab-radius) !important;",
+ // ".tab.active": "border-radius: var(--tab-radius) var(--tab-radius) 0 0 !important;",
+ // ".tab.dirty-border-top > .tab-border-top-container": "border-radius: var(--tab-radius) var(--tab-radius) 0 0 !important;",
+ // ".tab.active::before, .tab.active::after": "content: '' !important;display: block !important;box-sizing: border-box !important;position: absolute !important;z-index: 1;width: var(--tab-radius) !important;height: var(--tab-radius) !important;bottom: 0px !important;pointer-events: none !important;",
+ // ".tab.active::before": "left: calc(0px - var(--tab-radius)) !important;border-bottom-right-radius: var(--tab-radius) !important;box-shadow: 3px 3px 0 3px var(--vscode-editor-background) !important;",
+ // ".tab.active::after": "right: 0 !important;transform: translateX(var(--tab-radius)) !important;border-bottom-left-radius: var(--tab-radius) !important;box-shadow: -3px 3px 0 3px var(--vscode-editor-background) !important;",
+ // ".monaco-workbench .part.sidebar>.title>.title-label h2": "font-weight: bold !important;",
+ // "body.activity-bar-at-bottom div.monaco-grid-view > div > div > div.monaco-scrollable-element > div.split-view-container > div.split-view-view.visible > div > div > div.monaco-scrollable-element > div.split-view-container > div:nth-child(1) > div > div > div.monaco-scrollable-element > div.split-view-container > div:nth-child(2)": "height: auto !important;",
+ // "body.activity-bar-at-bottom .monaco-workbench .part.activitybar": "border: none !important; padding: 0px 6px 6px 6px !important;margin-top: -8px !important; background-color: var(--vscode-editor-background) !important; position: relative !important; z-index: 10 !important;",
+ // "body.activity-bar-at-bottom .monaco-workbench .activitybar.bordered:before": "display: none !important;",
+ // "body.activity-bar-at-bottom .monaco-workbench .activitybar > .content": "justify-content: center !important; border-radius: 6px; width: unset !important; border: 1px solid #3f4f818c !important;",
+ // "body.activity-bar-at-bottom .monaco-workbench .activitybar>.content>.composite-bar": "margin-bottom: unset !important;",
+ // "body.activity-bar-at-bottom .monaco-action-bar .action-item.icon > .action-label": "color: var(--vscode-editor-foreground) !important;opacity: 0.4;",
+ // "body.activity-bar-at-bottom .monaco-action-bar .action-item.icon.checked > .action-label": "opacity: 1 !important;",
+ // ".monaco-workbench .part.editor .tabs-and-actions-container .window-controls-container": "display: flex;flex-grow: 0;flex-shrink: 0;text-align: center; -webkit-app-region: no-drag; height: 100%;",
+ // ".monaco-workbench .part.editor .tabs-and-actions-container .window-controls-container .window-icon": "width: 30px !important;height: 30px !important; display: flex; align-items: center; justify-content: center;",
+ // ".monaco-workbench .part.editor .tabs-and-actions-container .window-controls-container .window-icon.window-close:hover": "background-color: #FF5555",
+ // },
+ // Terminal Settings
+ "terminal.integrated.altClickMovesCursor": true,
+ "terminal.integrated.cursorBlinking": true,
+ "terminal.integrated.customGlyphs": true,
+ "terminal.integrated.cursorStyle": "line",
+ "terminal.integrated.enableMultiLinePasteWarning": false,
+ "terminal.integrated.fontFamily": "'CaskaydiaCove Nerd Font', 'FiraCode Nerd Font', 'Hack Nerd Font Mono'",
+ "terminal.integrated.fontSize": 13,
+ "terminal.integrated.gpuAcceleration": "auto",
+ "terminal.integrated.scrollback": 200000,
+ "terminal.integrated.smoothScrolling": true,
+ "terminal.integrated.tabs.enabled": false,
+ "terminal.integrated.tabs.enableAnimation": true,
+ "terminal.integrated.profiles.linux": {
+ "zsh": {
+ "path": "/usr/bin/zsh"
+ }
+ },
+ "terminal.integrated.shellIntegration.enabled": false,
+ "terminal.integrated.shellIntegration.decorationsEnabled": "never",
+ // Language Settings
+ "[python]": {
+ "editor.semanticHighlighting.enabled": false
+ },
+ "python.analysis.completeFunctionParens": true,
+ "python.formatting.provider": "black",
+ "python.formatting.blackArgs": [
+ "--line-length",
+ "120"
+ ],
+ "python.languageServer": "Pylance",
+ "python.linting.ignorePatterns": [
+ ".vscode/*.py",
+ "**/site-packages/**/*.py",
+ ".git"
+ ],
+ "python.linting.enabled": true,
+ "python.linting.pylintEnabled": true,
+ "python.linting.pylintArgs": [
+ "--rcfile",
+ "${env:HOME}/.pylintrc"
+ // "--load-plugins=pylint_django"
+ ],
+ "[cpp]": {
+ "editor.semanticHighlighting.enabled": false,
+ },
+ "C_Cpp.default.includePath": [
+ "${workspaceFolder}/**",
+ "/usr/include",
+ "/usr/local/include",
+ "/usr/include/c++/11.1.0"
+ ],
+ "C_Cpp.default.defines": [
+ "${default}"
+ ],
+ "C_Cpp.default.compilerPath": "/usr/bin/g++",
+ "C_Cpp.default.cppStandard": "c++20",
+ "C_Cpp.default.cStandard": "c17",
+ "C_Cpp.default.intelliSenseMode": "gcc-x64",
+ "C_Cpp.default.compilerArgs": [
+ "-g"
+ ],
+ "launch": {
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(gdb) Launch",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "${fileDirname}/${fileBasenameNoExtension}",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "${workspaceFolder}",
+ "environment": [],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "miDebuggerPath": "/usr/bin/gdb",
+ "preLaunchTask": "Build",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ }
+ ]
+ }
+ ]
+ },
+ "tasks": {
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build",
+ "type": "shell",
+ "command": "g++ -g \"${file}\" -o \"${fileDirname}/${fileBasenameNoExtension}\"",
+ "problemMatcher": [
+ "$gcc"
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ },
+ {
+ "label": "Build (Multiple cpp files)",
+ "type": "shell",
+ "command": "g++ -g \"${fileDirname}/*.cpp\" -o \"${fileDirname}/${fileBasenameNoExtension}\"",
+ "problemMatcher": [
+ "$gcc"
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ }
+ ]
+ },
+ "C_Cpp.autocompleteAddParentheses": true,
+ "C_Cpp.codeFolding": "enabled",
+ "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 2, ColumnLimit: 0}",
+ "C_Cpp.inlayHints.autoDeclarationTypes.enabled": true,
+ "C_Cpp.inlayHints.autoDeclarationTypes.showOnLeft": true,
+ "C_Cpp.inlayHints.parameterNames.enabled": true,
+ "C_Cpp.inlayHints.parameterNames.hideLeadingUnderscores": true,
+ "C_Cpp.inlayHints.parameterNames.suppressWhenArgumentContainsName": true,
+ "C_Cpp.inlayHints.referenceOperator.enabled": true,
+ "C_Cpp.inlayHints.referenceOperator.showSpace": false,
+ "css.format.enable": true,
+ "css.format.newlineBetweenRules": true,
+ "css.format.newlineBetweenSelectors": true,
+ "css.format.spaceAroundSelectorSeparator": true,
+ "[css]": {
+ "editor.tabSize": 2
+ },
+ "java.jdt.ls.java.home": "/usr/lib/jvm/java-11-openjdk-amd64",
+ "java.configuration.runtimes": [
+ {
+ "name": "JavaSE-11",
+ "path": "/usr/lib/jvm/java-11-openjdk-amd64"
+ }
+ ],
+ "java.server.launchMode": "LightWeight",
+ "[html]": {
+ "editor.tabSize": 2
+ },
+ "html.autoClosingTags": true,
+ "html.completion.attributeDefaultValue": "singlequotes",
+ "[dart]": {
+ "editor.suggestSelection": "first",
+ "editor.tabCompletion": "onlySnippets",
+ "editor.wordBasedSuggestions": "off"
+ },
+ "[javascript]": {
+ "editor.tabSize": 2
+ },
+ "javascript.autoClosingTags": true,
+ "javascript.suggest.autoImports": true,
+ "javascript.suggest.completeFunctionCalls": true,
+ "javascript.suggest.enabled": true,
+ "javascript.updateImportsOnFileMove.enabled": "always",
+ "javascript.validate.enable": false,
+ "[json]": {
+ "editor.tabSize": 2
+ },
+ "json.maxItemsComputed": 100000,
+ "[markdown]": {
+ "editor.quickSuggestions": {
+ "other": "on",
+ "comments": "on",
+ "strings": "on",
+ }
+ },
+ "[scss]": {
+ "editor.tabSize": 2
+ },
+ "[typescript]": {
+ "editor.tabSize": 2
+ },
+ "typescript.autoClosingTags": true,
+ "typescript.suggest.autoImports": true,
+ "typescript.updateImportsOnFileMove.enabled": "always",
+ "typescript.validate.enable": false,
+ "[typescriptreact]": {
+ "editor.tabSize": 2,
+ "editor.formatOnSave": false,
+ },
+ "js/ts.implicitProjectConfig.checkJs": true,
+ //Latex
+ "[latex]": {
+ "editor.formatOnSave": false
+ },
+ // Empty-Indent Extension
+ "emptyIndent.highlightColor": "rgba(246,36,89,0.6)",
+ "emptyIndent.highlightIndent": false,
+ "emptyIndent.removeIndent": true,
+ // Github Co-pilot
+ "github.copilot.inlineSuggest.enable": true,
+ "github.copilot.enable": {
+ "*": true,
+ "yaml": false,
+ "plaintext": true,
+ "markdown": false
+ },
+ // Gitlens
+ "gitlens.currentLine.enabled": true,
+ "gitlens.hovers.currentLine.over": "line",
+ "gitlens.codeLens.enabled": false,
+ "gitlens.statusBar.enabled": true,
+ // General Setting\
+ "color-highlight.markRuler": false,
+ "color-highlight.markerType": "background",
+ "debug.onTaskErrors": "showErrors",
+ "debug.openDebug": "openOnDebugBreak",
+ "eslint.enable": true,
+ "extensions.ignoreRecommendations": true,
+ "files.autoSave": "onWindowChange",
+ "files.insertFinalNewline": true,
+ "files.restoreUndoStack": true,
+ "files.trimFinalNewlines": true,
+ "files.trimTrailingWhitespace": true,
+ "git.autofetch": true,
+ "grammarly.files.include": [
+ "**/*.md",
+ "**/*.txt",
+ "**/*.tex",
+ ],
+ "latex-workshop.latex.autoBuild.cleanAndRetry.enabled": true,
+ "latex-workshop.latex.autoBuild.run": "never",
+ "latex-workshop.latex.clean.fileTypes": [
+ "*/*.aux",
+ "*/*.synctex.gz"
+ ],
+ "latex-workshop.latex.outDir": "/tmp/Review-Paper/",
+ "latex-workshop.latex.recipe.default": "latexmk (lualatex)",
+ "latex-workshop.latex.recipes": [
+ {
+ "name": "latexmk 🔃",
+ "tools": [
+ "latexmk"
+ ]
+ },
+ {
+ "name": "latexmk (latexmkrc)",
+ "tools": [
+ "latexmk_rconly"
+ ]
+ },
+ {
+ "name": "latexmk (lualatex)",
+ "tools": [
+ "lualatexmk"
+ ]
+ },
+ {
+ "name": "latexmk (xelatex)",
+ "tools": [
+ "xelatexmk"
+ ]
+ },
+ {
+ "name": "pdflatex ➞ bibtex ➞ pdflatex × 2",
+ "tools": [
+ "pdflatex",
+ "bibtex",
+ "pdflatex",
+ "pdflatex"
+ ]
+ },
+ {
+ "name": "Compile Rnw files",
+ "tools": [
+ "rnw2tex",
+ "latexmk"
+ ]
+ },
+ {
+ "name": "Compile Jnw files",
+ "tools": [
+ "jnw2tex",
+ "latexmk"
+ ]
+ },
+ {
+ "name": "tectonic",
+ "tools": [
+ "tectonic"
+ ]
+ }
+ ],
+ "latex-workshop.latex.tools": [
+ {
+ "name": "latexmk",
+ "command": "latexmk",
+ "args": [
+ "-synctex=1",
+ "-interaction=nonstopmode",
+ "-file-line-error",
+ "-pdf",
+ "-outdir=%OUTDIR%",
+ "%DOC%"
+ ],
+ "env": {}
+ },
+ {
+ "name": "lualatexmk",
+ "command": "latexmk",
+ "args": [
+ "-synctex=1",
+ "-interaction=nonstopmode",
+ "-file-line-error",
+ "-lualatex",
+ "-outdir=%OUTDIR%",
+ "%DOC%"
+ ],
+ "env": {}
+ },
+ {
+ "name": "xelatexmk",
+ "command": "latexmk",
+ "args": [
+ "-synctex=1",
+ "-interaction=nonstopmode",
+ "-file-line-error",
+ "-xelatex",
+ "-outdir=%OUTDIR%",
+ "%DOC%"
+ ],
+ "env": {}
+ },
+ {
+ "name": "latexmk_rconly",
+ "command": "latexmk",
+ "args": [
+ "%DOC%"
+ ],
+ "env": {}
+ },
+ {
+ "name": "pdflatex",
+ "command": "pdflatex",
+ "args": [
+ "-synctex=1",
+ "-interaction=nonstopmode",
+ "-file-line-error",
+ "-output-directory=%OUTDIR%",
+ "%DOC%"
+ ],
+ "env": {}
+ },
+ {
+ "name": "bibtex",
+ "command": "bibtex",
+ "args": [
+ "%DOCFILE%"
+ ],
+ "env": {}
+ },
+ {
+ "name": "rnw2tex",
+ "command": "Rscript",
+ "args": [
+ "-e",
+ "knitr::opts_knit$set(concordance = TRUE); knitr::knit('%DOCFILE_EXT%')"
+ ],
+ "env": {}
+ },
+ {
+ "name": "jnw2tex",
+ "command": "julia",
+ "args": [
+ "-e",
+ "using Weave; weave(\"%DOC_EXT%\", doctype=\"tex\")"
+ ],
+ "env": {}
+ },
+ {
+ "name": "jnw2texmintex",
+ "command": "julia",
+ "args": [
+ "-e",
+ "using Weave; weave(\"%DOC_EXT%\", doctype=\"texminted\")"
+ ],
+ "env": {}
+ },
+ {
+ "name": "tectonic",
+ "command": "tectonic",
+ "args": [
+ "--synctex",
+ "--keep-logs",
+ "%DOC%.tex"
+ ],
+ "env": {}
+ }
+ ],
+ "liveServer.settings.donotShowInfoMsg": true,
+ "liveServer.settings.donotVerifyTags": true,
+ "liveServer.settings.file": "404.html",
+ // "liveServer.settings.https": {
+ // "enable": true,
+ // "cert": "/home/proxzima/.ssh/cert/cert.pem",
+ // "key": "/home/proxzima/.ssh/cert/key.pem",
+ // "passphrase": "1234"
+ // },
+ "liveServer.settings.useLocalIp": true,
+ "markdown.preview.markEditorSelection": true,
+ "markdown.preview.scrollEditorWithPreview": false,
+ "markdown.preview.scrollPreviewWithEditor": false,
+ "svgPreview.autoOpen": true,
+ "svgPreview.scaleToFit": true,
+ "svgPreview.style": {
+ "html": {
+ "background-position": "0 0, 13px 13px",
+ "background-size": "26px 26px",
+ "background": "rgba(255, 255, 255, 1)",
+ // "background-image": "linear-gradient(45deg, #141414 25%, transparent 25%, transparent 75%, #141414 75%, #141414), linear-gradient(45deg, #141414 25%, transparent 25%, transparent 75%, #141414 75%, #141414)"
+ }
+ },
+ "print.folder.exclude": [
+ "{bin,obj,out}",
+ "node_module",
+ "**/*.{bin,exe,dll,hex,pdb,pdf,pfx,png,jpg,gif,bmp,suo,pptx,ppt,jar,woff2,woff,ttf,eot,odt,otf,class}"
+ ],
+ "print.lineNumbers": "on",
+ "print.printAndClose": false,
+ "print.colourScheme": "XCode",
+ "print.alternateBrowser": true,
+ "print.browserPath": "/usr/bin/google-chrome",
+ "spellright.documentTypes": [
+ "markdown",
+ "latex",
+ "plaintext"
+ ],
+ "spellright.language": [
+ "en_GB"
+ ],
+ "spellright.notificationClass": "information",
+ "spellright.suggestionsInHints": false,
+ "spellright.useDocumentSymbolsInCode": true,
+ "telemetry.telemetryLevel": "off",
+ "search.exclude": {
+ "**/bower_components": true,
+ "**/*.code-search": true,
+ "**/node_modules": true,
+ "**/env": true,
+ "**/venv": true
+ },
+ "files.associations": {
+ "*.xml": "html",
+ "*.svg": "html",
+ "*.json": "jsonc"
+ },
+ "files.exclude": {
+ "**/.DS_Store": true,
+ "**/.git": true,
+ "**/.svn": true,
+ "**/.hg": true,
+ "**/CVS": true,
+ "**/.classpath": true,
+ "**/.project": true,
+ "**/.settings": true,
+ "**/.factorypath": true
+ },
+ "files.watcherExclude": {
+ "**/.DS_Store/**": true,
+ "**/.git/objects/**": true,
+ "**/.git/subtree-cache/**": true,
+ "**/.svn/**": true,
+ "**/.hg/**": true,
+ "**/CVS/**": true,
+ "**/node_modules/**": true,
+ "**/env/**": true,
+ "**/venv/**": true,
+ "env-*": true
+ },
+ "workbench.colorTheme": "Sweet Dracula",
+ "security.workspace.trust.untrustedFiles": "open",
+ "workbench.activityBar.location": "hidden",
+ "workbench.editor.tabActionLocation": "right",
+ "workbench.editor.empty.hint": "hidden"
+}
diff --git a/linux/home/.config/Code/User/snippets/snippet.code-snippets b/linux/home/.config/Code/User/snippets/snippet.code-snippets
new file mode 100644
index 0000000..16bdfed
--- /dev/null
+++ b/linux/home/.config/Code/User/snippets/snippet.code-snippets
@@ -0,0 +1,126 @@
+{
+ // Place your global snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
+ // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
+ // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
+ // used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
+ // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
+ // Placeholders with the same ids are connected.
+ // Example:
+ "if name is main": {
+ "scope": "python",
+ "prefix": "ifn",
+ "body": [
+ "if __name__ == '__main__':",
+ "\t$0"
+ ],
+ "description": ""
+ },
+ "for in": {
+ "scope": "python",
+ "prefix": "fori",
+ "body": [
+ "for ${1:item} in ${2:items}:",
+ "\t$0"
+ ],
+ "description": ""
+ },
+ "for in range": {
+ "scope": "python",
+ "prefix": "forr",
+ "body": [
+ "for ${1:i} in range($2):",
+ "\t$0"
+ ],
+ "description": ""
+ },
+ "lambda": {
+ "scope": "python",
+ "prefix": "lamb",
+ "body": [
+ "lambda ${1:arg} : $0",
+ ],
+ "description": ""
+ },
+ "map input": {
+ "scope": "python",
+ "prefix": "mapi",
+ "body": [
+ "map(${1:func}, input($2).split())"
+ ],
+ "description": ""
+ },
+ "html boiler plate": {
+ "scope": "html",
+ "prefix": "html",
+ "body": [
+ "<!DOCTYPE html>",
+ "<html lang=\"en\">",
+ "",
+ "<head>",
+ " <meta charset=\"utf-8\">",
+ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
+ "",
+ " <title>${1:Basic HTML5}</title>",
+ " <meta name=\"author\" content=\"${2:Trevor Gray}\">",
+ " <meta name=\"description\" content=\"${3:HTML5 Template}\">",
+ "",
+ " <meta property=\"og:title\" content=\"${4:HTML5 Template}\">",
+ " <meta property=\"og:type\" content=\"website\">",
+ " <meta property=\"og:url\" content=\"https://$5\">",
+ " <meta property=\"og:description\" content=\"${6:HTML5 Template}\">",
+ " <meta property=\"og:image\" content=\"${7:./assets/preview.png}\">",
+ "",
+ " <link rel=\"icon\" href=\"${8:./assets/favicon.ico}\">",
+ " <link rel=\"icon\" href=\"${9:./assets/favicon.svg}\" type=\"image/svg+xml\">",
+ " <link rel=\"apple-touch-icon\" href=\"${10:./assets/apple-touch-icon.png}\">",
+ "",
+ " <link rel=\"stylesheet\" href=\"${11:./css/style.css}\">",
+ "",
+ "</head>",
+ "",
+ "<body>",
+ " ${0:<!-- your content here... -->}",
+ " <script src=\"${12:./js/main.js}\" defer></script>",
+ "</body>",
+ "",
+ "</html>",
+ ],
+ "description": ""
+ },
+ "css boiler plate": {
+ "scope": "css",
+ "prefix": "css",
+ "body": [
+ "html {",
+ " font-family: sans-serif;",
+ " font-size: 100%;",
+ " box-sizing: border-box;",
+ "}",
+ "",
+ "*, ::before, ::after {",
+ " box-sizing: inherit;",
+ "}",
+ "",
+ "html, body {",
+ " margin: 0;",
+ " padding: 0;",
+ " width: 100vw;",
+ " min-height: 100%;",
+ " text-rendering: optimizeLegibility;",
+ "}",
+ ""
+ ]
+ },
+ "consoleLog": {
+ "scope": "javascript",
+ "prefix": "clg",
+ "body": "console.log(${1:object});",
+ "description": "Displays a message in the console"
+ },
+ "consoleLogObject": {
+ "scope": "javascript",
+ "prefix": "clo",
+ "body": "console.log('${1:object} :>> ', ${1:object});",
+ "description": "Displays an object in the console with its name"
+ }
+}
diff --git a/linux/home/.config/Code/User/spellright.dict b/linux/home/.config/Code/User/spellright.dict
new file mode 100644
index 0000000..94f1e64
--- /dev/null
+++ b/linux/home/.config/Code/User/spellright.dict
@@ -0,0 +1,14 @@
+srdusr
+gui
+url
+yara
+backend
+txt
+Regex
+github
+args
+grep
+res
+yar
+shipit
+json
diff --git a/linux/home/.config/Code/User/vsc.css b/linux/home/.config/Code/User/vsc.css
new file mode 100644
index 0000000..8316df0
--- /dev/null
+++ b/linux/home/.config/Code/User/vsc.css
@@ -0,0 +1,408 @@
+/* *:not(.monaco-editor .margin):not(.monaco-editor .lines-content.monaco-editor-background):not(.monaco-editor .cursors-layer.cursor-smooth-caret-animation > .cursor):not(.monaco-scrollable-element > .scrollbar.vertical > .slider):not(.monaco-scrollable-element > .scrollbar.horizontal > .slider):not(.pane .pane-body .monaco-list .monaco-scrollable-element .monaco-list-rows) {
+ transition-duration: 100ms !important;
+} */
+
+:root,
+::after,
+::before {
+ --tab-height: 25px;
+ --tab-radius: 8px;
+ --ui-radius: 6px;
+ --base-color: #bd93f9;
+ --gradient-one: #cba6f7;
+ --gradient-two: #89b4fa;
+ --side-pane-color: var(--vscode-editor-background);
+ --focus-border: #3f4f81;
+ --sash-size: 2px !important;
+ --sash-hover-size: 2px !important;
+}
+
+body > .monaco-workbench > .monaco-grid-view > .monaco-grid-branch-node > .monaco-split-view2 > .monaco-scrollable-element > .split-view-container {
+ background: var(--vscode-editor-background) !important;
+}
+
+.editor-container {
+ -webkit-font-smoothing: antialiased;
+}
+
+.monaco-workbench .part.titlebar .window-controls-container .window-icon,
+.monaco-workbench .part.editor > .content > .watermark {
+ display: none !important;
+}
+
+.monaco-workbench .part.editor .tabs-and-actions-container .window-controls-container {
+ display: flex;
+ flex-grow: 0;
+ flex-shrink: 0;
+ text-align: center;
+ -webkit-app-region: no-drag;
+ height: 100%;
+}
+
+.monaco-workbench .part.editor .tabs-and-actions-container .window-controls-container .window-icon {
+ width: 30px !important;
+ height: 30px !important;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.monaco-workbench .part.editor .tabs-and-actions-container .window-controls-container .window-icon.window-close:hover {
+ background: #FF5555 !important;
+}
+
+.monaco-workbench .part.titlebar .window-controls-container .layout-dropdown-container {
+ margin-left: auto !important;
+}
+
+.monaco-workbench .part.titlebar > .window-controls-container {
+ width: unset !important;
+}
+
+.monaco-workbench .part.titlebar > .window-controls-container > .window-icon {
+ width: 30px !important;
+}
+
+.monaco-workbench.linux .part.titlebar > .window-title {
+ font-size: 12.5px !important;
+}
+
+.monaco-workbench .part.titlebar > .titlebar-container .window-appicon > .home-bar-icon-badge,
+.monaco-workbench .part.titlebar > .titlebar-container > .window-appicon:not(.codicon) {
+ background-image: url(./vsc/vsc.svg) !important;
+}
+
+.monaco-workbench .part.editor > .content .editor-group-container.empty .editor-group-letterpress {
+ background-image: url(./vsc/vsc-back.svg) !important;
+}
+
+.monaco-workbench .part.editor.has-watermark > .content.empty .editor-group-container > .editor-group-letterpress {
+ background-position-y: 50% !important;
+}
+
+.mac,
+.windows,
+.linux {
+ /* font-family: 'CaskaydiaCove Nerd Font' !important;
+ --monaco-monospace-font: 'CaskaydiaCove Nerd Font' !important; */
+ /* font-family: 'Delugia' !important;
+ --monaco-monospace-font: 'Delugia Mono' !important; */
+}
+
+
+.monaco-workbench .part.sidebar > .title > .title-label h2 {
+ font-weight: bold !important;
+}
+
+.monaco-sash.hover:before,
+.monaco-sash.active:before {
+ background: linear-gradient(to bottom, var(--gradient-one), var(--gradient-two)) !important;
+}
+
+/* Squiggly lines to straight lines */
+
+.monaco-editor .squiggly-error {
+ background: none !important;
+ border-bottom: 0.5px solid var(--vscode-editorError-foreground) !important;
+}
+
+.monaco-editor .squiggly-warning {
+ background: none !important;
+ border-bottom: 0.5px solid var(--vscode-editorWarning-foreground) !important;
+}
+
+.monaco-editor .squiggly-info {
+ background: none !important;
+ border-bottom: 0.5px solid var(--vscode-editorInfo-foreground) !important;
+}
+
+.monaco-editor .squiggly-hint {
+ background: none !important;
+ border-bottom: 0.5px solid var(--vscode-editorHint-foreground) !important;
+}
+
+/* */
+
+/* Gradient Tab border */
+body.activity-bar-at-bottom .monaco-workbench .activitybar .active-item-indicator {
+ display: block !important;
+}
+
+.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before {
+ border: 0 !important;
+ width: 3px !important;
+ height: 100% !important;
+ position: absolute !important;
+ top: 0 !important;
+ left: 0 !important;
+ background-image: linear-gradient(to top, var(--gradient-one), var(--gradient-two)) !important;
+ border-radius: 100vmax !important;
+}
+
+.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-bottom-container,
+.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-top-container {
+ background-image: linear-gradient(to left, var(--gradient-one), var(--gradient-two)) !important;
+ height: 3px !important;
+ display: none !important;
+}
+
+.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .badge .badge-content {
+ background-image: linear-gradient(90deg, var(--gradient-one), var(--gradient-two)) !important;
+}
+
+/* */
+
+/* Hides dirty tab white dot indicator */
+
+.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover):before,
+.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover):before {
+ content: '' !important;
+}
+
+/* */
+
+/* Git icon background */
+
+.codicon-git-branch:before {
+ background: linear-gradient(to bottom, var(--gradient-one), var(--gradient-two)) !important;
+ -webkit-background-clip: text !important;
+ background-clip: text !important;
+ color: rgba(255, 255, 255, 0.4) !important;
+}
+
+/* */
+
+/* Editor Background */
+
+.monaco-editor,
+.split-view-view .lines-content.monaco-editor-background {
+ background: url(./vsc/pek.png) bottom right / 10% auto no-repeat scroll;
+ /*background: linear-gradient(rgba(0,0,0,.65), rgba(0,0,0,.65)), url(./vsc/bg.jpg) center center / auto 100vh no-repeat scroll;*/
+}
+
+/* */
+
+/* Scrollbar */
+.monaco-scrollable-element > .scrollbar > .slider {
+ border-radius: 7px !important;
+}
+
+.monaco-scrollable-element > .scrollbar > .slider.active {
+ background: linear-gradient(to top, var(--gradient-one), var(--gradient-two)) !important;
+}
+
+canvas.decorationsOverviewRuler,
+.monaco-scrollable-element > .scrollbar.vertical,
+.monaco-scrollable-element > .scrollbar.vertical > .slider {
+ width: 7px !important;
+}
+
+.monaco-scrollable-element > .scrollbar.vertical > .slider {
+ transition: all 100ms ease-out;
+}
+
+.monaco-scrollable-element > .scrollbar.horizontal,
+.monaco-scrollable-element > .scrollbar.horizontal > .slider {
+ height: 7px !important;
+}
+
+.monaco-scrollable-element > .scrollbar.horizontal > .slider {
+ transition: all 100ms ease-out;
+}
+
+.monaco-scrollable-element > .scrollbar.horizontal > .slider {
+ width: 135px !important;
+}
+
+/* */
+
+/* Rounded UI */
+
+/* Suggestions */
+.monaco-editor .monaco-hover,
+.monaco-editor .suggest-widget,
+.monaco-editor .suggest-details {
+ border-radius: var(--ui-radius) !important;
+ overflow: hidden !important;
+ box-shadow: rgb(0 0 0 / 36%) 0px 2px calc(var(--ui-radius) + 2px) !important;
+}
+
+/* Left pane selected file */
+.pane .pane-body,
+.pane .pane-body .monaco-list,
+.pane .pane-body .monaco-list .monaco-scrollable-element,
+.pane .pane-body .monaco-list .monaco-scrollable-element .monaco-list-rows,
+.pane .pane-body .monaco-list .monaco-scrollable-element .monaco-list-rows .monaco-list-row {
+ overflow: visible !important;
+}
+
+.open-editors .monaco-list .monaco-list-row {
+ padding-left: 0px !important;
+}
+
+.monaco-list-row:hover,
+.monaco-list-row.selected,
+.monaco-list-row.focused {
+ border-radius: 0 var(--ui-radius) var(--ui-radius) 0 !important;
+}
+
+.monaco-workbench .monaco-list:not(.element-focused):focus:before,
+.monaco-select-box,
+.monaco-select-box-dropdown-container,
+.monaco-select-box-dropdown-container .monaco-list-row:hover,
+.monaco-select-box-dropdown-container .monaco-list-row.selected,
+.monaco-select-box-dropdown-container .monaco-list-row.focused {
+ border-radius: var(--ui-radius) !important;
+}
+
+.monaco-list-row.selected::before,
+.monaco-list-row.selected::after {
+ content: '' !important;
+ display: block !important;
+ position: absolute !important;
+ width: var(--ui-radius) !important;
+ height: var(--ui-radius) !important;
+ left: 0 !important;
+ pointer-events: none !important;
+}
+
+.monaco-list-row.selected::before {
+ top: calc(0px - var(--ui-radius)) !important;
+ background: radial-gradient(circle at 100% 0%, transparent 70.71%, var(--side-pane-color) 29.289%) no-repeat;
+ background-position: 100% 0%;
+}
+
+.monaco-list-row.selected::after {
+ bottom: calc(0px - var(--ui-radius)) !important;
+ background: radial-gradient(circle at 100% 100%, transparent 70.71%, var(--side-pane-color) 29.289%) no-repeat;
+ background-position: 100% 100%;
+}
+
+.monaco-button.monaco-text-button {
+ border-radius: var(--ui-radius) !important;
+}
+
+/* */
+
+/* Smooth cursor */
+
+.monaco-editor .cursors-layer.cursor-smooth-caret-animation > .cursor {
+ transition: all 100ms ease-out;
+}
+
+/* */
+
+/* Active tab (Without border) */
+
+..tab:first-child {
+ margin-left: var(--tab-radius) !important;
+ /* margin-left: calc(var(--tab-radius) - 2px) !important; */
+}
+
+..tab:last-child {
+ margin-right: var(--tab-radius) !important;
+ /* margin-right: calc(var(--tab-radius) - 2px) !important; */
+}
+
+..tab.active {
+ /* border: 2px solid var(--base-color) !important; */
+ /* background-color: transparent !important; */
+ /* rgb(20, 24, 34) */
+ border-radius: var(--tab-radius) var(--tab-radius) 0 0 !important;
+ /* border-bottom: 2px solid #0c0e14 !important; */
+ /* z-index: 1 !important; */
+}
+
+.tab.dirty-border-top > .tab-border-top-container {
+ border-radius: var(--tab-radius) var(--tab-radius) 0 0 !important;
+}
+
+/* Border bottom radius*/
+..tab.active::before,
+..tab.active::after {
+ content: '' !important;
+ display: block !important;
+ box-sizing: border-box !important;
+ position: absolute !important;
+ z-index: 1;
+ width: var(--tab-radius) !important;
+ height: var(--tab-radius) !important;
+ bottom: 0px !important;
+ /* bottom: -2px !important; */
+ pointer-events: none !important;
+ /* border-bottom: 2px solid var(--base-color) !important; */
+}
+
+...tab.active::before {
+ left: calc(0px - var(--tab-radius)) !important;
+ /* transform: translateX(calc(0px - var(--tab-radius))) !important; */
+ /* border-right: 2px solid var(--base-color) !important; */
+ border-bottom-right-radius: var(--tab-radius) !important;
+ /* box-shadow: 3px 3px 0 3px var(--tab-color) !important; */
+ box-shadow: 3px 3px 0 3px var(--vscode-editor-background) !important;
+ /* background: radial-gradient(circle at 0% 0%, transparent 70.71%, var(--tab-color) 29.289%) no-repeat;
+ background-position: 0% 0%; */
+}
+
+..tab.active::after {
+ right: 0 !important;
+ transform: translateX(var(--tab-radius)) !important;
+ /* border-left: 2px solid var(--base-color) !important; */
+ border-bottom-left-radius: var(--tab-radius) !important;
+ /* box-shadow: -3px 3px 0 3px var(--tab-color) !important; */
+ box-shadow: -3px 3px 0 3px var(--vscode-editor-background) !important;
+ /* background: radial-gradient(circle at 100% 0%, transparent 70.71%, var(--tab-color) 29.289%) no-repeat;
+ background-position: 100% 0%; */
+}
+
+/* .tabs-container:before {
+ content: '' !important;
+ top: 0 !important;
+ left: 0 !important;
+ right: 0 !important;
+ bottom: 0 !important;
+ background: linear-gradient(90deg, rgb(12, 14, 20), rgb(189, 172, 255), rgb(189, 147, 249), rgb(189, 147, 249), rgb(189, 147, 249), rgb(189, 172, 249), rgb(12, 14, 20)) !important;
+ clip: rect(33px, 2600px, 33px, 0px) !important;
+ position: absolute !important;
+}
+
+.tabs-container:after {
+ content: '' !important;
+ top: 0 !important;
+ left: 0 !important;
+ right: 0 !important;
+ bottom: 0 !important;
+ background: linear-gradient(90deg, rgb(12, 14, 20), rgb(189, 172, 255), rgb(189, 147, 249), rgb(189, 147, 249), rgb(189, 147, 249), rgb(189, 172, 249), rgb(12, 14, 20)) !important;
+ clip: rect(33px, 2600px, 35px, 0px) !important;
+ position: absolute !important;
+}
+
+.tab-label > .monaco-icon-label-container::after {
+ background: transparent !important;
+} */
+
+/* */
+
+.tab{
+ border-radius: 12px !important;
+ margin: 5px !important;
+ height: 40px !important;
+ border:2px solid #313244 !important;
+ background-color: #161925 !important;
+}
+.tab.active {
+ border: 2px solid var(--gradient-one) !important;
+ color: var(--gradient-one) !important;
+ font-weight: bold !important;
+}
+.tab.dirty {
+ border: 2px solid var(--gradient-two) !important;
+
+}
+.tabs-container{
+ height: auto !important;
+ padding: 5px !important;
+}
+.editor-actions {
+ height: inherit !important;
+}
diff --git a/linux/home/.config/Code/User/vsc.js b/linux/home/.config/Code/User/vsc.js
new file mode 100644
index 0000000..a14bf5a
--- /dev/null
+++ b/linux/home/.config/Code/User/vsc.js
@@ -0,0 +1,100 @@
+function getOffset(el) {
+ const rect = el.getBoundingClientRect();
+ return {
+ left: rect.left + window.scrollX,
+ top: rect.top + window.scrollY,
+ };
+}
+
+const prevOffset = {};
+
+const displayEffect = (event) => {
+ if (prevOffset.top === undefined) {
+ let initialOffset = getOffset(document.getElementsByClassName("cursor")[0]);
+ prevOffset.top = initialOffset.top;
+ prevOffset.left = initialOffset.left;
+ }
+
+ const existingRect = document.getElementById("myRect");
+ if (existingRect !== null) {
+ existingRect.remove();
+ }
+
+ setTimeout(function () {
+ const currOffset = getOffset(document.getElementsByClassName("cursor")[0]);
+ const top = prevOffset.top;
+ const left = prevOffset.left;
+ const rect = document.createElement("div");
+
+ // console.log("prev: ", top, left);
+ // console.log("curr: ", currOffset.top, currOffset.left);
+
+ rect.id = "myRect";
+ rect.style.cssText = `
+ position:absolute;
+ top:${top}px;
+ left:${left}px;
+ width:9px;
+ height:20px;
+ z-index:10;
+ background-color:red;
+ opacity: 0.5;
+ `;
+ rect.animate(
+ [
+ // keyframes
+ { transform: "scale(1) skew(10deg)" },
+ { transform: "scale(0) skew(10deg)" },
+ ],
+ {
+ // timing options
+ duration: 500,
+ easing: "ease-in-out",
+ direction: "alternate",
+ iterations: Infinity,
+ }
+ );
+
+ document.body.appendChild(rect);
+ prevOffset.top = currOffset.top;
+ prevOffset.left = currOffset.left;
+ }, 24);
+};
+
+// NOTE: First enable editor.cursorSmoothCaretAnimation.
+// NOTE: Uncomment this to get a cursor trail effect.
+// document.addEventListener("keydown", displayEffect);
+
+const windowControls = document.createElement('div');
+windowControls.className = 'window-controls-container';
+const minimize = document.createElement('div');
+minimize.classList.add('window-icon', 'window-minimize', 'codicon', 'codicon-chrome-minimize');
+const restore = document.createElement('div');
+restore.classList.add('window-icon', 'window-max-restore', 'codicon', 'codicon-chrome-restore');
+const close = document.createElement('div');
+close.classList.add('window-icon', 'window-close', 'codicon', 'codicon-chrome-close');
+windowControls.appendChild(minimize);
+windowControls.appendChild(restore);
+windowControls.appendChild(close);
+// const html = '<div class="window-controls-container"><div class="window-icon window-minimize codicon codicon-chrome-minimize"></div><div class="window-icon window-max-restore codicon codicon-chrome-restore"></div><div class="window-icon window-close codicon codicon-chrome-close"></div></div>';
+// template.innerHTML = html;
+// const windowControls = template.content.firstChild;
+
+var observer = new MutationObserver(function (mutations, me) {
+ console.warn('Observing if window controls present');
+ // const windowControls = document.querySelector(".window-controls-container");
+ const tabContainer = document.querySelector(".tabs-and-actions-container");
+
+ if (windowControls && tabContainer) {
+ tabContainer.appendChild(windowControls);
+ console.warn('Observing completed');
+ me.disconnect();
+ return;
+ }
+});
+
+
+// observer.observe(document, {
+// childList: true,
+// subtree: true
+// });
diff --git a/linux/home/.config/X11/.Xresources b/linux/home/.config/X11/.Xresources
new file mode 100644
index 0000000..be509d1
--- /dev/null
+++ b/linux/home/.config/X11/.Xresources
@@ -0,0 +1,307 @@
+st.alpha: 255
+!! Transparency (0-1):
+*.alpha: 0.2
+
+urxvt*shading: 10
+urxvt*tintColor: #000000
+urxvt*blurRadius: 5
+urxvt*transparent: true
+urxvt*depth: 32
+urxvt*background: rgba:0000/0000/1111/dddd
+!urxvt*background: rgba:0000/0000/0200/c800
+!URxvt*scrollstyle: plain
+URxvt.scrollBar: False
+!URxvt.font: xft:DejaVu Sans Mono:pixelsize=11:antialias=true
+URxvt.font: xft:monospace:Bold:pixelsize=12:antialias=true
+!URxvt*transparent: true
+!URxvt*shading: 5
+! change to whateva background
+URxvt.keysym.C-7: command:\033]11;#ff0000\007
+!URxvt*reverseVideo: True
+
+URxvt*saveLines : 10000
+
+!URxvt.keysym.Control-Up: \033[1;5A
+!URxvt.keysym.Control-Down: \033[1;5B
+!URxvt.keysym.Control-Left: \033[1;5D
+!URxvt.keysym.Control-Right: \033[1;5C
+
+URxvt.keysym.Control-1: command:\007\033]711;xft:monospace:Bold:pixelsize=12:antialias=true\007
+URxvt.keysym.Control-2: command:\007\033]711;xft:monospace:Bold:pixelsize=18:antialias=true\007
+URxvt.keysym.Control-3: command:\007\033]711;xft:monospace:Bold:pixelsize=28:antialias=true\007
+!URxvt.keysym.Control-1: command:\033]710;xft:Terminus:pixelsize=10:antialias=false\007\033]711;xft:Terminus:Bold:pixelsize=10:antialias=false\007
+!URxvt.keysym.Control-2: command:\033]710;xft:Terminus:pixelsize=14:antialias=false\007\033]711;xft:Terminus:Bold:pixelsize=14:antialias=false\007
+!URxvt.keysym.Control-3: command:\033]710;xft:Terminus:pixelsize=18:antialias=false\007\033]711;xft:Terminus:Bold:pixelsize=18:antialias=false\007
+!URxvt.keysym.Control-4: command:\033]710;xft:Terminus:Bold:pixelsize=20:antialias=false\007\033]711;xft:Terminus:Bold:pixelsize=20:antialias=false\007
+!URxvt.keysym.Control-5: command:\033]710;xft:Terminus:Bold:pixelsize=28:antialias=false\007\033]711;xft:Terminus:Bold:pixelsize=28:antialias=false\007
+
+
+
+
+
+
+
+!URxvt.perl-ext-common: selection-to-clipboard
+!URxvt.keysym.Shift-Control-V: eval:paste_clipboard
+!URxvt.keysym.Shift-Control-C: eval:selection_to_clipboard
+
+
+URxvt.iso14755: false
+URxvt.iso14755_52: false
+
+
+!URxvt.scrollBar: true
+!URxvt.scrollBar_right: true
+!URxvt.scrollBar_floating: true
+!URxvt.searchable-scrollback: false
+URxvt.url-select.underline: true
+URxvt.url-select.autocopy: true
+URxvt.cutchars: `""()''*<>[]{|}
+URxvt.cursorBlink: True
+URxvt.cursorColor: green
+URxvt.colorBD: yellow
+URxvt.colorUL: green
+!URxvt.blurRadius: 5
+
+
+!! Copy Paste & Other Extensions
+!URxvt.perl-ext-common: default,clipboard,url-select,keyboard-select
+!URxvt.copyCommand: xclip -i -selection clipboard
+!URxvt.pasteCommand: xclip -o -selection clipboard
+!URxvt.keysym.M-c: perl:clipboard:copy
+!URxvt.keysym.M-v: perl:clipboard:paste
+!URxvt.keysym.M-C-v: perl:clipboard:paste_escaped
+!URxvt.keysym.M-Escape: perl:keyboard-select:activate
+!URxvt.keysym.M-s: perl:keyboard-select:search
+!URxvt.keysym.M-u: perl:url-select:select_next
+!URxvt.urlLauncher: firefox
+!URxvt.underlineURLs: true
+!URxvt.urlButton: 1
+
+
+
+
+URxvt.keysym.Control-v: eval:paste_clipboard
+URxvt.keysym.Control-c: eval:selection_to_clipboard
+URxvt.keysym.Control-Meta-c: builtin-string:
+URxvt.keysym.Control-Meta-v: builtin-string:
+
+
+URxvt.keysym.Control-k: command:\033]720;1\007
+URxvt.keysym.Control-j: command:\033]721;1\007
+
+
+URxvt.keysym.Meta-Page_Up: perl:pageup
+URxvt.keysym.Meta-Page_Down: perl:pagedown
+URxvt.perl-lib: /usr/local/lib/urxvt/perl
+URxvt.perl-ext: custom.pl
+
+
+!URxvt*loginShell: true
+!URxvt*termName: screen-256color
+
+!URxvt*perl-ext-common:
+!URxvt*perl-ext:
+
+
+!URxvt.keysym.Control-B: eval:scroll_up_pages 1
+!URxvt.keysym.Control-F: eval:scroll_down_pages 1
+
+
+URxvt.perl-ext-common: default,matcher,clipboard,tabbed
+URxvt.clipboard.autocopy: true
+URxvt.keysym.M-c: perl:clipboard:copy
+URxvt.keysym.M-v: perl:clipboard:paste
+
+!URxvt.url-launcher: /usr/bin/xdg-open
+!URxvt.matcher.button: 1
+!URxvt.keysym.C-Delete: perl:matcher:last
+!URxvt.keysym.M-Delete: perl:matcher:list
+!URxvt.matcher.rend.0: Uline Bold fg5
+
+!URxvt.perl-ext-common: ...,tabbed,...
+
+
+!Key Description
+!Shift+Down New tab
+!Shift+Left Go to left tab
+!Shift+Right Go to right tab
+!Ctrl+Left Move tab to the left
+!Ctrl+Right Move tab to the right
+!Ctrl+d Close tab
+
+
+!URxvt.keysym.Control-k: \033[1;5A
+!URxvt.keysym.Control-j: \033[1;5B
+!URxvt.keysym.Control-l: \033[1;5C
+!URxvt.keysym.Control-h: \033[1;5D
+
+!^[[5~
+
+
+rofi.kb-row-up: Up,Control+k,Shift+Tab,Shift+ISO_Left_Tab
+rofi.kb-row-down: Down,Control+j
+rofi.kb-accept-entry: Control+m,Return,KP_Enter
+rofi.terminal: st
+rofi.kb-remove-to-eol: Control+Shift+e
+rofi.kb-mode-next: Shift+Right,Control+Tab,Control+l
+rofi.kb-mode-previous: Shift+Left,Control+Shift+Tab,Control+h
+rofi.kb-remove-char-back: BackSpace
+
+
+
+
+!! Set a default font and font size as below:
+!*.font: monospace:size=10
+rofi.font: Noto Sans 11
+
+/* name dark light */
+/* black 0 8 */
+/* red 1 9 */
+/* green 2 10 */
+/* yellow 3 11 */
+/* blue 4 12 */
+/* purple 5 13 */
+/* cyan 6 14 */
+/* white 7 15 */
+
+/* !! gruvbox: */
+/* *.color0: #1d2021 */
+/* *.color1: #cc241d */
+/* *.color2: #98971a */
+/* *.color3: #d79921 */
+/* *.color4: #458588 */
+/* *.color5: #b16286 */
+/* *.color6: #689d6a */
+/* *.color7: #a89984 */
+/* *.color8: #928374 */
+/* *.color9: #fb4934 */
+/* *.color10: #b8bb26 */
+/* *.color11: #fabd2f */
+/* *.color12: #83a598 */
+/* *.color13: #d3869b */
+/* *.color14: #8ec07c */
+/* *.color15: #ebdbb2 */
+/* *.color256: #1d2021 */
+/* *.color257: #ebdbb2 */
+
+* !! gruvbox light: */
+* *.color0: #fbf1c7 */
+* *.color1: #cc241d */
+* *.color2: #98971a */
+* *.color3: #d79921 */
+* *.color4: #458588 */
+* *.color5: #b16286 */
+* *.color6: #689d6a */
+* *.color7: #7c6f64 */
+* *.color8: #928374 */
+* *.color9: #9d0006 */
+* *.color10: #79740e */
+* *.color11: #b57614 */
+* *.color12: #076678 */
+* *.color13: #8f3f71 */
+* *.color14: #427b58 */
+* *.color15: #3c3836 */
+* *.background: #fbf1c7 */
+* *.foreground: #282828 */
+* st.alpha: 0.2 */
+
+/* !! brogrammer: */
+/* *.foreground: #d6dbe5 */
+/* *.background: #131313 */
+/* *.color0: #1f1f1f */
+/* *.color8: #d6dbe5 */
+/* *.color1: #f81118 */
+/* *.color9: #de352e */
+/* *.color2: #2dc55e */
+/* *.color10: #1dd361 */
+/* *.color3: #ecba0f */
+/* *.color11: #f3bd09 */
+/* *.color4: #2a84d2 */
+/* *.color12: #1081d6 */
+/* *.color5: #4e5ab7 */
+/* *.color13: #5350b9 */
+/* *.color6: #1081d6 */
+/* *.color14: #0f7ddb */
+/* *.color7: #d6dbe5 */
+/* *.color15: #ffffff */
+/* *.colorBD: #d6dbe5 */
+
+/* ! base16 */
+/* *.color0: #181818 */
+/* *.color1: #ab4642 */
+/* *.color2: #a1b56c */
+/* *.color3: #f7ca88 */
+/* *.color4: #7cafc2 */
+/* *.color5: #ba8baf */
+/* *.color6: #86c1b9 */
+/* *.color7: #d8d8d8 */
+/* *.color8: #585858 */
+/* *.color9: #ab4642 */
+/* *.color10: #a1b56c */
+/* *.color11: #f7ca88 */
+/* *.color12: #7cafc2 */
+/* *.color13: #ba8baf */
+/* *.color14: #86c1b9 */
+/* *.color15: #f8f8f8 */
+
+/* !! solarized */
+/* *.color0: #073642 */
+/* *.color1: #dc322f */
+/* *.color2: #859900 */
+/* *.color3: #b58900 */
+/* *.color4: #268bd2 */
+/* *.color5: #d33682 */
+/* *.color6: #2aa198 */
+/* *.color7: #eee8d5 */
+/* *.color9: #cb4b16 */
+/* *.color8: #fdf6e3 */
+/* *.color10: #586e75 */
+/* *.color11: #657b83 */
+/* *.color12: #839496 */
+/* *.color13: #6c71c4 */
+/* *.color14: #93a1a1 */
+/* *.color15: #fdf6e3 */
+
+/* !! xterm */
+/* *.color0: #000000 */
+/* *.color1: #cd0000 */
+/* *.color2: #00cd00 */
+/* *.color3: #cdcd00 */
+/* *.color4: #0000cd */
+/* *.color5: #cd00cd */
+/* *.color6: #00cdcd */
+/* *.color7: #e5e5e5 */
+/* *.color8: #4d4d4d */
+/* *.color9: #ff0000 */
+/* *.color10: #00ff00 */
+/* *.color11: #ffff00 */
+/* *.color12: #0000ff */
+/* *.color13: #ff00ff */
+/* *.color14: #00ffff */
+/* *.color15: #aabac8 */
+/* *.background: #000000 */
+
+/* ! Dracula Xresources palette */
+/* *.foreground: #F8F8F2 */
+/* *.background: #282A36 */
+/* *.color0: #000000 */
+/* *.color8: #4D4D4D */
+/* *.color1: #FF5555 */
+/* *.color9: #FF6E67 */
+/* *.color2: #50FA7B */
+/* *.color10: #5AF78E */
+/* *.color3: #F1FA8C */
+/* *.color11: #F4F99D */
+/* *.color4: #BD93F9 */
+/* *.color12: #CAA9FA */
+/* *.color5: #FF79C6 */
+/* *.color13: #FF92D0 */
+/* *.color6: #8BE9FD */
+/* *.color14: #9AEDFE */
+/* *.color7: #BFBFBF */
+/* *.color15: #E6E6E6 */
+
+/* *.background: .color0 */
+/* *.color256: 0#1d2021 */
+/* *.color257: 15#ebdbb2 */
diff --git a/linux/home/.config/X11/.xbindkeysrc b/linux/home/.config/X11/.xbindkeysrc
new file mode 100644
index 0000000..481943d
--- /dev/null
+++ b/linux/home/.config/X11/.xbindkeysrc
@@ -0,0 +1,31 @@
+# # Up
+# "xdotool mousemove_relative --sync -- 0 -24"
+# alt + w
+#
+# # Left
+# "xdotool mousemove_relative --sync -- -24 0"
+# alt + a
+#
+# # Down
+# "xdotool mousemove_relative --sync -- 0 24"
+# alt + s
+#
+# # Right
+# "xdotool mousemove_relative --sync -- 24 0"
+# alt + d
+#
+# # left + up
+# "xdotool mousemove_relative --sync -- -24 -24"
+# Shift+alt + q
+#
+# # right + up
+# "xdotool mousemove_relative --sync -- 24 -24"
+# Shift+alt + e
+#
+# # right + down
+# "xdotool mousemove_relative --sync -- 24 24"
+# Shift+alt + d
+#
+# # left + down
+# "xdotool mousemove_relative --sync -- -24 24"
+# Shift+alt + a
diff --git a/linux/home/.config/X11/.xinitrc b/linux/home/.config/X11/.xinitrc
new file mode 100755
index 0000000..d500044
--- /dev/null
+++ b/linux/home/.config/X11/.xinitrc
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+#░█▀▀░▀█▀░█▀█░█▀▄░▀█▀░█░█
+#░▀▀█░░█░░█▀█░█▀▄░░█░░▄▀▄
+#░▀▀▀░░▀░░▀░▀░▀░▀░░▀░░▀░▀
+#
+
+# xinitrc.d
+if [ -d /etc/X11/xinit/xinitrc.d ]; then
+ for f in /etc/X11/xinit/xinitrc.d/*; do
+ [ -x "$f" ] && . "$f"
+ done
+ unset f
+fi
+
+# Keyboard layout
+setxkbmap -model pc105 -layout us -variant qwerty
+
+# xinitrc runs automatically when you run startx.
+
+# There are some small but important commands that need to be run when we start
+# the graphical environment. There is a link to this file in ~/.xprofile
+# because that file is run automatically if someone uses a display manager
+# (login screen) and so they are needed there. To prevent doubling up commands,
+# I source them here with the line below.
+
+# Profile
+[[ -f /etc/profile ]] && source /etc/profile
+
+if [ -f "${XDG_CONFIG_HOME:-$HOME/.config}/X11/.xprofile" ]; then
+ . "${XDG_CONFIG_HOME:-$HOME/.config}/X11/.xprofile"
+else
+ . "$HOME/.xprofile"
+fi
diff --git a/linux/home/.config/X11/.xprofile b/linux/home/.config/X11/.xprofile
new file mode 100644
index 0000000..bc2e5f8
--- /dev/null
+++ b/linux/home/.config/X11/.xprofile
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# Xresources
+[[ -f ~/.config/X11/.Xresources ]] && xrdb -merge ~/.config/X11/.Xresources
+
+#Xset
+xset r rate 150 50
+#xset r rate 290 70
+
+# Xbindkeys
+xbindkeys -f "$XDG_CONFIG_HOME"/X11/xbindkeysrc
+
+# XKB
+setxkbmap -print | xkbcomp -I"$HOME"/.config/xkb - "$DISPLAY"
+
+#export XAUTHORITY="$XDG_RUNTIME_DIR/.Xauthority" # This line will break some DMs.
+export USERXSESSION="$XDG_CACHE_HOME/X11/xsession"
+export USERXSESSIONRC="$XDG_CACHE_HOME/X11/xsessionrc"
+export ALTUSERXSESSION="$XDG_CACHE_HOME/X11/Xsession"
+export ERRFILE="$XDG_CACHE_HOME/X11/xsession-errors"
+
+if [[ "$(tty)" = "/dev/tty1" ]]; then
+ pgrep bspwm || startx "$XDG_CONFIG_HOME/X11/.xinitrc"
+fi
diff --git a/linux/home/.config/ags/.eslintrc.yml b/linux/home/.config/ags/.eslintrc.yml
new file mode 100644
index 0000000..ff96a83
--- /dev/null
+++ b/linux/home/.config/ags/.eslintrc.yml
@@ -0,0 +1,130 @@
+env:
+ es2022: true
+extends:
+ - "eslint:recommended"
+ - "plugin:@typescript-eslint/recommended"
+parser: "@typescript-eslint/parser"
+parserOptions:
+ ecmaVersion: 2022
+ sourceType: "module"
+ project: "./tsconfig.json"
+ warnOnUnsupportedTypeScriptVersion: false
+root: true
+ignorePatterns:
+ - types/
+plugins:
+ - "@typescript-eslint"
+rules:
+ "@typescript-eslint/ban-ts-comment":
+ - "off"
+ "@typescript-eslint/no-non-null-assertion":
+ - "off"
+ # "@typescript-eslint/no-explicit-any":
+ # - "off"
+ "@typescript-eslint/no-unused-vars":
+ - error
+ - varsIgnorePattern: (^unused|_$)
+ argsIgnorePattern: ^(unused|_)
+ "@typescript-eslint/no-empty-interface":
+ - "off"
+
+ arrow-parens:
+ - error
+ - as-needed
+ comma-dangle:
+ - error
+ - always-multiline
+ comma-spacing:
+ - error
+ - before: false
+ after: true
+ comma-style:
+ - error
+ - last
+ curly:
+ - error
+ - multi-or-nest
+ - consistent
+ dot-location:
+ - error
+ - property
+ eol-last:
+ - error
+ eqeqeq:
+ - error
+ - always
+ indent:
+ - error
+ - 4
+ - SwitchCase: 1
+ keyword-spacing:
+ - error
+ - before: true
+ lines-between-class-members:
+ - error
+ - always
+ - exceptAfterSingleLine: true
+ padded-blocks:
+ - error
+ - never
+ - allowSingleLineBlocks: false
+ prefer-const:
+ - error
+ quotes:
+ - error
+ - double
+ - avoidEscape: true
+ semi:
+ - error
+ - never
+ nonblock-statement-body-position:
+ - error
+ - below
+ no-trailing-spaces:
+ - error
+ no-useless-escape:
+ - off
+ max-len:
+ - error
+ - code: 100
+ func-call-spacing:
+ - error
+ array-bracket-spacing:
+ - error
+ space-before-function-paren:
+ - error
+ - anonymous: never
+ named: never
+ asyncArrow: ignore
+ space-before-blocks:
+ - error
+ key-spacing:
+ - error
+ object-curly-spacing:
+ - error
+ - always
+globals:
+ Widget: readonly
+ Utils: readonly
+ App: readonly
+ Variable: readonly
+ Service: readonly
+ pkg: readonly
+ ARGV: readonly
+ Debugger: readonly
+ GIRepositoryGType: readonly
+ globalThis: readonly
+ imports: readonly
+ Intl: readonly
+ log: readonly
+ logError: readonly
+ print: readonly
+ printerr: readonly
+ window: readonly
+ TextEncoder: readonly
+ TextDecoder: readonly
+ console: readonly
+ setTimeout: readonly
+ setInterval: readonly
+ clearTimeout: readonly
+ clearInterval: readonly
diff --git a/linux/home/.config/ags/.gitignore b/linux/home/.config/ags/.gitignore
new file mode 100644
index 0000000..f56dbd1
--- /dev/null
+++ b/linux/home/.config/ags/.gitignore
@@ -0,0 +1,6 @@
+node_modules
+types
+package-lock.json
+bun.lockb
+flake.lock
+.weather
diff --git a/linux/home/.config/ags/assets/arrows-down.svg b/linux/home/.config/ags/assets/arrows-down.svg
new file mode 100644
index 0000000..5851aed
--- /dev/null
+++ b/linux/home/.config/ags/assets/arrows-down.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" version="1.1">
+ <defs>
+ <style id="current-color-scheme" type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style>
+ </defs>
+ <g transform="matrix(1,0,0,1,4,4)">
+ <path class="ColorScheme-Text" d="M 7,2 V 10 L 3.5,6.5 2,8 8,14 14,8 12.5,6.5 9,10 V 2 Z" style="fill:currentColor"/>
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/assets/arrows-left.svg b/linux/home/.config/ags/assets/arrows-left.svg
new file mode 100644
index 0000000..f3c1b2e
--- /dev/null
+++ b/linux/home/.config/ags/assets/arrows-left.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" version="1.1">
+ <defs>
+ <style id="current-color-scheme" type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style>
+ </defs>
+ <g transform="matrix(1,0,0,1,4,4)">
+ <path class="ColorScheme-Text" d="M 14,7 H 6 L 9.5,3.5 8,2 2,8 8,14 9.5,12.5 6,9 H 14 Z" style="fill:currentColor"/>
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/assets/arrows-right.svg b/linux/home/.config/ags/assets/arrows-right.svg
new file mode 100644
index 0000000..30baa8f
--- /dev/null
+++ b/linux/home/.config/ags/assets/arrows-right.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" version="1.1">
+ <defs>
+ <style id="current-color-scheme" type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style>
+ </defs>
+ <g transform="matrix(1,0,0,1,4,4)">
+ <path class="ColorScheme-Text" d="M 2,9 H 10 L 6.5,12.5 8,14 14,8 8,2 6.5,3.5 10,7 H 2 Z" style="fill:currentColor"/>
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/assets/arrows-up.svg b/linux/home/.config/ags/assets/arrows-up.svg
new file mode 100644
index 0000000..68b9944
--- /dev/null
+++ b/linux/home/.config/ags/assets/arrows-up.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" version="1.1">
+ <defs>
+ <style id="current-color-scheme" type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style>
+ </defs>
+ <g transform="matrix(1,0,0,1,4,4)">
+ <path class="ColorScheme-Text" d="M 7,14 V 6 L 3.5,9.5 2,8 8,2 14,8 12.5,9.5 9,6 V 14 Z" style="fill:currentColor"/>
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/assets/battery-flash-symbolic.svg b/linux/home/.config/ags/assets/battery-flash-symbolic.svg
new file mode 100644
index 0000000..21b5e33
--- /dev/null
+++ b/linux/home/.config/ags/assets/battery-flash-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 8.96875 0 c -0.332031 0.0117188 -0.640625 0.1875 -0.816406 0.46875 l -5 8 c -0.105469 0.171875 -0.152344 0.355469 -0.152344 0.53125 v 1 h 3 v 5 c 0 1.003906 1.316406 1.378906 1.847656 0.53125 l 5 -8 c 0.105469 -0.171875 0.152344 -0.355469 0.152344 -0.53125 v -1 h -3 v -5 c 0 -0.5625 -0.464844 -1.015625 -1.03125 -1 z m 0 0"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/bomb-kill.svg b/linux/home/.config/ags/assets/bomb-kill.svg
new file mode 100644
index 0000000..a6e9f09
--- /dev/null
+++ b/linux/home/.config/ags/assets/bomb-kill.svg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ viewBox="0 0 22 22"
+ version="1.1"
+ id="svg1"
+ sodipodi:docname="bomb-kill.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview1"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="28.545455"
+ inkscape:cx="11"
+ inkscape:cy="11"
+ inkscape:current-layer="svg1" />
+ <defs
+ id="defs3051">
+ <style
+ type="text/css"
+ id="current-color-scheme">.ColorScheme-Text { color: #fcfcfc; } </style>
+ </defs>
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:0.855269"
+ d="m 17.858728,4.1743879 c -1.13103,0.2314779 -2.107737,0.9080479 -2.727751,1.858046 -0.214576,-0.095764 -0.450425,-0.1516429 -0.701613,-0.1516429 -0.178764,0 -0.347261,0.034927 -0.509046,0.084986 -0.975172,-0.5918778 -2.11854,-0.9381881 -3.348989,-0.9381881 -3.5622531,0 -6.4300571,2.8539574 -6.4300571,6.3990111 0,3.545055 2.867804,6.399011 6.4300571,6.399011 3.562254,0 6.430058,-2.853956 6.430058,-6.39901 0,-1.224507 -0.347991,-2.3623553 -0.94274,-3.3328191 0.0503,-0.1610043 0.0854,-0.3286874 0.0854,-0.5065884 0,-0.3822616 -0.128156,-0.7309813 -0.339922,-1.0148431 0.459139,-0.7437673 1.188057,-1.2957452 2.054604,-1.5197652 V 4.1743879 M 6.9862372,7.1755908 C 6.173802,8.138787 5.6851553,9.38169 5.6851553,10.743373 c 0,3.07238 2.4854307,5.54581 5.5727167,5.54581 1.36784,0 2.61568,-0.486764 3.583418,-1.2948 -1.020672,1.209625 -2.551332,1.978028 -4.269961,1.978028 -3.0872862,0 -5.5727161,-2.47343 -5.5727161,-5.54581 0,-1.7106949 0.7716789,-3.2352661 1.9876243,-4.2510102"
+ class="ColorScheme-Text"
+ id="path1" />
+</svg>
diff --git a/linux/home/.config/ags/assets/chat-bubbles-symbolic.svg b/linux/home/.config/ags/assets/chat-bubbles-symbolic.svg
new file mode 100644
index 0000000..fdee0b3
--- /dev/null
+++ b/linux/home/.config/ags/assets/chat-bubbles-symbolic.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 14 3.175781 v 3.824219 c 0 2.179688 -1.820312 4 -4 4 h -3.585938 l -2 2 h 5.585938 l 3 3 v -3 c 1.644531 0 3 -1.355469 3 -3 v -4 c 0 -1.292969 -0.839844 -2.40625 -2 -2.824219 z m 0 0" fill-opacity="0.34902"/>
+ <path d="m 3 0 c -1.644531 0 -3 1.355469 -3 3 v 4 c 0 1.644531 1.355469 3 3 3 v 3 l 3 -3 h 4 c 1.644531 0 3 -1.355469 3 -3 v -4 c 0 -1.644531 -1.355469 -3 -3 -3 z m 0 0"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/controller-symbolic.svg b/linux/home/.config/ags/assets/controller-symbolic.svg
new file mode 100644
index 0000000..98bf5d6
--- /dev/null
+++ b/linux/home/.config/ags/assets/controller-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 3.785156 2.03125 c -0.242187 0 -0.523437 0.066406 -0.804687 0.21875 c -1.039063 0.546875 -1.992188 2.335938 -2.511719 4.65625 c -0.4414062 1.972656 -0.605469 4.664062 -0.339844 5.75 c 0.226563 0.933594 0.625 1.34375 1.332032 1.34375 c 1.042968 -0.019531 2.359374 -1.183594 3.191406 -2.75 c 0.601562 -0.867188 2 -1.261719 3.347656 -1.21875 c 1.347656 -0.046875 2.746094 0.351562 3.347656 1.21875 c 0.832032 1.566406 2.148438 2.730469 3.191406 2.75 c 0.707032 0 1.105469 -0.410156 1.332032 -1.34375 c 0.265625 -1.085938 0.101562 -3.777344 -0.339844 -5.75 c -0.519531 -2.320312 -1.472656 -4.109375 -2.511719 -4.65625 c -0.566406 -0.304688 -1.039062 -0.296875 -1.453125 0 c -0.527344 0.375 -1.628906 0.78125 -3.566406 0.78125 c -1.9375 0.003906 -3.039062 -0.40625 -3.566406 -0.78125 c -0.207032 -0.148438 -0.40625 -0.21875 -0.648438 -0.21875 z m 0.246094 3 h 0.992188 v 1 h 0.992187 v 1 h -0.992187 v 1 h -0.992188 v -1 h -0.992188 v -1 h 0.992188 z m 7.441406 0 c 0.273438 0 0.496094 0.222656 0.496094 0.5 s -0.222656 0.5 -0.496094 0.5 c -0.273437 0 -0.496094 -0.222656 -0.496094 -0.5 s 0.222657 -0.5 0.496094 -0.5 z m -0.992187 1 c 0.273437 0 0.496093 0.222656 0.496093 0.5 s -0.222656 0.5 -0.496093 0.5 c -0.273438 0 -0.496094 -0.222656 -0.496094 -0.5 s 0.222656 -0.5 0.496094 -0.5 z m 1.984375 0 c 0.273437 0 0.496094 0.222656 0.496094 0.5 s -0.222657 0.5 -0.496094 0.5 c -0.273438 0 -0.496094 -0.222656 -0.496094 -0.5 s 0.222656 -0.5 0.496094 -0.5 z m -0.992188 1 c 0.273438 0 0.496094 0.222656 0.496094 0.5 s -0.222656 0.5 -0.496094 0.5 c -0.273437 0 -0.496094 -0.222656 -0.496094 -0.5 s 0.222657 -0.5 0.496094 -0.5 z m 0 0"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/controls-symbolic.svg b/linux/home/.config/ags/assets/controls-symbolic.svg
new file mode 100644
index 0000000..7df5663
--- /dev/null
+++ b/linux/home/.config/ags/assets/controls-symbolic.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 4.550781 1 c -1.9375 0 -3.5 1.5625 -3.5 3.5 s 1.5625 3.5 3.5 3.5 h 7 c 1.941407 0 3.5 -1.5625 3.5 -3.5 s -1.558593 -3.5 -3.5 -3.5 z m 7 1 c 1.386719 0 2.5 1.113281 2.5 2.5 c 0 1.382812 -1.113281 2.5 -2.5 2.5 c -1.382812 0 -2.5 -1.117188 -2.5 -2.5 c 0 -1.386719 1.117188 -2.5 2.5 -2.5 z m 0 0"/>
+ <path d="m 4.550781 9 c -1.9375 0 -3.5 1.5625 -3.5 3.5 s 1.5625 3.5 3.5 3.5 h 7 c 1.941407 0 3.5 -1.5625 3.5 -3.5 s -1.558593 -3.5 -3.5 -3.5 z m 0 1 c 1.386719 0 2.5 1.113281 2.5 2.5 c 0 1.382812 -1.113281 2.5 -2.5 2.5 c -1.382812 0 -2.5 -1.117188 -2.5 -2.5 c 0 -1.386719 1.117188 -2.5 2.5 -2.5 z m 0 0" fill-opacity="0.34902"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/dark-mode-symbolic.svg b/linux/home/.config/ags/assets/dark-mode-symbolic.svg
new file mode 100644
index 0000000..9f2e6b4
--- /dev/null
+++ b/linux/home/.config/ags/assets/dark-mode-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 0.917969 8.003906 c 0 3.914063 3.164062 7.078125 7.078125 7.078125 c 3.605468 -0.007812 6.617187 -2.703125 7.023437 -6.285156 c 0.042969 -0.378906 -0.136719 -0.75 -0.457031 -0.957031 c -0.324219 -0.203125 -0.738281 -0.207032 -1.0625 -0.003906 c -0.609375 0.375 -1.316406 0.578124 -2.03125 0.578124 c -2.140625 0 -3.882812 -1.742187 -3.882812 -3.882812 c 0 -0.714844 0.203124 -1.421875 0.578124 -2.03125 c 0.203126 -0.324219 0.199219 -0.738281 -0.003906 -1.0625 c -0.207031 -0.320312 -0.578125 -0.5 -0.957031 -0.457031 c -3.582031 0.40625 -6.277344 3.417969 -6.285156 7.023437 z m 4.667969 -3.472656 c 0 3.253906 2.628906 5.882812 5.886718 5.882812 c 1.085938 0 2.152344 -0.304687 3.078125 -0.878906 l -1.519531 -0.960937 c -0.289062 2.554687 -2.464844 4.503906 -5.035156 4.507812 c -2.796875 0 -5.078125 -2.28125 -5.078125 -5.078125 c 0.003906 -2.570312 1.953125 -4.746094 4.507812 -5.035156 l -0.960937 -1.519531 c -0.574219 0.925781 -0.875 1.992187 -0.878906 3.082031 z m 0 0"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/float.svg b/linux/home/.config/ags/assets/float.svg
new file mode 100644
index 0000000..dc8078a
--- /dev/null
+++ b/linux/home/.config/ags/assets/float.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ viewBox="0 0 16 16"
+ version="1.1"
+ id="svg2"
+ sodipodi:docname="float.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview2"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="39.25"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:current-layer="svg2" />
+ <defs
+ id="defs1">
+ <style
+ type="text/css"
+ id="current-color-scheme">.ColorScheme-Text { color: #fcfcfc; } </style>
+ </defs>
+ <path
+ class="ColorScheme-Text"
+ d="M 7.8252389,2.2944865 A 5.6426155,5.4275975 0 0 0 2.1826234,7.722084 5.6426155,5.4275975 0 0 0 7.8252389,13.149682 5.6426155,5.4275975 0 0 0 13.467855,7.722084 5.6426155,5.4275975 0 0 0 7.8252389,2.2944865 m 0,0.9045996 A 4.7021795,4.5229979 0 0 1 12.527419,7.722084 4.7021795,4.5229979 0 0 1 7.8252389,12.245082 4.7021795,4.5229979 0 0 1 3.1230593,7.722084 4.7021795,4.5229979 0 0 1 7.8252389,3.1990861"
+ fill="currentColor"
+ id="path1"
+ style="stroke-width:0.922343" />
+ <path
+ class="ColorScheme-Text"
+ d="M 12.52741,7.7220642 A 4.7021795,4.5229979 0 0 1 7.8252308,12.245062 4.7021795,4.5229979 0 0 1 3.1230512,7.7220642 4.7021795,4.5229979 0 0 1 7.8252308,3.1990663 4.7021795,4.5229979 0 0 1 12.52741,7.7220642 Z"
+ fill="currentColor"
+ fill-opacity="0.5"
+ id="path2"
+ style="stroke-width:0.922343" />
+</svg>
diff --git a/linux/home/.config/ags/assets/fullscreen.svg b/linux/home/.config/ags/assets/fullscreen.svg
new file mode 100644
index 0000000..e9ab0b8
--- /dev/null
+++ b/linux/home/.config/ags/assets/fullscreen.svg
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="24"
+ height="24"
+ version="1.1"
+ id="svg1"
+ sodipodi:docname="fullscreen.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview1"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="19.666667"
+ inkscape:cx="12"
+ inkscape:cy="12"
+ inkscape:current-layer="svg1" />
+ <defs
+ id="defs1">
+ <style
+ id="current-color-scheme"
+ type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style>
+ </defs>
+ <g
+ transform="matrix(0.94763804,0,0,0.95290155,4.4188957,4.3767876)"
+ id="g1">
+ <path
+ style="fill:currentColor"
+ class="ColorScheme-Text"
+ d="M 4,10 1,8 4,6 Z m 8,0 3,-2 -3,-2 z m -4,5 2,-3 H 6 Z M 8,1 10,4 H 6 Z m 7,6 h 1 V 9 H 15 Z M 0,7 H 1 V 9 H 0 Z m 7,8 h 2 v 1 H 7 Z M 7,0 H 9 V 1 H 7 Z M 0,16 v -5 h 1 v 4 h 4 v 1 z m 16,0 v -4 h -1 v 3 h -4 v 1 z M 16,0 V 5 H 15 V 1 H 11 V 0 Z M 0,0 V 5 H 1 V 1 H 5 V 0 Z"
+ id="path1" />
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/assets/hourglass-symbolic.svg b/linux/home/.config/ags/assets/hourglass-symbolic.svg
new file mode 100644
index 0000000..aa4f97c
--- /dev/null
+++ b/linux/home/.config/ags/assets/hourglass-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 5 0 c -0.96875 0 -2 1.050781 -2 2 v 2.988281 c 0 0.429688 0.222656 0.675781 0.554688 1.007813 l 2.023437 2.003906 l -2.007813 1.992188 c -0.367187 0.363281 -0.570312 0.6875 -0.570312 1 v 3.007812 c 0 1.011719 0.988281 2 2 2 h 6 c 1.007812 0 2 -1.011719 2 -2.003906 v -3.003906 c 0 -0.3125 -0.222656 -0.628907 -0.570312 -0.976563 l -2.015626 -2.015625 l 1.988282 -1.988281 c 0.261718 -0.261719 0.585937 -0.6875 0.597656 -1.015625 v -2.996094 c 0 -1.003906 -1.007812 -2 -2 -2 z m 6 4 h -6 v -2 h 6 m -3.589844 7 h 1.175782 l 2.414062 2.414062 v 1.585938 h -6 v -1.613281 z m 0 0"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/light-mode-symbolic.svg b/linux/home/.config/ags/assets/light-mode-symbolic.svg
new file mode 100644
index 0000000..d5fb271
--- /dev/null
+++ b/linux/home/.config/ags/assets/light-mode-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 8 0 c -0.554688 0 -1 0.445312 -1 1 v 1 c 0 0.554688 0.445312 1 1 1 s 1 -0.445312 1 -1 v -1 c 0 -0.554688 -0.445312 -1 -1 -1 z m -4.996094 2.003906 c -0.253906 0 -0.507812 0.097656 -0.707031 0.296875 c -0.390625 0.390625 -0.390625 1.019531 0 1.414063 l 0.707031 0.707031 c 0.394532 0.390625 1.023438 0.390625 1.414063 0 c 0.394531 -0.394531 0.394531 -1.023437 0 -1.414063 l -0.707031 -0.707031 c -0.195313 -0.199219 -0.449219 -0.296875 -0.707032 -0.296875 z m 9.988282 0 c -0.253907 0 -0.507813 0.097656 -0.707032 0.296875 l -0.707031 0.707031 c -0.390625 0.390626 -0.390625 1.019532 0 1.414063 c 0.394531 0.390625 1.023437 0.390625 1.414063 0 l 0.707031 -0.707031 c 0.394531 -0.394532 0.394531 -1.023438 0 -1.414063 c -0.195313 -0.199219 -0.449219 -0.296875 -0.707031 -0.296875 z m -4.992188 1.996094 c -2.210938 0 -4 1.789062 -4 4 s 1.789062 4 4 4 s 4 -1.789062 4 -4 s -1.789062 -4 -4 -4 z m 0 2 c 1.105469 0 2 0.894531 2 2 s -0.894531 2 -2 2 s -2 -0.894531 -2 -2 s 0.894531 -2 2 -2 z m -7 1 c -0.554688 0 -1 0.445312 -1 1 s 0.445312 1 1 1 h 1 c 0.554688 0 1 -0.445312 1 -1 s -0.445312 -1 -1 -1 z m 13 0 c -0.554688 0 -1 0.445312 -1 1 s 0.445312 1 1 1 h 1 c 0.554688 0 1 -0.445312 1 -1 s -0.445312 -1 -1 -1 z m -10.335938 4.289062 c -0.238281 0.007813 -0.472656 0.105469 -0.660156 0.292969 l -0.707031 0.707031 c -0.390625 0.390626 -0.390625 1.019532 0 1.414063 c 0.394531 0.390625 1.023437 0.390625 1.414063 0 l 0.707031 -0.707031 c 0.394531 -0.394532 0.394531 -1.023438 0 -1.414063 c -0.207031 -0.210937 -0.484375 -0.308593 -0.753907 -0.292969 z m 8.574219 0 c -0.238281 0.007813 -0.472656 0.105469 -0.660156 0.292969 c -0.390625 0.390625 -0.390625 1.019531 0 1.414063 l 0.707031 0.707031 c 0.394532 0.390625 1.023438 0.390625 1.414063 0 c 0.394531 -0.394531 0.394531 -1.023437 0 -1.414063 l -0.707031 -0.707031 c -0.207032 -0.210937 -0.484376 -0.308593 -0.753907 -0.292969 z m -4.292969 1.710938 c -0.527343 0.027344 -0.945312 0.464844 -0.945312 1 v 1 c 0 0.554688 0.445312 1 1 1 s 1 -0.445312 1 -1 v -1 c 0 -0.554688 -0.445312 -1 -1 -1 c -0.015625 0 -0.035156 0 -0.050781 0 z m 0 0"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/mixer-symbolic.svg b/linux/home/.config/ags/assets/mixer-symbolic.svg
new file mode 100644
index 0000000..ad6cfa8
--- /dev/null
+++ b/linux/home/.config/ags/assets/mixer-symbolic.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 11.5 1 c -1.921875 0 -3.5 1.578125 -3.5 3.5 s 1.578125 3.5 3.5 3.5 s 3.5 -1.578125 3.5 -3.5 s -1.578125 -3.5 -3.5 -3.5 z m 0 2 c 0.839844 0 1.5 0.660156 1.5 1.5 s -0.660156 1.5 -1.5 1.5 s -1.5 -0.660156 -1.5 -1.5 s 0.660156 -1.5 1.5 -1.5 z m 0 0"/>
+ <path d="m 4.5 8 c -1.921875 0 -3.5 1.578125 -3.5 3.5 s 1.578125 3.5 3.5 3.5 c 1.386719 0 2.59375 -0.820312 3.15625 -2 h 5.84375 c 0.832031 0 1.5 -0.667969 1.5 -1.5 s -0.667969 -1.5 -1.5 -1.5 h -5.84375 c -0.5625 -1.179688 -1.769531 -2 -3.15625 -2 z m 0 2 c 0.839844 0 1.5 0.660156 1.5 1.5 s -0.660156 1.5 -1.5 1.5 s -1.5 -0.660156 -1.5 -1.5 s 0.660156 -1.5 1.5 -1.5 z m 0 0"/>
+ <path d="m 2.5 3 c -0.832031 0 -1.5 0.667969 -1.5 1.5 s 0.667969 1.5 1.5 1.5 h 4.769531 c -0.175781 -0.480469 -0.265625 -0.988281 -0.269531 -1.5 c 0 -0.511719 0.09375 -1.019531 0.269531 -1.5 z m 0 0" fill-opacity="0.34902"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/nix-snowflake-symbolic.svg b/linux/home/.config/ags/assets/nix-snowflake-symbolic.svg
new file mode 100644
index 0000000..7bb42ed
--- /dev/null
+++ b/linux/home/.config/ags/assets/nix-snowflake-symbolic.svg
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="496"
+ height="496"
+ version="1"
+ id="svg6"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <g
+ id="g946"
+ transform="matrix(0.97173996,0,0,0.97173996,4.043873,36.112138)">
+ <g
+ id="layer7"
+ style="display:none"
+ transform="translate(-23.75651,-24.84972)">
+ <rect
+ transform="translate(-132.5822,958.04022)"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5389"
+ width="1543.4283"
+ height="483.7439"
+ x="132.5822"
+ y="-957.77832" />
+ </g>
+ <g
+ id="layer6"
+ style="display:none"
+ transform="translate(-156.33871,933.1905)">
+ <rect
+ y="-958.02759"
+ x="132.65129"
+ height="484.30399"
+ width="550.41602"
+ id="rect5379"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5c201e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c24a46;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5372"
+ width="501.94415"
+ height="434.30405"
+ x="156.12303"
+ y="-933.02759" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#d98d8a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5381"
+ width="24.939611"
+ height="24.939611"
+ x="658.02826"
+ y="-958.04022" />
+ </g>
+ <g
+ id="layer3"
+ style="display:inline;opacity:1"
+ transform="translate(37.235605,912.8581)">
+ <g
+ id="g2072"
+ transform="matrix(0.99894325,0,0,0.99894325,-36.551621,-913.90743)"
+ style="fill:#cccccc;fill-opacity:1">
+ <g
+ style="display:none;fill:#cccccc;fill-opacity:1"
+ transform="matrix(0.09048806,0,0,0.09048806,-14.15991,84.454917)"
+ id="layer1-3">
+ <rect
+ y="-2102.4253"
+ x="-1045.6049"
+ height="7145.4614"
+ width="7947.0356"
+ id="rect995"
+ style="opacity:1;fill:#cccccc;fill-opacity:1;stroke-width:10.3605" />
+ </g>
+ <g
+ transform="translate(-156.48372,537.56136)"
+ style="display:inline;opacity:1;fill:#cccccc;fill-opacity:1"
+ id="layer3-6">
+ <g
+ style="fill:#cccccc;stroke-width:11.0512;fill-opacity:1"
+ transform="matrix(0.09048806,0,0,0.09048806,142.32381,-453.10644)"
+ id="g955">
+ <g
+ transform="matrix(11.047619,0,0,11.047619,-1572.2888,9377.7107)"
+ id="g869"
+ style="fill:#cccccc;fill-opacity:1">
+ <g
+ transform="rotate(-60,226.35754,-449.37199)"
+ id="g932"
+ style="fill:#cccccc;stroke-width:11.0512;fill-opacity:1">
+ <path
+ id="path3336-6-7"
+ d="m 449.71876,-420.51322 c 40.73228,70.55837 81.46455,141.11675 122.19683,211.67512 -18.71902,0.1756 -37.43804,0.3512 -56.15706,0.5268 -10.87453,-18.9564 -21.74907,-37.9128 -32.6236,-56.8692 -10.95215,18.8551 -21.9043,37.7102 -32.85645,56.5653 -9.30079,-0.004 -18.60158,-0.007 -27.90237,-0.011 -4.76362,-8.22987 -9.52724,-16.45973 -14.29086,-24.6896 15.60349,-26.83003 31.20698,-53.66007 46.81047,-80.4901 -11.07649,-19.27523 -22.15297,-38.55047 -33.22946,-57.8257 9.35083,-16.29387 18.70167,-32.58775 28.0525,-48.88162 z"
+ style="opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:33.1535;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <path
+ id="path4260-0-5"
+ d="m 309.54892,-710.38827 c 40.73228,70.55837 81.46455,141.11675 122.19683,211.67512 -18.71902,0.1756 -37.43804,0.3512 -56.15706,0.5268 -10.87453,-18.9564 -21.74907,-37.9128 -32.6236,-56.8692 -10.95215,18.8551 -21.9043,37.7102 -32.85645,56.5653 -9.30079,-0.004 -18.60158,-0.007 -27.90237,-0.011 -4.76362,-8.22987 -9.52724,-16.45973 -14.29086,-24.6896 15.60349,-26.83003 31.20698,-53.66007 46.81047,-80.4901 -11.07649,-19.2752 -22.15297,-38.5504 -33.22946,-57.8256 9.35083,-16.29391 18.70167,-32.58781 28.0525,-48.88172 z"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:33.1535;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#path3336-6-7"
+ id="use3439-6-3"
+ transform="rotate(60,728.23563,-692.24036)"
+ width="100%"
+ height="100%"
+ style="fill:#cccccc;fill-opacity:1;stroke-width:11.0512" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#path3336-6-7"
+ id="use3449-5-5"
+ transform="rotate(180,477.5036,-570.81898)"
+ width="100%"
+ height="100%"
+ style="fill:#cccccc;fill-opacity:1;stroke-width:11.0512" />
+ <use
+ style="display:inline;fill:#cccccc;fill-opacity:1;stroke-width:11.0512"
+ x="0"
+ y="0"
+ xlink:href="#path4260-0-5"
+ id="use4354-5-6"
+ transform="rotate(120,407.33916,-716.08356)"
+ width="100%"
+ height="100%" />
+ <use
+ style="display:inline;fill:#cccccc;fill-opacity:1;stroke-width:11.0512"
+ x="0"
+ y="0"
+ xlink:href="#path4260-0-5"
+ id="use4362-2-2"
+ transform="rotate(-120,407.28823,-715.86995)"
+ width="100%"
+ height="100%" />
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/assets/nixos-symbolic.svg b/linux/home/.config/ags/assets/nixos-symbolic.svg
new file mode 100644
index 0000000..7bb42ed
--- /dev/null
+++ b/linux/home/.config/ags/assets/nixos-symbolic.svg
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="496"
+ height="496"
+ version="1"
+ id="svg6"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <g
+ id="g946"
+ transform="matrix(0.97173996,0,0,0.97173996,4.043873,36.112138)">
+ <g
+ id="layer7"
+ style="display:none"
+ transform="translate(-23.75651,-24.84972)">
+ <rect
+ transform="translate(-132.5822,958.04022)"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5389"
+ width="1543.4283"
+ height="483.7439"
+ x="132.5822"
+ y="-957.77832" />
+ </g>
+ <g
+ id="layer6"
+ style="display:none"
+ transform="translate(-156.33871,933.1905)">
+ <rect
+ y="-958.02759"
+ x="132.65129"
+ height="484.30399"
+ width="550.41602"
+ id="rect5379"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5c201e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c24a46;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5372"
+ width="501.94415"
+ height="434.30405"
+ x="156.12303"
+ y="-933.02759" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#d98d8a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5381"
+ width="24.939611"
+ height="24.939611"
+ x="658.02826"
+ y="-958.04022" />
+ </g>
+ <g
+ id="layer3"
+ style="display:inline;opacity:1"
+ transform="translate(37.235605,912.8581)">
+ <g
+ id="g2072"
+ transform="matrix(0.99894325,0,0,0.99894325,-36.551621,-913.90743)"
+ style="fill:#cccccc;fill-opacity:1">
+ <g
+ style="display:none;fill:#cccccc;fill-opacity:1"
+ transform="matrix(0.09048806,0,0,0.09048806,-14.15991,84.454917)"
+ id="layer1-3">
+ <rect
+ y="-2102.4253"
+ x="-1045.6049"
+ height="7145.4614"
+ width="7947.0356"
+ id="rect995"
+ style="opacity:1;fill:#cccccc;fill-opacity:1;stroke-width:10.3605" />
+ </g>
+ <g
+ transform="translate(-156.48372,537.56136)"
+ style="display:inline;opacity:1;fill:#cccccc;fill-opacity:1"
+ id="layer3-6">
+ <g
+ style="fill:#cccccc;stroke-width:11.0512;fill-opacity:1"
+ transform="matrix(0.09048806,0,0,0.09048806,142.32381,-453.10644)"
+ id="g955">
+ <g
+ transform="matrix(11.047619,0,0,11.047619,-1572.2888,9377.7107)"
+ id="g869"
+ style="fill:#cccccc;fill-opacity:1">
+ <g
+ transform="rotate(-60,226.35754,-449.37199)"
+ id="g932"
+ style="fill:#cccccc;stroke-width:11.0512;fill-opacity:1">
+ <path
+ id="path3336-6-7"
+ d="m 449.71876,-420.51322 c 40.73228,70.55837 81.46455,141.11675 122.19683,211.67512 -18.71902,0.1756 -37.43804,0.3512 -56.15706,0.5268 -10.87453,-18.9564 -21.74907,-37.9128 -32.6236,-56.8692 -10.95215,18.8551 -21.9043,37.7102 -32.85645,56.5653 -9.30079,-0.004 -18.60158,-0.007 -27.90237,-0.011 -4.76362,-8.22987 -9.52724,-16.45973 -14.29086,-24.6896 15.60349,-26.83003 31.20698,-53.66007 46.81047,-80.4901 -11.07649,-19.27523 -22.15297,-38.55047 -33.22946,-57.8257 9.35083,-16.29387 18.70167,-32.58775 28.0525,-48.88162 z"
+ style="opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:33.1535;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <path
+ id="path4260-0-5"
+ d="m 309.54892,-710.38827 c 40.73228,70.55837 81.46455,141.11675 122.19683,211.67512 -18.71902,0.1756 -37.43804,0.3512 -56.15706,0.5268 -10.87453,-18.9564 -21.74907,-37.9128 -32.6236,-56.8692 -10.95215,18.8551 -21.9043,37.7102 -32.85645,56.5653 -9.30079,-0.004 -18.60158,-0.007 -27.90237,-0.011 -4.76362,-8.22987 -9.52724,-16.45973 -14.29086,-24.6896 15.60349,-26.83003 31.20698,-53.66007 46.81047,-80.4901 -11.07649,-19.2752 -22.15297,-38.5504 -33.22946,-57.8256 9.35083,-16.29391 18.70167,-32.58781 28.0525,-48.88172 z"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:33.1535;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#path3336-6-7"
+ id="use3439-6-3"
+ transform="rotate(60,728.23563,-692.24036)"
+ width="100%"
+ height="100%"
+ style="fill:#cccccc;fill-opacity:1;stroke-width:11.0512" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#path3336-6-7"
+ id="use3449-5-5"
+ transform="rotate(180,477.5036,-570.81898)"
+ width="100%"
+ height="100%"
+ style="fill:#cccccc;fill-opacity:1;stroke-width:11.0512" />
+ <use
+ style="display:inline;fill:#cccccc;fill-opacity:1;stroke-width:11.0512"
+ x="0"
+ y="0"
+ xlink:href="#path4260-0-5"
+ id="use4354-5-6"
+ transform="rotate(120,407.33916,-716.08356)"
+ width="100%"
+ height="100%" />
+ <use
+ style="display:inline;fill:#cccccc;fill-opacity:1;stroke-width:11.0512"
+ x="0"
+ y="0"
+ xlink:href="#path4260-0-5"
+ id="use4362-2-2"
+ transform="rotate(-120,407.28823,-715.86995)"
+ width="100%"
+ height="100%" />
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/assets/nixos.svg b/linux/home/.config/ags/assets/nixos.svg
new file mode 100644
index 0000000..1a756ed
--- /dev/null
+++ b/linux/home/.config/ags/assets/nixos.svg
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="496"
+ height="496"
+ version="1"
+ id="svg6"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10">
+ <linearGradient
+ id="linearGradient5562">
+ <stop
+ style="stop-color:#699ad7;stop-opacity:1"
+ offset="0"
+ id="stop5564" />
+ <stop
+ id="stop5566"
+ offset="0.24345198"
+ style="stop-color:#7eb1dd;stop-opacity:1" />
+ <stop
+ style="stop-color:#7ebae4;stop-opacity:1"
+ offset="1"
+ id="stop5568" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5053">
+ <stop
+ style="stop-color:#415e9a;stop-opacity:1"
+ offset="0"
+ id="stop5055" />
+ <stop
+ id="stop5057"
+ offset="0.23168644"
+ style="stop-color:#4a6baf;stop-opacity:1" />
+ <stop
+ style="stop-color:#5277c3;stop-opacity:1"
+ offset="1"
+ id="stop5059" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient5960">
+ <stop
+ id="stop5962"
+ offset="0"
+ style="stop-color:#637ddf;stop-opacity:1" />
+ <stop
+ style="stop-color:#649afa;stop-opacity:1"
+ offset="0.23168644"
+ id="stop5964" />
+ <stop
+ id="stop5966"
+ offset="1"
+ style="stop-color:#719efa;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ y2="515.97058"
+ x2="282.26105"
+ y1="338.62445"
+ x1="213.95642"
+ gradientTransform="translate(983.36076,293.12113)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5855"
+ xlink:href="#linearGradient5960" />
+ <linearGradient
+ xlink:href="#linearGradient5562"
+ id="linearGradient4328"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(70.650339,-1055.1511)"
+ x1="200.59668"
+ y1="351.41116"
+ x2="290.08701"
+ y2="506.18814" />
+ <linearGradient
+ xlink:href="#linearGradient5053"
+ id="linearGradient4330"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(864.69589,-1491.3405)"
+ x1="-584.19934"
+ y1="782.33563"
+ x2="-496.29703"
+ y2="937.71399" />
+ </defs>
+ <g
+ id="g946"
+ transform="matrix(0.97173996,0,0,0.97173996,4.043873,36.112138)">
+ <g
+ id="layer7"
+ style="display:none"
+ transform="translate(-23.75651,-24.84972)">
+ <rect
+ transform="translate(-132.5822,958.04022)"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5389"
+ width="1543.4283"
+ height="483.7439"
+ x="132.5822"
+ y="-957.77832" />
+ </g>
+ <g
+ id="layer6"
+ style="display:none"
+ transform="translate(-156.33871,933.1905)">
+ <rect
+ y="-958.02759"
+ x="132.65129"
+ height="484.30399"
+ width="550.41602"
+ id="rect5379"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5c201e;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c24a46;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5372"
+ width="501.94415"
+ height="434.30405"
+ x="156.12303"
+ y="-933.02759" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#d98d8a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5381"
+ width="24.939611"
+ height="24.939611"
+ x="658.02826"
+ y="-958.04022" />
+ </g>
+ <g
+ id="layer1"
+ style="display:inline"
+ transform="translate(-156.33871,933.1905)">
+ <path
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 309.40365,-710.2521 c 40.73228,70.55837 81.46455,141.11673 122.19683,211.6751 -18.71902,0.1756 -37.43804,0.3512 -56.15706,0.5268 -10.87453,-18.9564 -21.74907,-37.9128 -32.6236,-56.8692 -10.95215,18.8551 -21.9043,37.7102 -32.85645,56.5653 -9.30079,-0.004 -18.60158,-0.007 -27.90237,-0.011 -4.76362,-8.22987 -9.52724,-16.45973 -14.29086,-24.6896 15.60349,-26.83007 31.20698,-53.66013 46.81047,-80.4902 -11.07649,-19.2752 -22.15297,-38.5504 -33.22946,-57.8256 9.35083,-16.29387 18.70167,-32.58773 28.0525,-48.8816 z"
+ id="path4861" />
+ <path
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#7ebae4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 353.50926,-797.4433 c -40.73919,70.55437 -81.47837,141.10873 -122.21756,211.6631 -9.51159,-16.12333 -19.02318,-32.24667 -28.53477,-48.37 10.97946,-18.89583 21.95893,-37.79167 32.93839,-56.6875 -21.80507,-0.0573 -43.61014,-0.1146 -65.41521,-0.1719 -4.64713,-8.0566 -9.29427,-16.1132 -13.9414,-24.1698 4.74546,-8.24033 9.49091,-16.48067 14.23637,-24.721 31.03726,0.098 62.07451,0.19593 93.11177,0.2939 11.15457,-19.2301 22.30914,-38.4602 33.46371,-57.6903 18.78623,-0.0488 37.57247,-0.0977 56.3587,-0.1465 z"
+ id="use4863" />
+ <path
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#7ebae4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 362.88537,-628.243 c 81.47146,0.004 162.94293,0.008 244.41439,0.012 -9.20743,16.29893 -18.41486,32.59787 -27.62229,48.8968 -21.854,-0.0606 -43.70799,-0.12113 -65.56199,-0.1817 10.85292,18.91237 21.70584,37.82473 32.55876,56.7371 -4.65366,8.05283 -9.30732,16.10567 -13.96098,24.1585 -9.50907,0.0107 -19.01815,0.0213 -28.52722,0.032 -15.43377,-26.92803 -30.86753,-53.85607 -46.3013,-80.7841 -22.23106,-0.0451 -44.46211,-0.0902 -66.69317,-0.1353 -9.4354,-16.2451 -18.8708,-32.4902 -28.3062,-48.7353 z"
+ id="use4865" />
+ <path
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#7ebae4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 505.14318,-720.9886 c -40.73228,-70.55837 -81.46455,-141.11673 -122.19683,-211.6751 18.71902,-0.1756 37.43804,-0.3512 56.15706,-0.5268 10.87453,18.9564 21.74907,37.9128 32.6236,56.8692 10.95215,-18.8551 21.9043,-37.7102 32.85645,-56.5653 9.30079,0.004 18.60158,0.007 27.90237,0.011 4.76362,8.22987 9.52724,16.45973 14.29086,24.6896 -15.60349,26.83007 -31.20698,53.66013 -46.81047,80.4902 11.07649,19.2752 22.15297,38.5504 33.22946,57.8256 -9.35083,16.29387 -18.70167,32.58773 -28.0525,48.8816 z"
+ id="use4867" />
+ <path
+ id="path4873"
+ d="m 309.40365,-710.2521 c 40.73228,70.55837 81.46455,141.11673 122.19683,211.6751 -18.71902,0.1756 -37.43804,0.3512 -56.15706,0.5268 -10.87453,-18.9564 -21.74907,-37.9128 -32.6236,-56.8692 -10.95215,18.8551 -21.9043,37.7102 -32.85645,56.5653 -9.30079,-0.004 -18.60158,-0.007 -27.90237,-0.011 -4.76362,-8.22987 -9.52724,-16.45973 -14.29086,-24.6896 15.60349,-26.83007 31.20698,-53.66013 46.81047,-80.4902 -11.07649,-19.2752 -22.15297,-38.5504 -33.22946,-57.8256 9.35083,-16.29387 18.70167,-32.58773 28.0525,-48.8816 z"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <path
+ id="use4875"
+ d="m 451.3364,-803.53264 c -81.47147,-0.004 -162.94293,-0.008 -244.4144,-0.012 9.20743,-16.29895 18.41486,-32.5979 27.62229,-48.89685 21.854,0.0606 43.70799,0.12117 65.56199,0.18175 -10.85292,-18.91239 -21.70583,-37.82478 -32.55875,-56.73717 4.65366,-8.05284 9.30731,-16.10567 13.96097,-24.15851 9.50907,-0.0105 19.01815,-0.021 28.52722,-0.0315 15.43377,26.92805 30.86753,53.85609 46.3013,80.78414 22.23106,0.0451 44.46211,0.0902 66.69317,0.13524 9.4354,16.24497 18.87081,32.48993 28.30621,48.7349 z"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <path
+ id="use4877"
+ d="m 460.87178,-633.8425 c 40.73919,-70.55435 81.47838,-141.10869 122.21757,-211.66304 9.51159,16.12334 19.02318,32.24669 28.53477,48.37003 -10.97946,18.89584 -21.95893,37.79167 -32.93839,56.68751 21.80507,0.0573 43.61013,0.11453 65.4152,0.1718 4.64713,8.0566 9.29427,16.1132 13.9414,24.1698 -4.74545,8.24037 -9.49091,16.48073 -14.23636,24.7211 -31.03726,-0.098 -62.07451,-0.196 -93.11177,-0.294 -11.15457,19.23013 -22.30914,38.46027 -33.46371,57.6904 -18.78624,0.0488 -37.57247,0.0976 -56.35871,0.1464 z"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#5277c3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <g
+ id="layer2"
+ style="display:none"
+ transform="translate(72.039038,-1799.4476)">
+ <path
+ d="M 460.60629,594.72881 209.74183,594.7288 84.309616,377.4738 209.74185,160.21882 l 250.86446,1e-5 125.43222,217.255 z"
+ id="path6032"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.236;fill:#4e4d52;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
+ <path
+ transform="translate(0,-308.26772)"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#4e4d52;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="path5875"
+ d="m 385.59154,773.06721 -100.83495,0 -50.41747,-87.32564 50.41748,-87.32563 100.83495,10e-6 50.41748,87.32563 z" />
+ <path
+ id="path5851"
+ d="m 1216.5591,630.26623 c 41.0182,76.04675 82.0363,152.09355 123.0545,228.14035 -14.2269,-0.4205 -28.4538,-0.8411 -42.6807,-1.2616 -14.4941,-26.5908 -28.9882,-53.1817 -43.4823,-79.7725 -13.2169,26.7756 -26.4337,53.5511 -39.6506,80.3267 -10.8958,-6.5995 -21.7917,-13.1989 -32.6875,-19.7984 17.8246,-33.4283 35.6491,-66.8565 53.4737,-100.2848 -12.3719,-24.6298 -24.7438,-49.2597 -37.1157,-73.88955 6.3629,-11.1534 12.7257,-22.3068 19.0886,-33.4602 z"
+ style="fill:url(#linearGradient5855);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.415;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c53a3a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect5884"
+ width="48.834862"
+ height="226.22897"
+ x="-34.74221"
+ y="446.17056"
+ transform="rotate(-30)" />
+ <path
+ transform="translate(0,-308.26772)"
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.509;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="path3428"
+ d="m 251.98568,878.63831 -14.02447,24.29109 h -28.04894 l -14.02447,-24.29109 14.02447,-24.2911 h 28.04894 z" />
+ <use
+ x="0"
+ y="0"
+ xlink:href="#rect5884"
+ id="use4252"
+ transform="rotate(60,268.29786,489.4515)"
+ width="100%"
+ height="100%" />
+ <rect
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.650794;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ id="rect4254"
+ width="5.3947482"
+ height="115.12564"
+ x="545.71014"
+ y="467.07007"
+ transform="rotate(30,575.23539,-154.13386)" />
+ </g>
+ </g>
+ <g
+ id="layer3"
+ style="display:inline;opacity:1"
+ transform="translate(-156.33871,933.1905)">
+ <path
+ id="path3336-6"
+ d="m 309.54892,-710.38827 c 40.73228,70.55837 81.46455,141.11675 122.19683,211.67512 -18.71902,0.1756 -37.43804,0.3512 -56.15706,0.5268 -10.87453,-18.9564 -21.74907,-37.9128 -32.6236,-56.8692 -10.95215,18.8551 -21.9043,37.7102 -32.85645,56.5653 -9.30079,-0.004 -18.60158,-0.007 -27.90237,-0.011 -4.76362,-8.22987 -9.52724,-16.45973 -14.29086,-24.6896 15.60349,-26.83003 31.20698,-53.66007 46.81047,-80.4901 -11.07649,-19.27523 -22.15297,-38.55047 -33.22946,-57.8257 9.35083,-16.29387 18.70167,-32.58775 28.0525,-48.88162 z"
+ style="opacity:1;fill:url(#linearGradient4328);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <use
+ height="100%"
+ width="100%"
+ transform="rotate(60,407.11155,-715.78724)"
+ id="use3439-6"
+ xlink:href="#path3336-6"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="rotate(-60,407.31177,-715.70016)"
+ id="use3445-0"
+ xlink:href="#path3336-6"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="rotate(180,407.41868,-715.7565)"
+ id="use3449-5"
+ xlink:href="#path3336-6"
+ y="0"
+ x="0" />
+ <path
+ style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4330);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 309.54892,-710.38827 c 40.73228,70.55837 81.46455,141.11675 122.19683,211.67512 -18.71902,0.1756 -37.43804,0.3512 -56.15706,0.5268 -10.87453,-18.9564 -21.74907,-37.9128 -32.6236,-56.8692 -10.95215,18.8551 -21.9043,37.7102 -32.85645,56.5653 -10.54113,-2.26829 -26.58606,6.01638 -31.9377,-7.5219 -4.39393,-6.91787 -12.57856,-15.53043 -6.85074,-23.97221 8.26178,-12.05394 14.90093,-25.28023 22.52611,-37.79439 6.95986,-11.9674 13.91971,-23.9348 20.87957,-35.9022 -11.07649,-19.2752 -22.15297,-38.5504 -33.22946,-57.8256 9.35083,-16.29391 18.70167,-32.58781 28.0525,-48.88172 z"
+ id="path4260-0" />
+ <use
+ height="100%"
+ width="100%"
+ transform="rotate(120,407.33916,-716.08356)"
+ id="use4354-5"
+ xlink:href="#path4260-0"
+ y="0"
+ x="0"
+ style="display:inline" />
+ <use
+ height="100%"
+ width="100%"
+ transform="rotate(-120,407.28823,-715.86995)"
+ id="use4362-2"
+ xlink:href="#path4260-0"
+ y="0"
+ x="0"
+ style="display:inline" />
+ </g>
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/assets/osk.svg b/linux/home/.config/ags/assets/osk.svg
new file mode 100644
index 0000000..511420a
--- /dev/null
+++ b/linux/home/.config/ags/assets/osk.svg
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="24"
+ height="24"
+ version="1"
+ id="svg20"
+ sodipodi:docname="osk.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs20" />
+ <sodipodi:namedview
+ id="namedview20"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="26.166667"
+ inkscape:cx="12"
+ inkscape:cy="12"
+ inkscape:current-layer="svg20" />
+ <rect
+ style="fill:#4f4f4f;stroke-width:0.871697"
+ width="11.938859"
+ height="17.820761"
+ x="-18.006235"
+ y="-20.910379"
+ rx="0.85277563"
+ ry="0.891038"
+ transform="matrix(0,-1,-1,0,0,0)"
+ id="rect1" />
+ <path
+ style="opacity:0.2;stroke-width:0.871697"
+ d="m 9.3268859,13.31597 a 0.89103802,0.85277564 0 0 1 -0.891038,0.852775 0.89103802,0.85277564 0 0 1 -0.891038,-0.852775 0.89103802,0.85277564 0 0 1 0.891038,-0.852776 0.89103802,0.85277564 0 0 1 0.891038,0.852776 z"
+ id="path1" />
+ <path
+ style="opacity:0.2;stroke-width:0.871697"
+ d="M 12,13.31597 A 0.89103802,0.85277564 0 0 1 11.108962,14.168745 0.89103802,0.85277564 0 0 1 10.217924,13.31597 0.89103802,0.85277564 0 0 1 11.108962,12.463194 0.89103802,0.85277564 0 0 1 12,13.31597 Z"
+ id="path2" />
+ <path
+ style="opacity:0.2;stroke-width:0.871697"
+ d="m 14.673114,13.31597 a 0.89103802,0.85277564 0 0 1 -0.891038,0.852775 0.89103802,0.85277564 0 0 1 -0.891038,-0.852775 0.89103802,0.85277564 0 0 1 0.891038,-0.852776 0.89103802,0.85277564 0 0 1 0.891038,0.852776 z"
+ id="path3" />
+ <path
+ style="opacity:0.2;stroke-width:0.871697"
+ d="M 17.346228,13.31597 A 0.89103802,0.85277564 0 0 1 16.45519,14.168745 0.89103802,0.85277564 0 0 1 15.564152,13.31597 0.89103802,0.85277564 0 0 1 16.45519,12.463194 0.89103802,0.85277564 0 0 1 17.346228,13.31597 Z"
+ id="path4" />
+ <rect
+ style="opacity:0.2;stroke-width:0.871697"
+ width="12.474532"
+ height="1.7055513"
+ x="5.7627339"
+ y="15.874296"
+ rx="0.41611475"
+ ry="0.42638782"
+ id="rect4" />
+ <rect
+ style="fill:#e4e4e4;stroke-width:0.871697"
+ width="12.474532"
+ height="1.7055513"
+ x="5.7627339"
+ y="15.447908"
+ rx="0.41611475"
+ ry="0.42638782"
+ id="rect5" />
+ <path
+ style="opacity:0.2;stroke-width:0.871697"
+ d="M 15.564152,9.904867 A 0.89103802,0.85277564 0 0 1 14.673114,10.757643 0.89103802,0.85277564 0 0 1 13.782076,9.904867 0.89103802,0.85277564 0 0 1 14.673114,9.0520913 0.89103802,0.85277564 0 0 1 15.564152,9.904867 Z"
+ id="path5" />
+ <path
+ style="opacity:0.2;stroke-width:0.871697"
+ d="M 7.5448099,9.904867 A 0.89103802,0.85277564 0 0 1 6.6537719,10.757643 0.89103802,0.85277564 0 0 1 5.7627339,9.904867 0.89103802,0.85277564 0 0 1 6.6537719,9.0520913 0.89103802,0.85277564 0 0 1 7.5448099,9.904867 Z"
+ id="path6" />
+ <path
+ style="opacity:0.2;stroke-width:0.871697"
+ d="M 10.217924,9.904867 A 0.89103802,0.85277564 0 0 1 9.3268859,10.757643 0.89103802,0.85277564 0 0 1 8.4358479,9.904867 0.89103802,0.85277564 0 0 1 9.3268859,9.0520913 0.89103802,0.85277564 0 0 1 10.217924,9.904867 Z"
+ id="path7" />
+ <path
+ style="opacity:0.2;stroke-width:0.871697"
+ d="M 12.891038,9.904867 A 0.89103802,0.85277564 0 0 1 12,10.757643 0.89103802,0.85277564 0 0 1 11.108962,9.904867 0.89103802,0.85277564 0 0 1 12,9.0520913 0.89103802,0.85277564 0 0 1 12.891038,9.904867 Z"
+ id="path8" />
+ <path
+ style="opacity:0.2;stroke-width:0.871697"
+ d="M 18.237266,9.904867 A 0.89103802,0.85277564 0 0 1 17.346228,10.757643 0.89103802,0.85277564 0 0 1 16.45519,9.904867 0.89103802,0.85277564 0 0 1 17.346228,9.0520913 0.89103802,0.85277564 0 0 1 18.237266,9.904867 Z"
+ id="path9" />
+ <g
+ style="fill:#e4e4e4"
+ id="g18"
+ transform="matrix(0.89103802,0,0,0.85277564,1.3075438,1.8034984)">
+ <path
+ d="M 9,13 A 1,1 0 0 1 8,14 1,1 0 0 1 7,13 1,1 0 0 1 8,12 1,1 0 0 1 9,13 Z"
+ id="path10" />
+ <path
+ d="m 12,13 a 1,1 0 0 1 -1,1 1,1 0 0 1 -1,-1 1,1 0 0 1 1,-1 1,1 0 0 1 1,1 z"
+ id="path11" />
+ <path
+ d="m 15,13 a 1,1 0 0 1 -1,1 1,1 0 0 1 -1,-1 1,1 0 0 1 1,-1 1,1 0 0 1 1,1 z"
+ id="path12" />
+ <path
+ d="m 18,13 a 1,1 0 0 1 -1,1 1,1 0 0 1 -1,-1 1,1 0 0 1 1,-1 1,1 0 0 1 1,1 z"
+ id="path13" />
+ <path
+ d="m 16,9 a 1,1 0 0 1 -1,1 1,1 0 0 1 -1,-1 1,1 0 0 1 1,-1 1,1 0 0 1 1,1 z"
+ id="path14" />
+ <path
+ d="M 7,9 A 1,1 0 0 1 6,10 1,1 0 0 1 5,9 1,1 0 0 1 6,8 1,1 0 0 1 7,9 Z"
+ id="path15" />
+ <path
+ d="M 10,9 A 1,1 0 0 1 9,10 1,1 0 0 1 8,9 1,1 0 0 1 9,8 1,1 0 0 1 10,9 Z"
+ id="path16" />
+ <path
+ d="m 13,9 a 1,1 0 0 1 -1,1 1,1 0 0 1 -1,-1 1,1 0 0 1 1,-1 1,1 0 0 1 1,1 z"
+ id="path17" />
+ <path
+ d="m 19,9 a 1,1 0 0 1 -1,1 1,1 0 0 1 -1,-1 1,1 0 0 1 1,-1 1,1 0 0 1 1,1 z"
+ id="path18" />
+ </g>
+ <path
+ style="opacity:0.1;fill:#ffffff;stroke-width:0.871697"
+ d="m 3.9806578,6.0673766 c -0.493635,0 -0.891038,0.3803379 -0.891038,0.8527756 v 0.4263879 c 0,-0.4724377 0.397403,-0.8527757 0.891038,-0.8527757 H 20.019342 c 0.493635,0 0.891038,0.380338 0.891038,0.8527757 V 6.9201522 c 0,-0.4724377 -0.397403,-0.8527756 -0.891038,-0.8527756 z"
+ id="path19" />
+ <path
+ style="opacity:0.2;stroke-width:0.871697"
+ d="m 3.0896198,17.15346 v 0.426388 c 0,0.472437 0.397403,0.852775 0.891038,0.852775 H 20.019342 c 0.493635,0 0.891038,-0.380338 0.891038,-0.852775 V 17.15346 c 0,0.472438 -0.397403,0.852776 -0.891038,0.852776 H 3.9806578 c -0.493635,0 -0.891038,-0.380338 -0.891038,-0.852776 z"
+ id="path20" />
+</svg>
diff --git a/linux/home/.config/ags/assets/pinned.svg b/linux/home/.config/ags/assets/pinned.svg
new file mode 100644
index 0000000..ce8efb3
--- /dev/null
+++ b/linux/home/.config/ags/assets/pinned.svg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ viewBox="0 0 22 22"
+ version="1.1"
+ id="svg1"
+ sodipodi:docname="pinned.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview1"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="21.454545"
+ inkscape:cx="11"
+ inkscape:cy="11"
+ inkscape:current-layer="svg1" />
+ <defs
+ id="defs3051">
+ <style
+ type="text/css"
+ id="current-color-scheme">.ColorScheme-Text { color: #fcfcfc; } </style>
+ </defs>
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:0.906415"
+ d="m 11,3.7453986 c -4.015418,0 -7.2480469,3.2355522 -7.2480469,7.2546014 0,4.019049 3.2326289,7.254601 7.2480469,7.254601 4.015418,0 7.248047,-3.235552 7.248047,-7.254601 0,-4.0190492 -3.232629,-7.2546014 -7.248047,-7.2546014 z m 0,0.9068251 c 3.513491,0 6.342041,2.8311083 6.342041,6.3477763 0,3.516668 -2.82855,6.347776 -6.342041,6.347776 C 7.4865093,17.347776 4.657959,14.516668 4.657959,11 4.657959,7.483332 7.4865093,4.6522237 11,4.6522237 Z m 0,4.5341259 c -1.0038533,0 -1.8120117,0.8088893 -1.8120117,1.8136504 0,1.004761 0.8081584,1.81365 1.8120117,1.81365 1.003854,0 1.812012,-0.808889 1.812012,-1.81365 0,-1.0047611 -0.808158,-1.8136504 -1.812012,-1.8136504 z m 0,0.9068254 c 0.501926,0 0.906006,0.404445 0.906006,0.906825 0,0.50238 -0.40408,0.906825 -0.906006,0.906825 -0.501926,0 -0.906006,-0.404445 -0.906006,-0.906825 0,-0.50238 0.40408,-0.906825 0.906006,-0.906825 z"
+ class="ColorScheme-Text"
+ id="path1" />
+</svg>
diff --git a/linux/home/.config/ags/assets/preferences-desktop-theme-symbolic.svg b/linux/home/.config/ags/assets/preferences-desktop-theme-symbolic.svg
new file mode 100644
index 0000000..4461454
--- /dev/null
+++ b/linux/home/.config/ags/assets/preferences-desktop-theme-symbolic.svg
@@ -0,0 +1,321 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="16px"
+ viewBox="0 0 16 16"
+ width="16px"
+ version="1.1"
+ id="svg3533"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs3537" />
+ <filter
+ id="a"
+ height="1"
+ width="1"
+ x="0"
+ y="0">
+ <feColorMatrix
+ in="SourceGraphic"
+ type="matrix"
+ values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"
+ id="feColorMatrix3414" />
+ </filter>
+ <mask
+ id="b">
+ <g
+ filter="url(#a)"
+ id="g3419">
+ <image
+ height="800"
+ width="1024"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAMgCAYAAAC9FhA7AAAABmJLR0QA/wD/AP+gvaeTAAAMr0lEQVR4nO3YsQ2AMAwAwcB8GZcBWQFEIoq/qy3b9Y8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQdi/bMl/PXorsAAADAA+ffDwAAAAD7CQAAAAAQIAAAAABAgAAAAAAAAQIAAAAABAgAAAAAECAAAAAAQIAAAAAAAAECAAAAAAQIAAAAABAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8dQMXagEYjYWofQAAAABJRU5ErkJggg=="
+ id="image3417" />
+ </g>
+ </mask>
+ <clipPath
+ id="c">
+ <path
+ d="m 0 0 h 1024 v 800 h -1024 z"
+ id="path3422" />
+ </clipPath>
+ <mask
+ id="d">
+ <g
+ filter="url(#a)"
+ id="g3427">
+ <image
+ height="800"
+ width="1024"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAMgCAYAAAC9FhA7AAAABmJLR0QA/wD/AP+gvaeTAAAMr0lEQVR4nO3YsQ2AMAwAwUCd/SdlAFYAkYji72rLdv1jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBx6I98+X8teguAAAA8MD59wMAAADAfgIAAAAABAgAAAAAECAAAAAAQIAAAAAAAAECAAAAAAQIAAAAABAgAAAAAECAAAAAAAABAgAAAAAECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX90LagEYFVKkEwAAAABJRU5ErkJggg=="
+ id="image3425" />
+ </g>
+ </mask>
+ <clipPath
+ id="e">
+ <path
+ d="m 0 0 h 1024 v 800 h -1024 z"
+ id="path3430" />
+ </clipPath>
+ <mask
+ id="f">
+ <g
+ filter="url(#a)"
+ id="g3435">
+ <image
+ height="800"
+ width="1024"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAMgCAYAAAC9FhA7AAAABmJLR0QA/wD/AP+gvaeTAAAMr0lEQVR4nO3YsQ2AMAwAwUCd/SdlAFYAkYji72rLdv1jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBx6I98+X8teguAAAA8MD59wMAAADAfgIAAAAABAgAAAAAECAAAAAAQIAAAAAAAAECAAAAAAQIAAAAABAgAAAAAECAAAAAAAABAgAAAAAECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX90LagEYFVKkEwAAAABJRU5ErkJggg=="
+ id="image3433" />
+ </g>
+ </mask>
+ <clipPath
+ id="g">
+ <path
+ d="m 0 0 h 1024 v 800 h -1024 z"
+ id="path3438" />
+ </clipPath>
+ <mask
+ id="h">
+ <g
+ filter="url(#a)"
+ id="g3443">
+ <image
+ height="800"
+ width="1024"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAMgCAYAAAC9FhA7AAAABmJLR0QA/wD/AP+gvaeTAAAMr0lEQVR4nO3YsQ2AMAwAwUCd/SdlAFYAkYji72rLdv1jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBx6I98+X8teguAAAA8MD59wMAAADAfgIAAAAABAgAAAAAECAAAAAAQIAAAAAAAAECAAAAAAQIAAAAABAgAAAAAECAAAAAAAABAgAAAAAECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX90LagEYFVKkEwAAAABJRU5ErkJggg=="
+ id="image3441" />
+ </g>
+ </mask>
+ <clipPath
+ id="i">
+ <path
+ d="m 0 0 h 1024 v 800 h -1024 z"
+ id="path3446" />
+ </clipPath>
+ <mask
+ id="j">
+ <g
+ filter="url(#a)"
+ id="g3451">
+ <image
+ height="800"
+ width="1024"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAMgCAYAAAC9FhA7AAAABmJLR0QA/wD/AP+gvaeTAAAMr0lEQVR4nO3YsQ2AMAwAwUCd/SdlAFYAkYji72rLdv1jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBx6I98+X8teguAAAA8MD59wMAAADAfgIAAAAABAgAAAAAECAAAAAAQIAAAAAAAAECAAAAAAQIAAAAABAgAAAAAECAAAAAAAABAgAAAAAECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX90LagEYFVKkEwAAAABJRU5ErkJggg=="
+ id="image3449" />
+ </g>
+ </mask>
+ <clipPath
+ id="k">
+ <path
+ d="m 0 0 h 1024 v 800 h -1024 z"
+ id="path3454" />
+ </clipPath>
+ <mask
+ id="l">
+ <g
+ filter="url(#a)"
+ id="g3459">
+ <image
+ height="800"
+ width="1024"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAMgCAYAAAC9FhA7AAAABmJLR0QA/wD/AP+gvaeTAAAMr0lEQVR4nO3YsQ2AMAwAwUCd/SdlAFYAkYji72rLdv1jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBx6I98+X8teguAAAA8MD59wMAAADAfgIAAAAABAgAAAAAECAAAAAAQIAAAAAAAAECAAAAAAQIAAAAABAgAAAAAECAAAAAAAABAgAAAAAECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX90LagEYFVKkEwAAAABJRU5ErkJggg=="
+ id="image3457" />
+ </g>
+ </mask>
+ <clipPath
+ id="m">
+ <path
+ d="m 0 0 h 1024 v 800 h -1024 z"
+ id="path3462" />
+ </clipPath>
+ <mask
+ id="n">
+ <g
+ filter="url(#a)"
+ id="g3467">
+ <image
+ height="800"
+ width="1024"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAMgCAYAAAC9FhA7AAAABmJLR0QA/wD/AP+gvaeTAAAMr0lEQVR4nO3YsQ2AMAwAwUCd/SdlAFYAkYji72rLdv1jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBx6I98+X8teguAAAA8MD59wMAAADAfgIAAAAABAgAAAAAECAAAAAAQIAAAAAAAAECAAAAAAQIAAAAABAgAAAAAECAAAAAAAABAgAAAAAECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX90LagEYFVKkEwAAAABJRU5ErkJggg=="
+ id="image3465" />
+ </g>
+ </mask>
+ <clipPath
+ id="o">
+ <path
+ d="m 0 0 h 1024 v 800 h -1024 z"
+ id="path3470" />
+ </clipPath>
+ <mask
+ id="p">
+ <g
+ filter="url(#a)"
+ id="g3475">
+ <image
+ height="800"
+ width="1024"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAMgCAYAAAC9FhA7AAAABmJLR0QA/wD/AP+gvaeTAAAMr0lEQVR4nO3YsQ2AMAwAwcB8GZcBWQFEIoq/qy3b9Y8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQdi/bMl/PXorsAAADAA+ffDwAAAAD7CQAAAAAQIAAAAABAgAAAAAAAAQIAAAAABAgAAAAAECAAAAAAQIAAAAAAAAECAAAAAAQIAAAAABAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8dQMXagEYjYWofQAAAABJRU5ErkJggg=="
+ id="image3473" />
+ </g>
+ </mask>
+ <clipPath
+ id="q">
+ <path
+ d="m 0 0 h 1024 v 800 h -1024 z"
+ id="path3478" />
+ </clipPath>
+ <mask
+ id="r">
+ <g
+ filter="url(#a)"
+ id="g3483">
+ <image
+ height="800"
+ width="1024"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAMgCAYAAAC9FhA7AAAABmJLR0QA/wD/AP+gvaeTAAAMqUlEQVR4nO3YsQ3AMAwEMSeTe/QM4QgujqwF4etbCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIen76s4fvAQAAgAPv7QEAAADAPAEAAAAAAgQAAAAACBAAAAAAIEAAAAAAgAABAAAAAAIEAAAAAAgQAAAAACBAAAAAAIAAAQAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAUx8hKgEYqUBSPgAAAABJRU5ErkJggg=="
+ id="image3481" />
+ </g>
+ </mask>
+ <clipPath
+ id="s">
+ <path
+ d="m 0 0 h 1024 v 800 h -1024 z"
+ id="path3486" />
+ </clipPath>
+ <path
+ d="m 3 1 c -1.644531 0 -3 1.355469 -3 3 v 6 c 0 1.644531 1.355469 3 3 3 h 10 c 1.644531 0 3 -1.355469 3 -3 v -6 c 0 -0.570312 -0.167969 -1.101562 -0.449219 -1.558594 l -1.550781 1.554688 v 6.003906 c 0 0.570312 -0.429688 1 -1 1 h -10 c -0.570312 0 -1 -0.429688 -1 -1 v -6 c 0 -0.570312 0.429688 -1 1 -1 h 5.96875 l 2.007812 -2 z m 0 0"
+ fill="#2e3436"
+ id="path3489"
+ style="fill:#000000" />
+ <g
+ clip-path="url(#c)"
+ mask="url(#b)"
+ transform="matrix(1 0 0 1 -40 -620)"
+ id="g3493"
+ style="fill:#000000">
+ <path
+ d="m 439.105469 225.78125 h 7.839843 c -0.890624 0.371094 -0.972656 1.847656 0 2.25 h -7.839843 z m 0 0"
+ fill="#2e3436"
+ id="path3491"
+ style="fill:#000000" />
+ </g>
+ <g
+ clip-path="url(#e)"
+ mask="url(#d)"
+ transform="matrix(1 0 0 1 -40 -620)"
+ id="g3497"
+ style="fill:#000000">
+ <path
+ d="m 29.25 627.75 h 0.75 v 0.75 h -0.75 z m 0 0"
+ fill="#2e3436"
+ fill-rule="evenodd"
+ id="path3495"
+ style="fill:#000000" />
+ </g>
+ <g
+ clip-path="url(#g)"
+ mask="url(#f)"
+ transform="matrix(1 0 0 1 -40 -620)"
+ id="g3501"
+ style="fill:#000000">
+ <path
+ d="m 30 627 h 0.75 v 0.75 h -0.75 z m 0 0"
+ fill="#2e3436"
+ fill-rule="evenodd"
+ id="path3499"
+ style="fill:#000000" />
+ </g>
+ <g
+ clip-path="url(#i)"
+ mask="url(#h)"
+ transform="matrix(1 0 0 1 -40 -620)"
+ id="g3505"
+ style="fill:#000000">
+ <path
+ d="m 30.75 629.25 h 0.75 v 0.75 h -0.75 z m 0 0"
+ fill="#2e3436"
+ fill-rule="evenodd"
+ id="path3503"
+ style="fill:#000000" />
+ </g>
+ <g
+ clip-path="url(#k)"
+ mask="url(#j)"
+ transform="matrix(1 0 0 1 -40 -620)"
+ id="g3509"
+ style="fill:#000000">
+ <path
+ d="m 29.25 629.25 h 0.75 v 0.75 h -0.75 z m 0 0"
+ fill="#2e3436"
+ fill-rule="evenodd"
+ id="path3507"
+ style="fill:#000000" />
+ </g>
+ <g
+ clip-path="url(#m)"
+ mask="url(#l)"
+ transform="matrix(1 0 0 1 -40 -620)"
+ id="g3513"
+ style="fill:#000000">
+ <path
+ d="m 30 630 h 0.75 v 0.75 h -0.75 z m 0 0"
+ fill="#2e3436"
+ fill-rule="evenodd"
+ id="path3511"
+ style="fill:#000000" />
+ </g>
+ <g
+ clip-path="url(#o)"
+ mask="url(#n)"
+ transform="matrix(1 0 0 1 -40 -620)"
+ id="g3517"
+ style="fill:#000000">
+ <path
+ d="m 31.5 630 h 0.75 v 0.75 h -0.75 z m 0 0"
+ fill="#2e3436"
+ fill-rule="evenodd"
+ id="path3515"
+ style="fill:#000000" />
+ </g>
+ <g
+ clip-path="url(#q)"
+ mask="url(#p)"
+ transform="matrix(1 0 0 1 -40 -620)"
+ id="g3521"
+ style="fill:#000000">
+ <path
+ d="m 119.253906 648.75 v 5.25 h 5.25 v -5.25 z m 0 0"
+ fill="#2e3436"
+ id="path3519"
+ style="fill:#000000" />
+ </g>
+ <path
+ d="m 11 7 c 0 1.65625 -1.339844 3.007812 -3 3 h -3 v -3 c 0 -1.660156 1.34375 -3 3 -3 c 1.660156 0 3 1.339844 3 3 z m 0 0"
+ fill="#2e3436"
+ id="path3523"
+ style="fill:#000000" />
+ <path
+ d="m 13.398438 0 l -3.46875 3.457031 c 0.683593 0.355469 1.234374 0.910157 1.589843 1.589844 l 0.171875 -0.171875 l 0.007813 0.007812 l 4.300781 -4.300781 v -0.582031 z m 0 0"
+ fill="#2e3436"
+ id="path3525"
+ style="fill:#000000" />
+ <g
+ clip-path="url(#s)"
+ mask="url(#r)"
+ transform="matrix(1 0 0 1 -40 -620)"
+ id="g3529"
+ style="fill:#000000">
+ <path
+ d="m 181.503906 635.25 h 2.25 v 9 h -2.25 z m 0 0"
+ fill="#2e3436"
+ id="path3527"
+ style="fill:#000000" />
+ </g>
+ <path
+ d="m 5 14 c -1.105469 0 -2 0.894531 -2 2 h 10 c 0 -1.105469 -0.894531 -2 -2 -2 z m 0 0"
+ fill="#2e3436"
+ id="path3531"
+ style="fill:#000000" />
+</svg>
diff --git a/linux/home/.config/ags/assets/processor-symbolic.svg b/linux/home/.config/ags/assets/processor-symbolic.svg
new file mode 100644
index 0000000..832dbaf
--- /dev/null
+++ b/linux/home/.config/ags/assets/processor-symbolic.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 5 5 h 6 v 6 h -6 z m 0 0"/>
+ <path d="m 13 5 h 3 v 1 h -3 z m 0 0"/>
+ <path d="m 13 7 h 3 v 1 h -3 z m 0 0"/>
+ <path d="m 13 9 h 3 v 1 h -3 z m 0 0"/>
+ <path d="m 0 6 h 3 v 1 h -3 z m 0 0"/>
+ <path d="m 0 8 h 3 v 1 h -3 z m 0 0"/>
+ <path d="m 0 10 h 3 v 1 h -3 z m 0 0"/>
+ <path d="m 5 0 h 1 v 3 h -1 z m 0 0"/>
+ <path d="m 7 0 h 1 v 3 h -1 z m 0 0"/>
+ <path d="m 9 0 h 1 v 3 h -1 z m 0 0"/>
+ <path d="m 10 13 h 1 v 3 h -1 z m 0 0"/>
+ <path d="m 8 13 h 1 v 3 h -1 z m 0 0"/>
+ <path d="m 6 13 h 1 v 3 h -1 z m 0 0"/>
+ <path d="m 5 2 c -1.644531 0 -3 1.355469 -3 3 v 6 c 0 1.644531 1.355469 3 3 3 h 6 c 1.644531 0 3 -1.355469 3 -3 v -6 c 0 -1.644531 -1.355469 -3 -3 -3 z m 0 2 h 6 c 0.570312 0 1 0.429688 1 1 v 6 c 0 0.570312 -0.429688 1 -1 1 h -6 c -0.570312 0 -1 -0.429688 -1 -1 v -6 c 0 -0.570312 0.429688 -1 1 -1 z m 0 0"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/rotation.svg b/linux/home/.config/ags/assets/rotation.svg
new file mode 100644
index 0000000..7ed7e34
--- /dev/null
+++ b/linux/home/.config/ags/assets/rotation.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <defs>
+ <style id="current-color-scheme" type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style>
+ </defs>
+ <path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 10 L 7 10 L 7 5 L 9 5 C 10.108 5 11 5.89199 11 7 L 11 9 L 8 9 L 8 11 L 6 11 L 6 15 L 15 15 L 15 9 L 12 9 L 12 7 C 12 5.338 10.662 4 9 4 L 7 4 L 7 1 L 1 1 z" transform="translate(4 4)"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/swapnext.svg b/linux/home/.config/ags/assets/swapnext.svg
new file mode 100644
index 0000000..0690cb9
--- /dev/null
+++ b/linux/home/.config/ags/assets/swapnext.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <defs>
+ <style id="current-color-scheme" type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style>
+ </defs>
+ <path style="fill:currentColor" class="ColorScheme-Text" d="M 3 1 C 1.892 1 1 1.892 1 3 L 1 13 C 1 14.108 1.892 15 3 15 L 13 15 C 14.108 15 15 14.108 15 13 L 15 3 C 15 1.892 14.108 1 13 1 L 3 1 z M 3 3 L 13 3 L 13 13 L 3 13 L 3 3 z M 5 4 A 1 1 0 0 0 4 5 A 1 1 0 0 0 5 6 A 1 1 0 0 0 6 5 A 1 1 0 0 0 5 4 z M 11 4 A 1 1 0 0 0 10 5 A 1 1 0 0 0 11 6 A 1 1 0 0 0 12 5 A 1 1 0 0 0 11 4 z M 8 7 A 1 1 0 0 0 7 8 A 1 1 0 0 0 8 9 A 1 1 0 0 0 9 8 A 1 1 0 0 0 8 7 z M 5 10 A 1 1 0 0 0 4 11 A 1 1 0 0 0 5 12 A 1 1 0 0 0 6 11 A 1 1 0 0 0 5 10 z M 11 10 A 1 1 0 0 0 10 11 A 1 1 0 0 0 11 12 A 1 1 0 0 0 12 11 A 1 1 0 0 0 11 10 z" transform="translate(4 4)"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/tbox-close.svg b/linux/home/.config/ags/assets/tbox-close.svg
new file mode 100644
index 0000000..f325d3c
--- /dev/null
+++ b/linux/home/.config/ags/assets/tbox-close.svg
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="24"
+ height="24"
+ version="1.1"
+ id="svg1"
+ sodipodi:docname="tbox-close.svg"
+ xml:space="preserve"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
+ id="namedview1"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="26.708333"
+ inkscape:cx="11.981279"
+ inkscape:cy="11.981279"
+ inkscape:current-layer="svg1" /><defs
+ id="defs1"><style
+ id="current-color-scheme"
+ type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style></defs><g
+ id="g2"
+ transform="matrix(0.43167414,0,0,0.42254065,1.6211,1.3192164)"><path
+ style="opacity:0.2"
+ d="m 34.158694,7.001891 c -2.339941,-0.051114 -4.593769,0.9363666 -6.144072,2.6910582 -0.0012,0.0014 -0.0027,0.0025 -0.0039,0.0039 l -4.015616,4.4240828 -4.01174,-4.4162958 c -1.424465,-1.6183716 -3.450682,-2.592914 -5.603207,-2.6949526 -0.260519,-0.012616 -0.51772,-0.013001 -0.778224,0 a 5.977352,5.9824591 0 0 0 -0.01945,0 C 7.2433432,7.3455998 3.8467314,15.780704 8.1816032,20.422133 L 13.224489,25.975605 8.2594255,31.4434 c -1.5192901,1.580887 -2.4400962,4.078962 -2.2296097,6.266155 0.2104868,2.187194 1.2500876,3.924619 2.6187213,5.167923 1.3686339,1.243304 3.1976469,2.112373 5.3930859,2.110788 2.19544,-0.0016 4.590268,-1.161185 6.015666,-2.827365 l 3.937809,-4.330618 4.054544,4.463029 -0.147863,-0.167461 c 1.418645,1.683037 3.819828,2.866353 6.02734,2.874097 2.20751,0.0078 4.047562,-0.863731 5.420324,-2.110787 1.372761,-1.247055 2.417086,-2.995369 2.622612,-5.195185 0.205525,-2.199813 -0.739595,-4.705945 -2.276302,-6.281731 l -4.933937,-5.43664 5.050669,-5.561261 c 2.191468,-2.3538 2.626913,-5.893796 1.513644,-8.536614 C 40.212861,9.2349127 37.376326,7.0748944 34.162585,7.001891 a 5.977352,5.9824591 0 0 0 -0.0039,0 z"
+ id="path1-3" /><path
+ style="fill:#4f4f4f"
+ d="m 34.158694,6.0018906 c -2.339941,-0.051114 -4.593769,0.9363666 -6.144072,2.6910586 -0.0012,0.00139 -0.0027,0.0025 -0.0039,0.0039 L 23.995106,13.120932 19.983366,8.7046361 C 18.558901,7.0862642 16.532684,6.1117218 14.380159,6.0096832 c -0.260519,-0.012616 -0.51772,-0.013001 -0.778224,0 a 5.977352,5.9824591 0 0 0 -0.01945,0 C 7.2433432,6.3455994 3.8467314,14.780704 8.1816032,19.422133 L 13.224489,24.975605 8.2594255,30.4434 c -1.5192901,1.580887 -2.4400962,4.078962 -2.2296097,6.266155 0.2104868,2.187194 1.2500876,3.924619 2.6187213,5.167923 1.3686339,1.243304 3.1976469,2.112373 5.3930859,2.110788 2.19544,-0.0016 4.590268,-1.161185 6.015666,-2.827365 l 3.937809,-4.330618 4.054544,4.463029 -0.147863,-0.167461 c 1.418645,1.683037 3.819828,2.866353 6.02734,2.874097 2.20751,0.0078 4.047562,-0.863731 5.420324,-2.110787 1.372761,-1.247055 2.417086,-2.995369 2.622612,-5.195185 0.205525,-2.199813 -0.739595,-4.705945 -2.276302,-6.281731 l -4.933937,-5.43664 5.050669,-5.561261 c 2.191468,-2.3538 2.626913,-5.893796 1.513644,-8.536614 C 40.212861,8.2349123 37.376326,6.074894 34.162585,6.0018906 a 5.977352,5.9824591 0 0 0 -0.0039,0 z"
+ id="path2" /><path
+ style="opacity:0.2"
+ d="m 13.875,12.980111 a 2.0002,2.0002 0 0 0 -1.355469,3.363282 l 8.789063,9.667968 -8.769532,9.644532 A 2.0002,2.0002 0 1 0 15.5,38.343393 l 8.507812,-9.359374 8.51172,9.359374 a 2.0006762,2.0006762 0 1 0 2.960936,-2.6875 L 26.710938,26.011361 35.5,16.343393 a 2.0002,2.0002 0 0 0 -1.417968,-3.363282 2.0002,2.0002 0 0 0 -1.54297,0.675782 l -8.53125,9.382812 -8.527343,-9.382812 a 2.0002,2.0002 0 0 0 -1.40625,-0.675782 2.0002,2.0002 0 0 0 -0.199219,0 z"
+ id="path3" /><path
+ style="fill:#ffffff"
+ transform="scale(2)"
+ d="M 6.9375,5.9902344 A 1.0001,1.0001 0 0 0 6.2597656,7.671875 l 4.3945314,4.833984 -4.3847658,4.822266 A 1.0001,1.0001 0 1 0 7.75,18.671875 l 4.253906,-4.679687 4.25586,4.679687 a 1.0003381,1.0003381 0 1 0 1.480468,-1.34375 L 13.355469,12.505859 17.75,7.671875 A 1.0001,1.0001 0 0 0 17.041016,5.9902344 1.0001,1.0001 0 0 0 16.269531,6.328125 L 12.003906,11.019531 7.7402344,6.328125 a 1.0001,1.0001 0 0 0 -0.703125,-0.3378906 1.0001,1.0001 0 0 0 -0.099609,0 z"
+ id="path4" /><path
+ style="opacity:0.2;fill:#ffffff"
+ transform="scale(2)"
+ d="m 17.080078,3 c -1.16997,-0.025557 -2.297114,0.4683571 -3.072266,1.3457031 0,0 -0.002,0.00195 -0.002,0.00195 L 11.998047,6.5605469 9.9921875,4.3515625 C 9.279955,3.5423765 8.2657156,3.0549255 7.1894531,3.0039062 c -0.1302595,-0.00631 -0.2584199,-0.0065 -0.3886719,0 a 2.988676,2.9912295 0 0 0 -0.00977,0 C 4.516087,3.1244563 3.008504,5.3315905 3.1347612,7.4257812 3.2542394,5.4946775 4.6972247,3.6148577 6.7910112,3.5039062 a 2.988676,2.9912295 0 0 1 0.00977,0 c 0.130252,-0.0065 0.2584124,-0.00631 0.3886719,0 1.0762625,0.051019 2.0905019,0.5384705 2.8027344,1.3476563 l 2.0058595,2.2089844 2.007812,-2.2128907 c 0,0 0.002,-0.00195 0.002,-0.00195 C 14.782964,3.9683573 15.910108,3.474443 17.080078,3.5 a 2.988676,2.9912295 0 0 0 0.002,0 c 1.606871,0.036502 3.023444,1.1180442 3.580078,2.4394531 0.16598,0.3940229 0.255721,0.8292143 0.28125,1.2753907 0.03853,-0.6193247 -0.05357,-1.2348847 -0.28125,-1.7753907 C 20.105475,4.1180443 18.688902,3.0365017 17.082031,3 a 2.988676,2.9912295 0 0 0 -0.002,0 z M 6.3847656,12.738281 4.1289062,15.222656 c -0.7462708,0.776527 -1.199139,1.994686 -1.1152343,3.074219 0.063557,-0.939898 0.4824865,-1.915817 1.1152343,-2.574219 l 2.4824219,-2.734375 z m 11.2226564,0 -0.226563,0.25 2.466797,2.71875 c 0.646214,0.662647 1.079159,1.652595 1.140625,2.605469 0.08979,-1.091494 -0.380318,-2.325827 -1.140625,-3.105469 z"
+ id="path5" /></g></svg>
diff --git a/linux/home/.config/ags/assets/terminal-symbolic.svg b/linux/home/.config/ags/assets/terminal-symbolic.svg
new file mode 100644
index 0000000..9f82bcf
--- /dev/null
+++ b/linux/home/.config/ags/assets/terminal-symbolic.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 2.199219 0 c -1.207031 0 -2.199219 1.007812 -2.199219 2.207031 v 10.585938 c 0 1.199219 0.992188 2.207031 2.199219 2.207031 h 11.601562 c 1.207031 0 2.199219 -1.007812 2.199219 -2.207031 v -10.585938 c 0 -1.199219 -0.992188 -2.207031 -2.199219 -2.207031 z m 0 2 h 11.601562 c 0.121094 0 0.199219 0.070312 0.199219 0.207031 v 10.585938 c 0 0.136719 -0.078125 0.207031 -0.199219 0.207031 h -11.601562 c -0.121094 0 -0.199219 -0.070312 -0.199219 -0.207031 v -10.585938 c 0 -0.136719 0.078125 -0.207031 0.199219 -0.207031 z m 0 0"/>
+ <path d="m 4.515625 5.898438 c -0.164063 -0.003907 -0.324219 0.0625 -0.441406 0.175781 c -0.230469 0.234375 -0.230469 0.617187 0 0.851562 l 1.578125 1.574219 l -1.578125 1.574219 c -0.230469 0.234375 -0.230469 0.617187 0 0.851562 c 0.234375 0.230469 0.617187 0.230469 0.851562 0 l 2 -2 c 0.230469 -0.234375 0.230469 -0.617187 0 -0.851562 l -2 -2 c -0.109375 -0.105469 -0.257812 -0.167969 -0.410156 -0.175781 z m 3.484375 4.101562 v 1 h 3 v -1 z m 0 0"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/togglesplit.svg b/linux/home/.config/ags/assets/togglesplit.svg
new file mode 100644
index 0000000..15d5011
--- /dev/null
+++ b/linux/home/.config/ags/assets/togglesplit.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" version="1.1">
+ <defs>
+ <style id="current-color-scheme" type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style>
+ </defs>
+ <g transform="translate(4,4)">
+ <path style="fill:currentColor" class="ColorScheme-Text" d="M 1,1 V 10 H 5 V 5 H 10 V 1 Z M 6,6 H 15 V 15 H 6 Z"/>
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/assets/toolbars-symbolic.svg b/linux/home/.config/ags/assets/toolbars-symbolic.svg
new file mode 100644
index 0000000..9f4c564
--- /dev/null
+++ b/linux/home/.config/ags/assets/toolbars-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <path d="m 2 0 c -1.214844 0 -2 0.828125 -2 2 v 12 c 0 1 1 2 2 2 h 11.984375 c 1 0 2 -1 2 -2 v -12 c 0 -1.238281 -0.828125 -2 -2 -2 z m 0 2 h 2 v 2 h -2 z m 3 0 h 2 v 2 h -2 z m 3 0 h 2 v 2 h -2 z m -6 4 h 11.984375 v 8 h -11.984375 z m 0 0"/>
+</svg>
diff --git a/linux/home/.config/ags/assets/wp-next.svg b/linux/home/.config/ags/assets/wp-next.svg
new file mode 100644
index 0000000..ac0245d
--- /dev/null
+++ b/linux/home/.config/ags/assets/wp-next.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" version="1.1">
+ <defs>
+ <style id="current-color-scheme" type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style>
+ </defs>
+ <g transform="translate(4,4)">
+ <path style="fill:currentColor" class="ColorScheme-Text" d="M 3,2 V 14 L 14,8 Z"/>
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/assets/wp-prev.svg b/linux/home/.config/ags/assets/wp-prev.svg
new file mode 100644
index 0000000..12ed8dd
--- /dev/null
+++ b/linux/home/.config/ags/assets/wp-prev.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" version="1.1">
+ <defs>
+ <style id="current-color-scheme" type="text/css">
+ .ColorScheme-Text { color:#dfdfdf; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
+ </style>
+ </defs>
+ <g transform="translate(4,4)">
+ <path style="fill:currentColor" class="ColorScheme-Text" d="M 13,2 2,8 13,14 Z"/>
+ </g>
+</svg>
diff --git a/linux/home/.config/ags/config.js b/linux/home/.config/ags/config.js
new file mode 100644
index 0000000..2864dec
--- /dev/null
+++ b/linux/home/.config/ags/config.js
@@ -0,0 +1,46 @@
+import GLib from "gi://GLib"
+
+const main = "/tmp/ags/main.js"
+const entry = `${App.configDir}/main.ts`
+const bundler = GLib.getenv("AGS_BUNDLER") || "bun"
+
+const v = {
+ ags: pkg.version?.split(".").map(Number) || [],
+ expect: [1, 8, 0],
+}
+
+try {
+ switch (bundler) {
+ case "bun": await Utils.execAsync([
+ "bun", "build", entry,
+ "--outfile", main,
+ "--external", "resource://*",
+ "--external", "gi://*",
+ "--external", "file://*",
+ ]); break
+
+ case "esbuild": await Utils.execAsync([
+ "esbuild", "--bundle", entry,
+ "--format=esm",
+ `--outfile=${main}`,
+ "--external:resource://*",
+ "--external:gi://*",
+ "--external:file://*",
+ ]); break
+
+ default:
+ throw `"${bundler}" is not a valid bundler`
+ }
+
+ if (v.ags[1] < v.expect[1] || v.ags[2] < v.expect[2]) {
+ print(`my config needs at least v${v.expect.join(".")}, yours is v${v.ags.join(".")}`)
+ App.quit()
+ }
+
+ await import(`file://${main}`)
+} catch (error) {
+ console.error(error)
+ App.quit()
+}
+
+export { }
diff --git a/linux/home/.config/ags/default.nix b/linux/home/.config/ags/default.nix
new file mode 100644
index 0000000..f0e0c41
--- /dev/null
+++ b/linux/home/.config/ags/default.nix
@@ -0,0 +1,104 @@
+{
+ inputs,
+ writeShellScript,
+ system,
+ stdenv,
+ cage,
+ swww,
+ esbuild,
+ dart-sass,
+ fd,
+ fzf,
+ brightnessctl,
+ accountsservice,
+ slurp,
+ wf-recorder,
+ wl-clipboard,
+ wayshot,
+ swappy,
+ hyprpicker,
+ pavucontrol,
+ networkmanager,
+ gtk3,
+ which,
+}: let
+ name = "asztal";
+
+ ags = inputs.ags.packages.${system}.default.override {
+ extraPackages = [accountsservice];
+ };
+
+ dependencies = [
+ which
+ dart-sass
+ fd
+ fzf
+ brightnessctl
+ swww
+ inputs.matugen.packages.${system}.default
+ inputs.hyprland.packages.${system}.default
+ slurp
+ wf-recorder
+ wl-clipboard
+ wayshot
+ swappy
+ hyprpicker
+ pavucontrol
+ networkmanager
+ gtk3
+ ];
+
+ addBins = list: builtins.concatStringsSep ":" (builtins.map (p: "${p}/bin") list);
+
+ greeter = writeShellScript "greeter" ''
+ export PATH=$PATH:${addBins dependencies}
+ ${cage}/bin/cage -ds -m last ${ags}/bin/ags -- -c ${config}/greeter.js
+ '';
+
+ desktop = writeShellScript name ''
+ export PATH=$PATH:${addBins dependencies}
+ ${ags}/bin/ags -b ${name} -c ${config}/config.js $@
+ '';
+
+ config = stdenv.mkDerivation {
+ inherit name;
+ src = ./.;
+
+ buildPhase = ''
+ ${esbuild}/bin/esbuild \
+ --bundle ./main.ts \
+ --outfile=main.js \
+ --format=esm \
+ --external:resource://\* \
+ --external:gi://\* \
+
+ ${esbuild}/bin/esbuild \
+ --bundle ./greeter/greeter.ts \
+ --outfile=greeter.js \
+ --format=esm \
+ --external:resource://\* \
+ --external:gi://\* \
+ '';
+
+ installPhase = ''
+ mkdir -p $out
+ cp -r assets $out
+ cp -r style $out
+ cp -r greeter $out
+ cp -r widget $out
+ cp -f main.js $out/config.js
+ cp -f greeter.js $out/greeter.js
+ '';
+ };
+in
+ stdenv.mkDerivation {
+ inherit name;
+ src = config;
+
+ installPhase = ''
+ mkdir -p $out/bin
+ cp -r . $out
+ cp ${desktop} $out/bin/${name}
+ cp ${greeter} $out/bin/greeter
+ '';
+ }
diff --git a/linux/home/.config/ags/greeter.js b/linux/home/.config/ags/greeter.js
new file mode 100644
index 0000000..5c8e369
--- /dev/null
+++ b/linux/home/.config/ags/greeter.js
@@ -0,0 +1,18 @@
+const main = "/tmp/ags/greeter.js"
+const entry = `${App.configDir}/greeter/greeter.ts`
+
+try {
+ await Utils.execAsync([
+ "bun", "build", entry,
+ "--outfile", main,
+ "--external", "resource://*",
+ "--external", "gi://*",
+ "--external", "file://*",
+ ])
+ await import(`file://${main}`)
+} catch (error) {
+ console.error(error)
+ App.quit()
+}
+
+export { }
diff --git a/linux/home/.config/ags/greeter/auth.ts b/linux/home/.config/ags/greeter/auth.ts
new file mode 100644
index 0000000..23477eb
--- /dev/null
+++ b/linux/home/.config/ags/greeter/auth.ts
@@ -0,0 +1,109 @@
+import AccountsService from "gi://AccountsService?version=1.0"
+import GLib from "gi://GLib?version=2.0"
+import icons from "lib/icons"
+
+const { iconFile, realName, userName } = AccountsService.UserManager
+ .get_default().list_users()[0]
+
+const loggingin = Variable(false)
+
+const CMD = GLib.getenv("ASZTAL_DM_CMD")
+ || "Hyprland"
+
+const ENV = GLib.getenv("ASZTAL_DM_ENV")
+ || "WLR_NO_HARDWARE_CURSORS=1 _JAVA_AWT_WM_NONREPARENTING=1"
+
+async function login(pw: string) {
+ loggingin.value = true
+ const greetd = await Service.import("greetd")
+ return greetd.login(userName, pw, CMD, ENV.split(/\s+/))
+ .catch(res => {
+ loggingin.value = false
+ response.label = res?.description || JSON.stringify(res)
+ password.text = ""
+ revealer.reveal_child = true
+ })
+}
+
+const avatar = Widget.Box({
+ class_name: "avatar",
+ hpack: "center",
+ css: `background-image: url('${iconFile}')`,
+})
+
+const password = Widget.Entry({
+ placeholder_text: "Password",
+ hexpand: true,
+ visibility: false,
+ on_accept: ({ text }) => { login(text || "") },
+})
+
+const response = Widget.Label({
+ class_name: "response",
+ wrap: true,
+ max_width_chars: 35,
+ hpack: "center",
+ hexpand: true,
+ xalign: .5,
+})
+
+const revealer = Widget.Revealer({
+ transition: "slide_down",
+ child: response,
+})
+
+export default Widget.Box({
+ class_name: "auth",
+ attribute: { password },
+ vertical: true,
+ children: [
+ Widget.Overlay({
+ child: Widget.Box(
+ {
+ css: "min-width: 200px; min-height: 200px;",
+ vertical: true,
+ },
+ Widget.Box({
+ class_name: "wallpaper",
+ css: `background-image: url('${WALLPAPER}')`,
+ }),
+ Widget.Box({
+ class_name: "wallpaper-contrast",
+ vexpand: true,
+ }),
+ ),
+ overlay: Widget.Box(
+ {
+ vpack: "end",
+ vertical: true,
+ },
+ avatar,
+ Widget.Box({
+ hpack: "center",
+ children: [
+ Widget.Icon(icons.ui.avatar),
+ Widget.Label(realName || userName),
+ ],
+ }),
+ Widget.Box(
+ {
+ class_name: "password",
+ },
+ Widget.Spinner({
+ visible: loggingin.bind(),
+ active: true,
+ }),
+ Widget.Icon({
+ visible: loggingin.bind().as(b => !b),
+ icon: icons.ui.lock,
+ }),
+ password,
+ ),
+ ),
+ }),
+ Widget.Box(
+ { class_name: "response-box" },
+ revealer,
+ ),
+ ],
+})
diff --git a/linux/home/.config/ags/greeter/greeter.scss b/linux/home/.config/ags/greeter/greeter.scss
new file mode 100644
index 0000000..e3a5cd8
--- /dev/null
+++ b/linux/home/.config/ags/greeter/greeter.scss
@@ -0,0 +1,64 @@
+@import "../style/mixins/floating-widget.scss";
+@import "../style/mixins/widget.scss";
+@import "../style/mixins/spacing.scss";
+@import "../style/mixins/unset.scss";
+@import "../style/mixins/a11y-button.scss";
+@import "../widget/bar/bar.scss";
+
+window#greeter {
+ background-color: lighten($bg, 6%);
+ color: $fg;
+
+ .bar {
+ background-color: transparent;
+
+ .date {
+ @include unset($rec: true);
+ @include panel-button($flat: true, $reactive: false);
+ }
+ }
+
+ .auth {
+ @include floating_widget;
+ border-radius: $radius;
+ min-width: 400px;
+ padding: 0;
+
+ .wallpaper {
+ min-height: 220px;
+ background-size: cover;
+ border-top-left-radius: $radius;
+ border-top-right-radius: $radius;
+ }
+
+ .wallpaper-contrast {
+ min-height: 100px;
+ }
+
+ .avatar {
+ border-radius: 99px;
+ min-width: 140px;
+ min-height: 140px;
+ background-size: cover;
+ box-shadow: 3px 3px 6px 0 $shadow-color;
+ margin-bottom: $spacing;
+ }
+
+
+ .password {
+ entry {
+ @include button;
+ padding: $padding*.7 $padding;
+ margin-left: $spacing*.5;
+ }
+
+ margin: 0 $padding*4;
+ margin-top: $spacing;
+ }
+
+ .response-box {
+ color: $error-bg;
+ margin: $spacing 0;
+ }
+ }
+}
diff --git a/linux/home/.config/ags/greeter/greeter.ts b/linux/home/.config/ags/greeter/greeter.ts
new file mode 100644
index 0000000..eb1493f
--- /dev/null
+++ b/linux/home/.config/ags/greeter/greeter.ts
@@ -0,0 +1,37 @@
+import "./session"
+import "style/style"
+import GLib from "gi://GLib?version=2.0"
+import RegularWindow from "widget/RegularWindow"
+import statusbar from "./statusbar"
+import auth from "./auth"
+
+const win = RegularWindow({
+ name: "greeter",
+ setup: self => {
+ self.set_default_size(500, 500)
+ self.show_all()
+ auth.attribute.password.grab_focus()
+ },
+ child: Widget.Overlay({
+ child: Widget.Box({ expand: true }),
+ overlays: [
+ Widget.Box({
+ vpack: "start",
+ hpack: "fill",
+ hexpand: true,
+ child: statusbar,
+ }),
+ Widget.Box({
+ vpack: "center",
+ hpack: "center",
+ child: auth,
+ }),
+ ],
+ }),
+})
+
+App.config({
+ icons: "./assets",
+ windows: [win],
+ cursorTheme: GLib.getenv("XCURSOR_THEME")!,
+})
diff --git a/linux/home/.config/ags/greeter/session.ts b/linux/home/.config/ags/greeter/session.ts
new file mode 100644
index 0000000..092a5c2
--- /dev/null
+++ b/linux/home/.config/ags/greeter/session.ts
@@ -0,0 +1,20 @@
+import GLib from "gi://GLib?version=2.0"
+import AccountsService from "gi://AccountsService?version=1.0"
+
+const { userName } = AccountsService.UserManager.get_default().list_users()[0]
+
+declare global {
+ const WALLPAPER: string
+}
+
+Object.assign(globalThis, {
+ TMP: `${GLib.get_tmp_dir()}/greeter`,
+ OPTIONS: "/var/cache/greeter/options.json",
+ WALLPAPER: "/var/cache/greeter/background",
+ // TMP: "/tmp/ags",
+ // OPTIONS: Utils.CACHE_DIR + "/options.json",
+ // WALLPAPER: Utils.HOME + "/.config/background",
+ USER: userName,
+})
+
+Utils.ensureDirectory(TMP)
diff --git a/linux/home/.config/ags/greeter/statusbar.ts b/linux/home/.config/ags/greeter/statusbar.ts
new file mode 100644
index 0000000..8076011
--- /dev/null
+++ b/linux/home/.config/ags/greeter/statusbar.ts
@@ -0,0 +1,46 @@
+import { clock } from "lib/variables"
+import options from "options"
+import icons from "lib/icons"
+import BatteryBar from "widget/bar/buttons/BatteryBar"
+import PanelButton from "widget/bar/PanelButton"
+
+const { scheme } = options.theme
+const { monochrome } = options.bar.powermenu
+const { format } = options.bar.date
+
+const poweroff = PanelButton({
+ class_name: "powermenu",
+ child: Widget.Icon(icons.powermenu.shutdown),
+ on_clicked: () => Utils.exec("shutdown now"),
+ setup: self => self.hook(monochrome, () => {
+ self.toggleClassName("colored", !monochrome.value)
+ self.toggleClassName("box")
+ }),
+})
+
+const date = PanelButton({
+ class_name: "date",
+ child: Widget.Label({
+ label: clock.bind().as(c => c.format(`${format}`)!),
+ }),
+})
+
+const darkmode = PanelButton({
+ class_name: "darkmode",
+ child: Widget.Icon({ icon: scheme.bind().as(s => icons.color[s]) }),
+ on_clicked: () => scheme.value = scheme.value === "dark" ? "light" : "dark",
+})
+
+export default Widget.CenterBox({
+ class_name: "bar",
+ hexpand: true,
+ center_widget: date,
+ end_widget: Widget.Box({
+ hpack: "end",
+ children: [
+ darkmode,
+ BatteryBar(),
+ poweroff,
+ ],
+ }),
+})
diff --git a/linux/home/.config/ags/lib/battery.ts b/linux/home/.config/ags/lib/battery.ts
new file mode 100644
index 0000000..3817260
--- /dev/null
+++ b/linux/home/.config/ags/lib/battery.ts
@@ -0,0 +1,16 @@
+import icons from "./icons"
+
+export default async function init() {
+ const bat = await Service.import("battery")
+ bat.connect("notify::percent", ({ percent, charging }) => {
+ const low = 30
+ if (percent !== low || percent !== low / 2 || !charging)
+ return
+
+ Utils.notify({
+ summary: `${percent}% Battery Percentage`,
+ iconName: icons.battery.warning,
+ urgency: "critical",
+ })
+ })
+}
diff --git a/linux/home/.config/ags/lib/client.js b/linux/home/.config/ags/lib/client.js
new file mode 100644
index 0000000..9fb9164
--- /dev/null
+++ b/linux/home/.config/ags/lib/client.js
@@ -0,0 +1,134 @@
+import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
+import { find_icon } from "./iconUtils.js";
+import { lookUpIcon, timeout } from 'resource:///com/github/Aylur/ags/utils.js';
+
+
+export let clientMapWorkSpace = {};
+
+export function substitute(str) {
+ const subs = [
+ { from: "code-url-handler", to: "visual-studio-code" },
+ { from: "Code", to: "visual-studio-code" },
+ { from: "GitHub Desktop", to: "github-desktop" },
+ { from: "wpsoffice", to: "wps-office2019-kprometheus" },
+ { from: "gnome-tweaks", to: "org.gnome.tweaks" },
+ { from: "Minecraft* 1.20.1", to: "minecraft" },
+ { from: "", to: "image-missing" },
+ ];
+
+ for (const { from, to } of subs) {
+ if (from === str) {
+ return to;
+ }
+ }
+
+ return str;
+}
+
+function titleToClient(title, className) {
+ const subs = [
+ { from: "musicfox", to: "musicfox" },
+ ];
+
+ for (const { from, to } of subs) {
+ if (title.indexOf(from) !== -1) {
+ return to;
+ }
+ }
+
+ return className
+}
+
+export const getClientByAdrees = function(address) {
+
+ const clients = Hyprland.clients
+
+ const client = clients.find(item => {
+ return item.address === address
+ })
+
+ return client
+}
+
+//Fullscreen client
+export const getFullScreenClientAddress = function(workspace_id) {
+
+ const clients = Hyprland.clients
+ const client = clients.find(item => {
+ return item.fullscreen && item.workspace.id === workspace_id
+ })
+ return client
+}
+
+export const ignoreAppsClass = [
+ 'image-missing',
+ 'fcitx',
+ 'rofi'
+]
+
+export const getClientIcon = (clientClass, title = "") => {
+
+ clientClass.toLowerCase()
+ clientClass = clientClass.replace(" ", "_");
+
+
+ if (title.length > 0) {
+ clientClass = titleToClient(title, clientClass)
+ }
+
+ const awesome_icon = find_icon(clientClass)
+ if (awesome_icon) {
+ return awesome_icon
+ }
+
+ if (lookUpIcon(clientClass)) {
+ return clientClass
+ }
+
+ if (find_icon('system')) {
+ return find_icon('system')
+ }
+
+ return ""
+}
+
+
+export const focus = (client) => {
+ //client
+ const { address } = client;
+ const liveClient = getClientByAdrees(address);
+
+ //special window
+ if (liveClient.workspace.id < 0) {
+ const oldWorkSpace = clientMapWorkSpace[address];
+ if (oldWorkSpace) {
+ Utils.exec(
+ `hyprctl dispatch movetoworkspace ${oldWorkSpace},address:${address}`,
+ );
+ Utils.exec(`hyprctl dispatch workspace ${oldWorkSpace}`);
+ }
+ }
+
+ //fullscreen
+ if (liveClient.fullscreen) {
+ Utils.exec("hyprctl dispatch focuswindow address:" + address);
+ return;
+ }
+
+ //workspace fullscreen client
+ const currentFullScreenAddress = getFullScreenClientAddress(
+ liveClient.workspace.id,
+ );
+ if (currentFullScreenAddress) {
+ const fullScreenAdress = currentFullScreenAddress.address;
+ Utils.exec("hyprctl dispatch focuswindow address:" + fullScreenAdress);
+ Utils.exec("hyprctl dispatch fullscreen 1");
+ }
+
+ Utils.exec("hyprctl dispatch focuswindow address:" + address);
+ // Utils.exec('hyprctl dispatch cyclenext')
+ Utils.exec("hyprctl dispatch alterzorder top,address:" + address);
+ if (currentFullScreenAddress) {
+ Utils.exec("hyprctl dispatch fullscreen 1");
+ }
+};
diff --git a/linux/home/.config/ags/lib/cursorhover.js b/linux/home/.config/ags/lib/cursorhover.js
new file mode 100644
index 0000000..d93d021
--- /dev/null
+++ b/linux/home/.config/ags/lib/cursorhover.js
@@ -0,0 +1,86 @@
+const { Gdk, Gtk } = imports.gi;
+
+const CLICK_BRIGHTEN_AMOUNT = 0.13;
+
+export function setupCursorHover(button) {
+ const display = Gdk.Display.get_default();
+ button.connect('enter-notify-event', () => {
+ const cursor = Gdk.Cursor.new_from_name(display, 'pointer');
+ button.get_window().set_cursor(cursor);
+ });
+
+ button.connect('leave-notify-event', () => {
+ const cursor = Gdk.Cursor.new_from_name(display, 'default');
+ button.get_window().set_cursor(cursor);
+ });
+
+}
+
+export function setupCursorHoverAim(button) {
+ button.connect('enter-notify-event', () => {
+ const display = Gdk.Display.get_default();
+ const cursor = Gdk.Cursor.new_from_name(display, 'crosshair');
+ button.get_window().set_cursor(cursor);
+ });
+
+ button.connect('leave-notify-event', () => {
+ const display = Gdk.Display.get_default();
+ const cursor = Gdk.Cursor.new_from_name(display, 'default');
+ button.get_window().set_cursor(cursor);
+ });
+}
+
+export function setupCursorHoverGrab(button) {
+ button.connect('enter-notify-event', () => {
+ const display = Gdk.Display.get_default();
+ const cursor = Gdk.Cursor.new_from_name(display, 'grab');
+ button.get_window().set_cursor(cursor);
+ });
+
+ button.connect('leave-notify-event', () => {
+ const display = Gdk.Display.get_default();
+ const cursor = Gdk.Cursor.new_from_name(display, 'default');
+ button.get_window().set_cursor(cursor);
+ });
+}
+
+// failed radial ripple experiment
+//
+// var clicked = false;
+// var dummy = false;
+// var cursorX = 0;
+// var cursorY = 0;
+// const styleContext = button.get_style_context();
+// var clickColor = styleContext.get_property('background-color', Gtk.StateFlags.HOVER);
+// clickColor.green += CLICK_BRIGHTEN_AMOUNT;
+// clickColor.blue += CLICK_BRIGHTEN_AMOUNT;
+// clickColor.red += CLICK_BRIGHTEN_AMOUNT;
+// clickColor = clickColor.to_string();
+// button.add_events(Gdk.EventMask.POINTER_MOTION_MASK);
+// button.connect('motion-notify-event', (widget, event) => {
+// [dummy, cursorX, cursorY] = event.get_coords(); // Get the mouse coordinates relative to the widget
+// if(!clicked) widget.css = `
+// background-image: radial-gradient(circle at ${cursorX}px ${cursorY}px, rgba(0,0,0,0), rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, ${clickColor} 0%, ${clickColor} 0%, ${clickColor} 0%, ${clickColor} 0%, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%);
+// `;
+// });
+
+// button.connect('button-press-event', (widget, event) => {
+// clicked = true;
+// [dummy, cursorX, cursorY] = event.get_coords(); // Get the mouse coordinates relative to the widget
+// cursorX = Math.round(cursorX); cursorY = Math.round(cursorY);
+// widget.css = `
+// background-image: radial-gradient(circle at ${cursorX}px ${cursorY}px, rgba(0,0,0,0), rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, ${clickColor} 0%, ${clickColor} 0%, ${clickColor} 0%, ${clickColor} 0%, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%);
+// `;
+// widget.toggleClassName('growingRadial', true);
+// widget.css = `
+// background-image: radial-gradient(circle at ${cursorX}px ${cursorY}px, rgba(0,0,0,0), rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, ${clickColor} 0%, ${clickColor} 0%, ${clickColor} 70%, ${clickColor} 70%, rgba(0,0,0,0) 70%, rgba(0,0,0,0) 70%);
+// `
+// });
+// button.connect('button-release-event', (widget, event) => {
+// widget.toggleClassName('growingRadial', false);
+// widget.toggleClassName('fadingRadial', false);
+// widget.css = `
+// background-image: radial-gradient(circle at ${cursorX}px ${cursorY}px, rgba(0,0,0,0), rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 0%, rgba(0,0,0,0) 70%, rgba(0,0,0,0) 70%, rgba(0,0,0,0) 70%, rgba(0,0,0,0) 70%);
+// `
+// clicked = false;
+// });
diff --git a/linux/home/.config/ags/lib/gtk.ts b/linux/home/.config/ags/lib/gtk.ts
new file mode 100644
index 0000000..8cd60a3
--- /dev/null
+++ b/linux/home/.config/ags/lib/gtk.ts
@@ -0,0 +1,16 @@
+import Gio from "gi://Gio"
+import options from "options"
+
+const settings = new Gio.Settings({
+ schema: "org.gnome.desktop.interface",
+})
+
+function gtk() {
+ const scheme = options.theme.scheme.value
+ settings.set_string("color-scheme", `prefer-${scheme}`)
+}
+
+export default function init() {
+ options.theme.scheme.connect("changed", gtk)
+ gtk()
+}
diff --git a/linux/home/.config/ags/lib/hyprland.ts b/linux/home/.config/ags/lib/hyprland.ts
new file mode 100644
index 0000000..7f6a68c
--- /dev/null
+++ b/linux/home/.config/ags/lib/hyprland.ts
@@ -0,0 +1,80 @@
+import options from "options"
+const { messageAsync } = await Service.import("hyprland")
+
+const {
+ hyprland,
+ theme: {
+ spacing,
+ radius,
+ border: { width },
+ blur,
+ shadows,
+ dark: {
+ primary: { bg: darkActive },
+ },
+ light: {
+ primary: { bg: lightActive },
+ },
+ scheme,
+ },
+} = options
+
+const deps = [
+ "hyprland",
+ spacing.id,
+ radius.id,
+ blur.id,
+ width.id,
+ shadows.id,
+ darkActive.id,
+ lightActive.id,
+ scheme.id,
+]
+
+function activeBorder() {
+ const color = scheme.value === "dark"
+ ? darkActive.value
+ : lightActive.value
+
+ return color.replace("#", "")
+}
+
+function sendBatch(batch: string[]) {
+ const cmd = batch
+ .filter(x => !!x)
+ .map(x => `keyword ${x}`)
+ .join("; ")
+
+ return messageAsync(`[[BATCH]]/${cmd}`)
+}
+
+async function setupHyprland() {
+ const wm_gaps = Math.floor(hyprland.gaps.value * spacing.value)
+
+ //sendBatch([
+ // `general:border_size ${width}`,
+ // `general:gaps_out ${wm_gaps}`,
+ // `general:gaps_in ${Math.floor(wm_gaps / 2)}`,
+ // `general:col.active_border rgba(${activeBorder()}ff)`,
+ // `general:col.inactive_border rgba(${hyprland.inactiveBorder.value})`,
+ // `decoration:rounding ${radius}`,
+ // `decoration:drop_shadow ${shadows.value ? "yes" : "no"}`,
+ // `dwindle:no_gaps_when_only ${hyprland.gapsWhenOnly.value ? 0 : 1}`,
+ // `master:no_gaps_when_only ${hyprland.gapsWhenOnly.value ? 0 : 1}`,
+ //])
+
+ //await sendBatch(App.windows.map(({ name }) => `layerrule unset, ${name}`))
+
+ if (blur.value > 0) {
+ sendBatch(App.windows.flatMap(({ name }) => [
+ `layerrule unset, ${name}`,
+ `layerrule blur, ${name}`,
+ `layerrule ignorealpha ${/* based on shadow color */.29}, ${name}`,
+ ]))
+ }
+}
+
+export default function init() {
+ options.handler(deps, setupHyprland)
+ setupHyprland()
+}
diff --git a/linux/home/.config/ags/lib/iconUtils.js b/linux/home/.config/ags/lib/iconUtils.js
new file mode 100644
index 0000000..baba660
--- /dev/null
+++ b/linux/home/.config/ags/lib/iconUtils.js
@@ -0,0 +1,46 @@
+const { Gio, Gdk, Gtk } = imports.gi;
+
+function fileExists(filePath) {
+ let file = Gio.File.new_for_path(filePath);
+ return file.query_exists(null);
+}
+
+function cartesianProduct(arrays) {
+ if (arrays.length === 0) {
+ return [[]];
+ }
+
+ const [head, ...tail] = arrays;
+ const tailCartesian = cartesianProduct(tail);
+ const result = [];
+
+ for (const item of head) {
+ for (const tailItem of tailCartesian) {
+ result.push([item, ...tailItem]);
+ }
+ }
+ return result;
+}
+import { HOME } from '../utils.ts';
+export const find_icon = app_class => {
+ const themPath = [
+ [`${HOME}/.local/share/icons/WhiteSur/`, `${HOME}/.local/share//icons/WhiteSur-dark/`],
+ ['512x512/', '128x128/', '64x64/', '96x96/', '72x72/', '48x48/', '36x36/'],
+ ['apps/', ''],
+ [app_class + '.png', app_class + '.svg', app_class + '.xpm'],
+ ];
+
+ let real_path = '';
+ const all_icon_dir = cartesianProduct(themPath);
+
+ for (let index = 0; index < all_icon_dir.length; index++) {
+ const pathItem = all_icon_dir[index];
+ const icon_path = pathItem.join('');
+ if (fileExists(icon_path)) {
+ real_path = icon_path;
+ break;
+ }
+ }
+
+ return real_path;
+};
diff --git a/linux/home/.config/ags/lib/icons.ts b/linux/home/.config/ags/lib/icons.ts
new file mode 100644
index 0000000..dfc9e18
--- /dev/null
+++ b/linux/home/.config/ags/lib/icons.ts
@@ -0,0 +1,186 @@
+export const substitutes = {
+ "transmission-gtk": "transmission",
+ "blueberry.py": "blueberry",
+ "Caprine": "facebook-messenger",
+ "phototonic": "terminal-symbolic",
+ "com.raggesilver.BlackBox-symbolic": "terminal-symbolic",
+ "org.wezfurlong.wezterm-symbolic": "terminal-symbolic",
+ "audio-headset-bluetooth": "audio-headphones-symbolic",
+ "audio-card-analog-usb": "audio-speakers-symbolic",
+ "audio-card-analog-pci": "audio-volume-medium-symbolic",
+ "preferences-system": "emblem-system-symbolic",
+ "com.github.Aylur.ags-symbolic": "controls-symbolic",
+ "com.github.Aylur.ags": "controls-symbolic",
+}
+
+export default {
+ missing: "image-missing-symbolic",
+ nix: {
+ nix: "nix-snowflake-symbolic",
+ },
+ app: {
+ terminal: "terminal-symbolic",
+ },
+ fallback: {
+ executable: "application-x-executable",
+ notification: "dialog-information-symbolic",
+ video: "video-x-generic-symbolic",
+ // audio: "audio-x-generic-symbolic",
+ audio: "audio-volume-medium-symbolic",
+ },
+ ui: {
+ close: "window-close-symbolic",
+ colorpicker: "color-select-symbolic",
+ info: "info-symbolic",
+ link: "external-link-symbolic",
+ lock: "system-lock-screen-symbolic",
+ menu: "open-menu-symbolic",
+ refresh: "view-refresh-symbolic",
+ search: "system-search-symbolic",
+ settings: "emblem-system-symbolic",
+ themes: "preferences-desktop-theme-symbolic",
+ tick: "object-select-symbolic",
+ time: "hourglass-symbolic",
+ toolbars: "toolbars-symbolic",
+ warning: "dialog-warning-symbolic",
+ avatar: "avatar-default-symbolic",
+ arrow: {
+ right: "pan-end-symbolic",
+ left: "pan-start-symbolic",
+ down: "pan-down-symbolic",
+ up: "pan-up-symbolic",
+ },
+ },
+ audio: {
+ mic: {
+ muted: "microphone-disabled-symbolic",
+ low: "microphone-sensitivity-low-symbolic",
+ medium: "microphone-sensitivity-medium-symbolic",
+ high: "microphone-sensitivity-high-symbolic",
+ },
+ volume: {
+ muted: "audio-volume-muted-symbolic",
+ low: "audio-volume-low-symbolic",
+ medium: "audio-volume-medium-symbolic",
+ high: "audio-volume-high-symbolic",
+ overamplified: "audio-volume-medium-symbolic",
+ },
+ type: {
+ headset: "audio-headphones-symbolic",
+ speaker: "audio-speakers-symbolic",
+ card: "audio-card-symbolic",
+ },
+ mixer: "mixer-symbolic",
+ },
+ powerprofile: {
+ balanced: "power-profile-balanced-symbolic",
+ "power-saver": "power-profile-power-saver-symbolic",
+ performance: "power-profile-performance-symbolic",
+ },
+ asusctl: {
+ profile: {
+ Balanced: "power-profile-balanced-symbolic",
+ Quiet: "power-profile-power-saver-symbolic",
+ Performance: "power-profile-performance-symbolic",
+ },
+ mode: {
+ Integrated: "processor-symbolic",
+ Hybrid: "controller-symbolic",
+ },
+ },
+ battery: {
+ charging: "battery-flash-symbolic",
+ warning: "battery-empty-symbolic",
+ },
+ bluetooth: {
+ enabled: "bluetooth-active-symbolic",
+ disabled: "bluetooth-disabled-symbolic",
+ },
+ brightness: {
+ indicator: "display-brightness-symbolic",
+ keyboard: "keyboard-brightness-symbolic",
+ screen: "display-brightness-symbolic",
+ },
+ powermenu: {
+ sleep: "weather-clear-night-symbolic",
+ reboot: "system-reboot-symbolic",
+ logout: "system-log-out-symbolic",
+ shutdown: "system-shutdown-symbolic",
+ },
+ recorder: {
+ recording: "media-record-symbolic",
+ },
+ notifications: {
+ noisy: "org.gnome.Settings-notifications-symbolic",
+ silent: "notifications-disabled-symbolic",
+ message: "chat-bubbles-symbolic",
+ },
+ trash: {
+ full: "user-trash-full-symbolic",
+ empty: "user-trash-symbolic",
+ },
+ mpris: {
+ shuffle: {
+ enabled: "media-playlist-shuffle-symbolic",
+ disabled: "media-playlist-consecutive-symbolic",
+ },
+ loop: {
+ none: "media-playlist-repeat-symbolic",
+ track: "media-playlist-repeat-song-symbolic",
+ playlist: "media-playlist-repeat-symbolic",
+ },
+ playing: "media-playback-pause-symbolic",
+ paused: "media-playback-start-symbolic",
+ stopped: "media-playback-start-symbolic",
+ prev: "media-skip-backward-symbolic",
+ next: "media-skip-forward-symbolic",
+ },
+ system: {
+ cpu: "org.gnome.SystemMonitor-symbolic",
+ ram: "drive-harddisk-solidstate-symbolic",
+ temp: "temperature-symbolic",
+ },
+ color: {
+ dark: "dark-mode-symbolic",
+ light: "light-mode-symbolic",
+ },
+ ui: {
+ arch: "archlinux-logo",
+ close: "window-close",
+ colorpicker: "color-select",
+ info: "info",
+ link: "external-link",
+ lock: "system-lock-screen",
+ menu: "open-menu",
+ refresh: "view-refresh",
+ search: "system-search",
+ settings: "emblem-system",
+ themes: "preferences-desktop-theme",
+ tick: "object-select",
+ time: "hourglass",
+ toolbars: "toolbars-symbolic",
+ warning: "dialog-warning",
+ avatar: "avatar-default",
+ tbox_osk: "osk",
+ tbox_appkill: "bomb-kill",
+ tbox_close: "tbox-close",
+ tbox_rotate: "rotation",
+ tbox_moveup: "arrows-up",
+ tbox_movedown: "arrows-down",
+ tbox_moveleft: "arrows-left",
+ tbox_moveright: "arrows-right",
+ tbox_workspacenext: "wp-next",
+ tbox_workspaceprev: "wp-prev",
+ tbox_fullscreen: "fullscreen",
+ tbox_swapnext: "swapnext",
+ tbox_float: "float",
+ tbox_pinned: "pinned",
+ tbox_split: "togglesplit",
+ arrow: {
+ right: "pan-end",
+ left: "pan-start",
+ down: "pan-down",
+ up: "pan-up",
+ },
+ },
+}
diff --git a/linux/home/.config/ags/lib/init.ts b/linux/home/.config/ags/lib/init.ts
new file mode 100644
index 0000000..e9e396c
--- /dev/null
+++ b/linux/home/.config/ags/lib/init.ts
@@ -0,0 +1,19 @@
+import matugen from './matugen';
+import hyprland from './hyprland';
+import tmux from './tmux';
+import gtk from './gtk';
+import lowBattery from './battery';
+import notifications from './notifications';
+
+export default function init() {
+ try {
+ gtk();
+ tmux();
+ matugen();
+ lowBattery();
+ notifications();
+ hyprland();
+ } catch (error) {
+ logError(error);
+ }
+}
diff --git a/linux/home/.config/ags/lib/matugen.ts b/linux/home/.config/ags/lib/matugen.ts
new file mode 100644
index 0000000..dfccccf
--- /dev/null
+++ b/linux/home/.config/ags/lib/matugen.ts
@@ -0,0 +1,113 @@
+import wallpaper from "service/wallpaper"
+import options from "options"
+import { sh, dependencies } from "./utils"
+
+export default function init() {
+ wallpaper.connect("changed", () => matugen())
+ options.autotheme.connect("changed", () => matugen())
+}
+
+function animate(...setters: Array<() => void>) {
+ const delay = options.transition.value / 2
+ setters.forEach((fn, i) => Utils.timeout(delay * i, fn))
+}
+
+export async function matugen(
+ type: "image" | "color" = "image",
+ arg = wallpaper.wallpaper,
+) {
+ if (!options.autotheme.value || !dependencies("matugen"))
+ return
+
+ const colors = await sh(`matugen --dry-run -j hex ${type} ${arg}`)
+ const c = JSON.parse(colors).colors as { light: Colors, dark: Colors }
+ const { dark, light } = options.theme
+
+ animate(
+ () => {
+ dark.widget.value = c.dark.on_surface
+ light.widget.value = c.light.on_surface
+ },
+ () => {
+ dark.border.value = c.dark.outline
+ light.border.value = c.light.outline
+ },
+ () => {
+ dark.bg.value = c.dark.surface
+ light.bg.value = c.light.surface
+ },
+ () => {
+ dark.fg.value = c.dark.on_surface
+ light.fg.value = c.light.on_surface
+ },
+ () => {
+ dark.primary.bg.value = c.dark.primary
+ light.primary.bg.value = c.light.primary
+ options.bar.battery.charging.value = options.theme.scheme.value === "dark"
+ ? c.dark.primary : c.light.primary
+ },
+ () => {
+ dark.primary.fg.value = c.dark.on_primary
+ light.primary.fg.value = c.light.on_primary
+ },
+ () => {
+ dark.error.bg.value = c.dark.error
+ light.error.bg.value = c.light.error
+ },
+ () => {
+ dark.error.fg.value = c.dark.on_error
+ light.error.fg.value = c.light.on_error
+ },
+ )
+}
+
+type Colors = {
+ background: string
+ error: string
+ error_container: string
+ inverse_on_surface: string
+ inverse_primary: string
+ inverse_surface: string
+ on_background: string
+ on_error: string
+ on_error_container: string
+ on_primary: string
+ on_primary_container: string
+ on_primary_fixed: string
+ on_primary_fixed_variant: string
+ on_secondary: string
+ on_secondary_container: string
+ on_secondary_fixed: string
+ on_secondary_fixed_variant: string
+ on_surface: string
+ on_surface_variant: string
+ on_tertiary: string
+ on_tertiary_container: string
+ on_tertiary_fixed: string
+ on_tertiary_fixed_variant: string
+ outline: string
+ outline_variant: string
+ primary: string
+ primary_container: string
+ primary_fixed: string
+ primary_fixed_dim: string
+ scrim: string
+ secondary: string
+ secondary_container: string
+ secondary_fixed: string
+ secondary_fixed_dim: string
+ shadow: string
+ surface: string
+ surface_bright: string
+ surface_container: string
+ surface_container_high: string
+ surface_container_highest: string
+ surface_container_low: string
+ surface_container_lowest: string
+ surface_dim: string
+ surface_variant: string
+ tertiary: string
+ tertiary_container: string
+ tertiary_fixed: string
+ tertiary_fixed_dim: string
+}
diff --git a/linux/home/.config/ags/lib/notifications.ts b/linux/home/.config/ags/lib/notifications.ts
new file mode 100644
index 0000000..0000831
--- /dev/null
+++ b/linux/home/.config/ags/lib/notifications.ts
@@ -0,0 +1,16 @@
+import options from "options"
+const notifs = await Service.import("notifications")
+
+// TODO: consider adding this to upstream
+
+const { blacklist } = options.notifications
+
+export default function init() {
+ const notify = notifs.constructor.prototype.Notify.bind(notifs)
+ notifs.constructor.prototype.Notify = function(appName: string, ...rest: unknown[]) {
+ if (blacklist.value.includes(appName))
+ return Number.MAX_SAFE_INTEGER
+
+ return notify(appName, ...rest)
+ }
+}
diff --git a/linux/home/.config/ags/lib/option.ts b/linux/home/.config/ags/lib/option.ts
new file mode 100644
index 0000000..2d73978
--- /dev/null
+++ b/linux/home/.config/ags/lib/option.ts
@@ -0,0 +1,115 @@
+import { Variable } from "resource:///com/github/Aylur/ags/variable.js"
+
+type OptProps = {
+ persistent?: boolean
+}
+
+export class Opt<T = unknown> extends Variable<T> {
+ static { Service.register(this) }
+
+ constructor(initial: T, { persistent = false }: OptProps = {}) {
+ super(initial)
+ this.initial = initial
+ this.persistent = persistent
+ }
+
+ initial: T
+ id = ""
+ persistent: boolean
+ toString() { return `${this.value}` }
+ toJSON() { return `opt:${this.value}` }
+
+ getValue = (): T => {
+ return super.getValue()
+ }
+
+ init(cacheFile: string) {
+ const cacheV = JSON.parse(Utils.readFile(cacheFile) || "{}")[this.id]
+ if (cacheV !== undefined)
+ this.value = cacheV
+
+ this.connect("changed", () => {
+ const cache = JSON.parse(Utils.readFile(cacheFile) || "{}")
+ cache[this.id] = this.value
+ Utils.writeFileSync(JSON.stringify(cache, null, 2), cacheFile)
+ })
+ }
+
+ reset() {
+ if (this.persistent)
+ return
+
+ if (JSON.stringify(this.value) !== JSON.stringify(this.initial)) {
+ this.value = this.initial
+ return this.id
+ }
+ }
+}
+
+export const opt = <T>(initial: T, opts?: OptProps) => new Opt(initial, opts)
+
+function getOptions(object: object, path = ""): Opt[] {
+ return Object.keys(object).flatMap(key => {
+ const obj: Opt = object[key]
+ const id = path ? path + "." + key : key
+
+ if (obj instanceof Variable) {
+ obj.id = id
+ return obj
+ }
+
+ if (typeof obj === "object")
+ return getOptions(obj, id)
+
+ return []
+ })
+}
+
+export function mkOptions<T extends object>(cacheFile: string, object: T) {
+ for (const opt of getOptions(object))
+ opt.init(cacheFile)
+
+ Utils.ensureDirectory(cacheFile.split("/").slice(0, -1).join("/"))
+
+ const configFile = `${TMP}/config.json`
+ const values = getOptions(object).reduce((obj, { id, value }) => ({ [id]: value, ...obj }), {})
+ Utils.writeFileSync(JSON.stringify(values, null, 2), configFile)
+ Utils.monitorFile(configFile, () => {
+ const cache = JSON.parse(Utils.readFile(configFile) || "{}")
+ for (const opt of getOptions(object)) {
+ if (JSON.stringify(cache[opt.id]) !== JSON.stringify(opt.value))
+ opt.value = cache[opt.id]
+ }
+ })
+
+ function sleep(ms = 0) {
+ return new Promise(r => setTimeout(r, ms))
+ }
+
+ async function reset(
+ [opt, ...list] = getOptions(object),
+ id = opt?.reset(),
+ ): Promise<Array<string>> {
+ if (!opt)
+ return sleep().then(() => [])
+
+ return id
+ ? [id, ...(await sleep(50).then(() => reset(list)))]
+ : await sleep().then(() => reset(list))
+ }
+
+ return Object.assign(object, {
+ configFile,
+ array: () => getOptions(object),
+ async reset() {
+ return (await reset()).join("\n")
+ },
+ handler(deps: string[], callback: () => void) {
+ for (const opt of getOptions(object)) {
+ if (deps.some(i => opt.id.startsWith(i)))
+ opt.connect("changed", callback)
+ }
+ },
+ })
+}
+
diff --git a/linux/home/.config/ags/lib/session.ts b/linux/home/.config/ags/lib/session.ts
new file mode 100644
index 0000000..0e3e0cf
--- /dev/null
+++ b/linux/home/.config/ags/lib/session.ts
@@ -0,0 +1,16 @@
+import GLib from "gi://GLib?version=2.0"
+
+declare global {
+ const OPTIONS: string
+ const TMP: string
+ const USER: string
+}
+
+Object.assign(globalThis, {
+ OPTIONS: `${GLib.get_user_cache_dir()}/ags/options.json`,
+ TMP: `${GLib.get_tmp_dir()}/asztal`,
+ USER: GLib.get_user_name(),
+})
+
+Utils.ensureDirectory(TMP)
+App.addIcons(`${App.configDir}/assets`)
diff --git a/linux/home/.config/ags/lib/tmux.ts b/linux/home/.config/ags/lib/tmux.ts
new file mode 100644
index 0000000..1372eb2
--- /dev/null
+++ b/linux/home/.config/ags/lib/tmux.ts
@@ -0,0 +1,14 @@
+import options from "options"
+import { sh } from "./utils"
+
+export async function tmux() {
+ const { scheme, dark, light } = options.theme
+ const hex = scheme.value === "dark" ? dark.primary.bg.value : light.primary.bg.value
+ if (await sh("which tmux"))
+ sh(`tmux set @main_accent "${hex}"`)
+}
+
+export default function init() {
+ options.theme.dark.primary.bg.connect("changed", tmux)
+ options.theme.light.primary.bg.connect("changed", tmux)
+}
diff --git a/linux/home/.config/ags/lib/utils.ts b/linux/home/.config/ags/lib/utils.ts
new file mode 100644
index 0000000..f3ff2e3
--- /dev/null
+++ b/linux/home/.config/ags/lib/utils.ts
@@ -0,0 +1,113 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { type Application } from "types/service/applications"
+import icons, { substitutes } from "./icons"
+import Gtk from "gi://Gtk?version=3.0"
+import Gdk from "gi://Gdk"
+import GLib from "gi://GLib?version=2.0"
+
+export const HOME = GLib.get_home_dir();
+
+export type Binding<T> = import("types/service").Binding<any, any, T>
+
+/**
+ * @returns substitute icon || name || fallback icon
+ */
+export function icon(name: string | null, fallback = icons.missing) {
+ if (!name)
+ return fallback || ""
+
+ if (GLib.file_test(name, GLib.FileTest.EXISTS))
+ return name
+
+ const icon = (substitutes[name] || name)
+ if (Utils.lookUpIcon(icon))
+ return icon
+
+ print(`no icon substitute "${icon}" for "${name}", fallback: "${fallback}"`)
+ return fallback
+}
+
+/**
+ * @returns execAsync(["bash", "-c", cmd])
+ */
+export async function bash(strings: TemplateStringsArray | string, ...values: unknown[]) {
+ const cmd = typeof strings === "string" ? strings : strings
+ .flatMap((str, i) => str + `${values[i] ?? ""}`)
+ .join("")
+
+ return Utils.execAsync(["bash", "-c", cmd]).catch(err => {
+ console.error(cmd, err)
+ return ""
+ })
+}
+
+/**
+ * @returns execAsync(cmd)
+ */
+export async function sh(cmd: string | string[]) {
+ return Utils.execAsync(cmd).catch(err => {
+ console.error(typeof cmd === "string" ? cmd : cmd.join(" "), err)
+ return ""
+ })
+}
+
+export function forMonitors(widget: (monitor: number) => Gtk.Window) {
+ const n = Gdk.Display.get_default()?.get_n_monitors() || 1
+ return range(n, 0).map(widget).flat(1)
+}
+
+/**
+ * @returns [start...length]
+ */
+export function range(length: number, start = 1) {
+ return Array.from({ length }, (_, i) => i + start)
+}
+
+/**
+ * @returns true if all of the `bins` are found
+ */
+export function dependencies(...bins: string[]) {
+ const missing = bins.filter(bin => {
+ return !Utils.exec(`which ${bin}`)
+ })
+
+ if (missing.length > 0) {
+ console.warn("missing dependencies:", missing.join(", "))
+ Utils.notify(`missing dependencies: ${missing.join(", ")}`)
+ }
+
+ return missing.length === 0
+}
+
+/**
+ * run app detached
+ */
+export function launchApp(app: Application) {
+ const exe = app.executable
+ .split(/\s+/)
+ .filter(str => !str.startsWith("%") && !str.startsWith("@"))
+ .join(" ")
+
+ bash(`${exe} &`)
+ app.frequency += 1
+}
+
+/**
+ * to use with drag and drop
+ */
+export function createSurfaceFromWidget(widget: Gtk.Widget) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const cairo = imports.gi.cairo as any
+ const alloc = widget.get_allocation()
+ const surface = new cairo.ImageSurface(
+ cairo.Format.ARGB32,
+ alloc.width,
+ alloc.height,
+ )
+ const cr = new cairo.Context(surface)
+ cr.setSourceRGBA(255, 255, 255, 0)
+ cr.rectangle(0, 0, alloc.width, alloc.height)
+ cr.fill()
+ widget.draw(cr)
+ return surface
+}
diff --git a/linux/home/.config/ags/lib/variables.ts b/linux/home/.config/ags/lib/variables.ts
new file mode 100644
index 0000000..78d8793
--- /dev/null
+++ b/linux/home/.config/ags/lib/variables.ts
@@ -0,0 +1,47 @@
+import GLib from "gi://GLib"
+// import options from "options"
+//
+// const intval = options.system.fetchInterval.value
+// const tempPath = options.system.temperature.value
+
+export const clock = Variable(GLib.DateTime.new_now_local(), {
+ poll: [1000, () => GLib.DateTime.new_now_local()],
+})
+
+export const uptime = Variable(0, {
+ poll: [60_000, "cat /proc/uptime", line =>
+ Number.parseInt(line.split(".")[0]) / 60,
+ ],
+})
+
+
+export const user = {
+ name: GLib.get_user_name()
+}
+
+export const distro = {
+ id: GLib.get_os_info("ID"),
+ logo: GLib.get_os_info("LOGO"),
+}
+
+// const divide = ([total, free]: string[]) => Number.parseInt(free) / Number.parseInt(total)
+//
+// export const cpu = Variable(0, {
+// poll: [intval, "top -b -n 1", out => divide(["100", out.split("\n")
+// .find(line => line.includes("Cpu(s)"))
+// ?.split(/\s+/)[1]
+// .replace(",", ".") || "0"])],
+// })
+//
+// export const ram = Variable(0, {
+// poll: [intval, "free", out => divide(out.split("\n")
+// .find(line => line.includes("Mem:"))
+// ?.split(/\s+/)
+// .splice(1, 2) || ["1", "1"])],
+// })
+//
+// export const temperature = Variable(0, {
+// poll: [intval, `cat ${tempPath}`, n => {
+// return Number.parseInt(n) / 100_000
+// }],
+// })
diff --git a/linux/home/.config/ags/main.ts b/linux/home/.config/ags/main.ts
new file mode 100644
index 0000000..fa18846
--- /dev/null
+++ b/linux/home/.config/ags/main.ts
@@ -0,0 +1,47 @@
+import "lib/session"
+import "style/style"
+import init from "lib/init"
+import options from "options"
+import Bar from "widget/bar/Bar"
+import Launcher from "widget/launcher/Launcher"
+import NotificationPopups from "widget/notifications/NotificationPopups"
+import OSD from "widget/osd/OSD"
+import Overview from "widget/overview/Overview"
+import PowerMenu from "widget/powermenu/PowerMenu"
+import ScreenCorners from "widget/bar/ScreenCorners"
+import SettingsDialog from "widget/settings/SettingsDialog"
+import Verification from "widget/powermenu/Verification"
+import { forMonitors } from "lib/utils"
+import { setupQuickSettings } from "widget/quicksettings/QuickSettings"
+import { setupDateMenu } from "widget/datemenu/DateMenu"
+//import Dock from "widget/dock/Dock";
+import FloatingDock from "widget/dock/FloatingDock"
+import ToolBoxDock from "widget/dock/ToolBoxDock"
+
+App.config({
+ onConfigParsed: () => {
+ setupQuickSettings()
+ setupDateMenu()
+ init()
+ },
+ closeWindowDelay: {
+ "launcher": options.transition.value,
+ "overview": options.transition.value,
+ "quicksettings": options.transition.value,
+ "datemenu": options.transition.value,
+ },
+ windows: () => [
+ ...forMonitors(Bar),
+ //...forMonitors(Dock),
+ ...forMonitors(FloatingDock),
+ //...forMonitors(ToolBoxDock),
+ ...forMonitors(NotificationPopups),
+ ...forMonitors(ScreenCorners),
+ ...forMonitors(OSD),
+ Launcher(),
+ Overview(),
+ PowerMenu(),
+ SettingsDialog(),
+ Verification(),
+ ],
+})
diff --git a/linux/home/.config/ags/options.ts b/linux/home/.config/ags/options.ts
new file mode 100644
index 0000000..4cf5a53
--- /dev/null
+++ b/linux/home/.config/ags/options.ts
@@ -0,0 +1,261 @@
+import { opt, mkOptions } from 'lib/option';
+import { distro } from 'lib/variables';
+import { icon } from 'lib/utils';
+import { icons } from 'assets';
+import icons from 'lib/icons';
+//import Dock from "./widgets/dock/index.js";
+
+const options = mkOptions(OPTIONS, {
+ autotheme: opt(false),
+
+ wallpaper: {
+ enable: opt(false),
+ resolution: opt<import('service/wallpaper').Resolution>(1920),
+ market: opt<import('service/wallpaper').Market>('random'),
+ },
+
+ theme: {
+ dark: {
+ primary: {
+ bg: opt('#51a4e7'),
+ fg: opt('#141414'),
+ },
+ error: {
+ bg: opt('#e55f86'),
+ fg: opt('#141414'),
+ },
+ bg: opt('#171717'),
+ fg: opt('#eeeeee'),
+ widget: opt('#eeeeee'),
+ border: opt('#eeeeee'),
+ },
+ light: {
+ primary: {
+ bg: opt('#426ede'),
+ fg: opt('#eeeeee'),
+ },
+ error: {
+ bg: opt('#b13558'),
+ fg: opt('#eeeeee'),
+ },
+ bg: opt('#fffffa'),
+ fg: opt('#080808'),
+ widget: opt('#080808'),
+ border: opt('#080808'),
+ },
+
+ blur: opt(0),
+ scheme: opt<'dark' | 'light'>('dark'),
+ widget: { opacity: opt(94) },
+ border: {
+ width: opt(1),
+ opacity: opt(100),
+ },
+
+ shadows: opt(true),
+ padding: opt(7),
+ spacing: opt(12),
+ radius: opt(11),
+ },
+
+ transition: opt(200),
+
+ font: {
+ size: opt(13),
+ name: opt('Ubuntu Nerd Font'),
+ },
+ bar: {
+ flatButtons: opt(true),
+ position: opt<'top' | 'bottom'>('top'),
+ corners: opt(false),
+ layout: {
+ start: opt<Array<import('widget/bar/Bar').BarWidget>>([
+ 'launcher',
+ 'workspaces',
+ //"taskbar",
+ 'expander',
+ 'messages',
+ ]),
+ center: opt<Array<import('widget/bar/Bar').BarWidget>>(['date']),
+ end: opt<Array<import('widget/bar/Bar').BarWidget>>([
+ 'media',
+ 'expander',
+ //"colorpicker",
+ 'screenrecord',
+ 'battery',
+ 'systray',
+ 'system',
+ 'powermenu',
+ ]),
+ },
+ launcher: {
+ icon: {
+ colored: opt(true),
+ icon: opt(icon(distro.logo, icons.ui.search)),
+ },
+ label: {
+ colored: opt(false),
+ label: opt(''),
+ //label: opt(" Applications"),
+ },
+ action: opt(() => App.toggleWindow('launcher')),
+ },
+ date: {
+ format: opt('%a %d %b %Y %H:%M:%S'),
+ action: opt(() => App.toggleWindow('datemenu')),
+ },
+ battery: {
+ bar: opt<'hidden' | 'regular' | 'whole'>('regular'),
+ charging: opt('#00D787'),
+ percentage: opt(true),
+ blocks: opt(7),
+ width: opt(50),
+ low: opt(30),
+ },
+ workspaces: {
+ workspaces: opt(6),
+ },
+ taskbar: {
+ iconSize: opt(0),
+ monochrome: opt(false),
+ exclusive: opt(false),
+ },
+ messages: {
+ action: opt(() => App.toggleWindow('datemenu')),
+ },
+ systray: {
+ ignore: opt([
+ 'KDE Connect Indicator',
+ //"spotify-client",
+ ]),
+ },
+ media: {
+ monochrome: opt(false),
+ preferred: opt('spotify'),
+ direction: opt<'left' | 'right'>('right'),
+ format: opt('{artists} - {title}'),
+ length: opt(40),
+ },
+ powermenu: {
+ monochrome: opt(false),
+ action: opt(() => App.toggleWindow('powermenu')),
+ },
+ },
+
+ dock: {
+ iconSize: opt(44),
+ pinnedApps: opt([
+ 'nemo',
+ 'firefox',
+ 'mullvad',
+ 'qbittorrent',
+ 'com.obsproject.Studio',
+ 'vlc',
+ 'spotify',
+ //"viewnior",
+ //"phototonic",
+ 'gthumb',
+ 'nomachine',
+ 'lutris',
+ 'steam',
+ 'discord',
+ 'vscode',
+ 'wezterm',
+ 'obsidian',
+ ]),
+ toolbox: {
+ icons: [opt(icon(icons.ui.tbox_close)), opt(icon(icons.ui.tbox_appkill)), opt(icon(icons.ui.tbox_rotate)), opt(icon(icons.ui.tbox_workspaceprev)), opt(icon(icons.ui.tbox_workspacenext)), opt(icon(icons.ui.tbox_moveleft)), opt(icon(icons.ui.tbox_moveright)), opt(icon(icons.ui.tbox_moveup)), opt(icon(icons.ui.tbox_movedown)), opt(icon(icons.ui.tbox_swapnext)), opt(icon(icons.ui.tbox_split)), opt(icon(icons.ui.tbox_float)), opt(icon(icons.ui.tbox_pinned)), opt(icon(icons.ui.tbox_fullscreen)), opt(icon(icons.ui.tbox_osk))],
+ },
+ },
+ launcher: {
+ width: opt(0),
+ margin: opt(80),
+ nix: {
+ pkgs: opt('nixpkgs/nixos-unstable'),
+ max: opt(8),
+ },
+ sh: {
+ max: opt(16),
+ },
+ apps: {
+ iconSize: opt(62),
+ max: opt(6),
+ favorites: opt([['firefox', 'nemo', 'obsidian', 'discord', 'spotify']]),
+ },
+ },
+
+ overview: {
+ scale: opt(9),
+ workspaces: opt(6),
+ monochromeIcon: opt(false),
+ },
+
+ powermenu: {
+ //sleep: opt('systemctl suspend'),
+ sleep: opt('loginctl suspend'),
+ //reboot: opt('reboot'),
+ reboot: opt('loginctl reboot'),
+ logout: opt('pkill Hyprland'),
+ //shutdown: opt('shutdown now'),
+ shutdown: opt('loginctl poweroff'),
+ layout: opt<'line' | 'box'>('line'),
+ labels: opt(true),
+ },
+
+ quicksettings: {
+ avatar: {
+ image: opt(`/var/lib/AccountsService/icons/${Utils.USER}`),
+ size: opt(40),
+ },
+ width: opt(380),
+ position: opt<'left' | 'center' | 'right'>('right'),
+ networkSettings: opt('gtk-launch nm-connection-editor'),
+ //networkSettings: opt('gtk-launch gnome-control-center'),
+ media: {
+ monochromeIcon: opt(true),
+ coverSize: opt(100),
+ },
+ },
+
+ datemenu: {
+ position: opt<'left' | 'center' | 'right'>('center'),
+ weather: {
+ interval: opt(60_000),
+ unit: opt<'metric' | 'imperial' | 'standard'>('metric'),
+ key: opt<string>(JSON.parse(Utils.readFile(`${App.configDir}/.weather`) || '{}')?.key || ''),
+ cities: opt<Array<number>>(JSON.parse(Utils.readFile(`${App.configDir}/.weather`) || '{}')?.cities || []),
+ },
+ },
+
+ osd: {
+ progress: {
+ vertical: opt(true),
+ pack: {
+ h: opt<'start' | 'center' | 'end'>('end'),
+ v: opt<'start' | 'center' | 'end'>('center'),
+ },
+ },
+ microphone: {
+ pack: {
+ h: opt<'start' | 'center' | 'end'>('center'),
+ v: opt<'start' | 'center' | 'end'>('end'),
+ },
+ },
+ },
+
+ notifications: {
+ position: opt<Array<'top' | 'bottom' | 'left' | 'right'>>(['top', 'right']),
+ blacklist: opt(['']),
+ //blacklist: opt(["Spotify"]),
+ width: opt(440),
+ },
+
+ hyprland: {
+ gaps: opt(2.4),
+ inactiveBorder: opt('333333ff'),
+ gapsWhenOnly: opt(false),
+ },
+});
+
+globalThis['options'] = options;
+export default options;
diff --git a/linux/home/.config/ags/package.json b/linux/home/.config/ags/package.json
new file mode 100644
index 0000000..e0586f7
--- /dev/null
+++ b/linux/home/.config/ags/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "ags",
+ "author": "srdusr",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/srdusr/dotfiles.git"
+ },
+ "devDependencies": {
+ "@girs/accountsservice-1.0": "^1.0.0-3.2.7",
+ "@typescript-eslint/eslint-plugin": "^6.20.0",
+ "eslint": "^8.56.0",
+ "eslint-config-standard-with-typescript": "^43.0.1",
+ "eslint-plugin-import": "^2.29.1",
+ "eslint-plugin-n": "^16.6.2",
+ "eslint-plugin-promise": "^6.1.1",
+ "typescript": "^5.3.3"
+ }
+}
diff --git a/linux/home/.config/ags/service/asusctl.ts b/linux/home/.config/ags/service/asusctl.ts
new file mode 100644
index 0000000..16acbd7
--- /dev/null
+++ b/linux/home/.config/ags/service/asusctl.ts
@@ -0,0 +1,52 @@
+import { sh } from "lib/utils"
+
+type Profile = "Performance" | "Balanced" | "Quiet"
+type Mode = "Hybrid" | "Integrated"
+
+class Asusctl extends Service {
+ static {
+ Service.register(this, {}, {
+ "profile": ["string", "r"],
+ "mode": ["string", "r"],
+ })
+ }
+
+ available = !!Utils.exec("which asusctl")
+ #profile: Profile = "Balanced"
+ #mode: Mode = "Hybrid"
+
+ async nextProfile() {
+ await sh("asusctl profile -n")
+ const profile = await sh("asusctl profile -p")
+ const p = profile.split(" ")[3] as Profile
+ this.#profile = p
+ this.changed("profile")
+ }
+
+ async setProfile(prof: Profile) {
+ await sh(`asusctl profile --profile-set ${prof}`)
+ this.#profile = prof
+ this.changed("profile")
+ }
+
+ async nextMode() {
+ await sh(`supergfxctl -m ${this.#mode === "Hybrid" ? "Integrated" : "Hybrid"}`)
+ this.#mode = await sh("supergfxctl -g") as Mode
+ this.changed("profile")
+ }
+
+ constructor() {
+ super()
+
+ if (this.available) {
+ sh("asusctl profile -p").then(p => this.#profile = p.split(" ")[3] as Profile)
+ sh("supergfxctl -g").then(m => this.#mode = m as Mode)
+ }
+ }
+
+ get profiles(): Profile[] { return ["Performance", "Balanced", "Quiet"] }
+ get profile() { return this.#profile }
+ get mode() { return this.#mode }
+}
+
+export default new Asusctl
diff --git a/linux/home/.config/ags/service/brightness.ts b/linux/home/.config/ags/service/brightness.ts
new file mode 100644
index 0000000..a0b8eb5
--- /dev/null
+++ b/linux/home/.config/ags/service/brightness.ts
@@ -0,0 +1,69 @@
+import { bash, dependencies, sh } from "lib/utils"
+
+if (!dependencies("brightnessctl"))
+ App.quit()
+
+const get = (args: string) => Number(Utils.exec(`brightnessctl ${args}`))
+const screen = await bash`ls -w1 /sys/class/backlight | head -1`
+const kbd = await bash`ls -w1 /sys/class/leds | head -1`
+
+class Brightness extends Service {
+ static {
+ Service.register(this, {}, {
+ "screen": ["float", "rw"],
+ "kbd": ["int", "rw"],
+ })
+ }
+
+ #kbdMax = get(`--device ${kbd} max`)
+ #kbd = get(`--device ${kbd} get`)
+ #screenMax = get("max")
+ #screen = get("get") / get("max")
+
+ get kbd() { return this.#kbd }
+ get screen() { return this.#screen }
+
+ set kbd(value) {
+ if (value < 0 || value > this.#kbdMax)
+ return
+
+ sh(`brightnessctl -d ${kbd} s ${value} -q`).then(() => {
+ this.#kbd = value
+ this.changed("kbd")
+ })
+ }
+
+ set screen(percent) {
+ if (percent < 0)
+ percent = 0
+
+ if (percent > 1)
+ percent = 1
+
+ sh(`brightnessctl set ${Math.floor(percent * 100)}% -q`).then(() => {
+ this.#screen = percent
+ this.changed("screen")
+ })
+ }
+
+ constructor() {
+ super()
+
+ const screenPath = `/sys/class/backlight/${screen}/brightness`
+ const kbdPath = `/sys/class/leds/${kbd}/brightness`
+
+ Utils.monitorFile(screenPath, async f => {
+ const v = await Utils.readFileAsync(f)
+ this.#screen = Number(v) / this.#screenMax
+ this.changed("screen")
+ })
+
+ Utils.monitorFile(kbdPath, async f => {
+ const v = await Utils.readFileAsync(f)
+ this.#kbd = Number(v) / this.#kbdMax
+ this.changed("kbd")
+ })
+ }
+}
+
+export default new Brightness
diff --git a/linux/home/.config/ags/service/colorpicker.ts b/linux/home/.config/ags/service/colorpicker.ts
new file mode 100644
index 0000000..5918f31
--- /dev/null
+++ b/linux/home/.config/ags/service/colorpicker.ts
@@ -0,0 +1,56 @@
+import icons from "lib/icons"
+import { bash, dependencies } from "lib/utils"
+
+const COLORS_CACHE = Utils.CACHE_DIR + "/colorpicker.json"
+const MAX_NUM_COLORS = 10
+
+class ColorPicker extends Service {
+ static {
+ Service.register(this, {}, {
+ "colors": ["jsobject"],
+ })
+ }
+
+ #notifID = 0
+ #colors = JSON.parse(Utils.readFile(COLORS_CACHE) || "[]") as string[]
+
+ get colors() { return [...this.#colors] }
+ set colors(colors) {
+ this.#colors = colors
+ this.changed("colors")
+ }
+
+ // TODO: doesn't work?
+ async wlCopy(color: string) {
+ if (dependencies("wl-copy"))
+ bash(`wl-copy ${color}`)
+ }
+
+ readonly pick = async () => {
+ if (!dependencies("hyprpicker"))
+ return
+
+ const color = await bash("hyprpicker -a -r")
+ if (!color)
+ return
+
+ this.wlCopy(color)
+ const list = this.colors
+ if (!list.includes(color)) {
+ list.push(color)
+ if (list.length > MAX_NUM_COLORS)
+ list.shift()
+
+ this.colors = list
+ Utils.writeFile(JSON.stringify(list, null, 2), COLORS_CACHE)
+ }
+
+ this.#notifID = await Utils.notify({
+ id: this.#notifID,
+ iconName: icons.ui.colorpicker,
+ summary: color,
+ })
+ }
+}
+
+export default new ColorPicker
diff --git a/linux/home/.config/ags/service/nix.ts b/linux/home/.config/ags/service/nix.ts
new file mode 100644
index 0000000..3bde9fc
--- /dev/null
+++ b/linux/home/.config/ags/service/nix.ts
@@ -0,0 +1,109 @@
+import icons from "lib/icons"
+import { bash, dependencies } from "lib/utils"
+import options from "options"
+
+const CACHE = `${Utils.CACHE_DIR}/nixpkgs`
+const PREFIX = "legacyPackages.x86_64-linux."
+const MAX = options.launcher.nix.max
+const nixpkgs = options.launcher.nix.pkgs
+
+export type Nixpkg = {
+ name: string
+ description: string
+ pname: string
+ version: string
+}
+
+class Nix extends Service {
+ static {
+ Service.register(this, {}, {
+ "available": ["boolean", "r"],
+ "ready": ["boolean", "rw"],
+ })
+ }
+
+ #db: { [name: string]: Nixpkg } = {}
+ #ready = true
+
+ private set ready(r: boolean) {
+ this.#ready = r
+ this.changed("ready")
+ }
+
+ get db() { return this.#db }
+ get ready() { return this.#ready }
+ get available() { return Utils.exec("which nix") }
+
+ constructor() {
+ super()
+ if (!this.available)
+ return this
+
+ this.#updateList()
+ nixpkgs.connect("changed", this.#updateList)
+ }
+
+ query = async (filter: string) => {
+ if (!dependencies("fzf", "nix") || !this.#ready)
+ return [] as string[]
+
+ return bash(`cat ${CACHE} | fzf -f ${filter} -e | head -n ${MAX} `)
+ .then(str => str.split("\n").filter(i => i))
+ }
+
+ nix(cmd: string, bin: string, args: string) {
+ return Utils.execAsync(`nix ${cmd} ${nixpkgs}#${bin} --impure ${args}`)
+ }
+
+ run = async (input: string) => {
+ if (!dependencies("nix"))
+ return
+
+ try {
+ const [bin, ...args] = input.trim().split(/\s+/)
+
+ this.ready = false
+ await this.nix("shell", bin, "--command sh -c 'exit'")
+ this.ready = true
+
+ this.nix("run", bin, ["--", ...args].join(" "))
+ } catch (err) {
+ if (typeof err === "string")
+ Utils.notify("NixRun Error", err, icons.nix.nix)
+ else
+ logError(err)
+ } finally {
+ this.ready = true
+ }
+ }
+
+ #updateList = async () => {
+ if (!dependencies("nix"))
+ return
+
+ this.ready = false
+ this.#db = {}
+
+ // const search = await bash(`nix search ${nixpkgs} --json`)
+ const search = ""
+ if (!search) {
+ this.ready = true
+ return
+ }
+
+ const json = Object.entries(JSON.parse(search) as {
+ [name: string]: Nixpkg
+ })
+
+ for (const [pkg, info] of json) {
+ const name = pkg.replace(PREFIX, "")
+ this.#db[name] = { ...info, name }
+ }
+
+ const list = Object.keys(this.#db).join("\n")
+ await Utils.writeFile(list, CACHE)
+ this.ready = true
+ }
+}
+
+export default new Nix
diff --git a/linux/home/.config/ags/service/powermenu.ts b/linux/home/.config/ags/service/powermenu.ts
new file mode 100644
index 0000000..fd16bc1
--- /dev/null
+++ b/linux/home/.config/ags/service/powermenu.ts
@@ -0,0 +1,43 @@
+import options from "options"
+
+const { sleep, reboot, logout, shutdown } = options.powermenu
+
+export type Action = "sleep" | "reboot" | "logout" | "shutdown"
+
+class PowerMenu extends Service {
+ static {
+ Service.register(this, {}, {
+ "title": ["string"],
+ "cmd": ["string"],
+ })
+ }
+
+ #title = ""
+ #cmd = ""
+
+ get title() { return this.#title }
+ get cmd() { return this.#cmd }
+
+ action(action: Action) {
+ [this.#cmd, this.#title] = {
+ sleep: [sleep.value, "Sleep"],
+ reboot: [reboot.value, "Reboot"],
+ logout: [logout.value, "Log Out"],
+ shutdown: [shutdown.value, "Shutdown"],
+ }[action]
+
+ this.notify("cmd")
+ this.notify("title")
+ this.emit("changed")
+ App.closeWindow("powermenu")
+ App.openWindow("verification")
+ }
+
+ readonly shutdown = () => {
+ this.action("shutdown")
+ }
+}
+
+const powermenu = new PowerMenu
+Object.assign(globalThis, { powermenu })
+export default powermenu
diff --git a/linux/home/.config/ags/service/screenrecord.ts b/linux/home/.config/ags/service/screenrecord.ts
new file mode 100644
index 0000000..58721d2
--- /dev/null
+++ b/linux/home/.config/ags/service/screenrecord.ts
@@ -0,0 +1,102 @@
+import GLib from "gi://GLib"
+import icons from "lib/icons"
+import { dependencies, sh, bash } from "lib/utils"
+
+const now = () => GLib.DateTime.new_now_local().format("%Y-%m-%d_%H-%M-%S")
+
+class Recorder extends Service {
+ static {
+ Service.register(this, {}, {
+ "timer": ["int"],
+ "recording": ["boolean"],
+ })
+ }
+
+ #recordings = Utils.HOME + "/Videos/Screencasting"
+ #screenshots = Utils.HOME + "/Pictures/Screenshots"
+ #file = ""
+ #interval = 0
+
+ recording = false
+ timer = 0
+
+ async start() {
+ if (!dependencies("slurp", "wf-recorder"))
+ return
+
+ if (this.recording)
+ return
+
+ Utils.ensureDirectory(this.#recordings)
+ this.#file = `${this.#recordings}/${now()}.mp4`
+ sh(`wf-recorder -g "${await sh("slurp")}" -f ${this.#file} --pixel-format yuv420p`)
+
+ this.recording = true
+ this.changed("recording")
+
+ this.timer = 0
+ this.#interval = Utils.interval(1000, () => {
+ this.changed("timer")
+ this.timer++
+ })
+ }
+
+ async stop() {
+ if (!this.recording)
+ return
+
+ await bash("killall -INT wf-recorder")
+ this.recording = false
+ this.changed("recording")
+ GLib.source_remove(this.#interval)
+
+ Utils.notify({
+ iconName: icons.fallback.video,
+ summary: "Screenrecord",
+ body: this.#file,
+ actions: {
+ "Show in Files": () => sh(`xdg-open ${this.#recordings}`),
+ "View": () => sh(`xdg-open ${this.#file}`),
+ },
+ })
+ }
+
+ async screenshot(full = false) {
+ if (!dependencies("slurp", "wayshot"))
+ return
+
+ const file = `${this.#screenshots}/${now()}.png`
+ Utils.ensureDirectory(this.#screenshots)
+
+ if (full) {
+ await sh(`wayshot -f ${file}`)
+ }
+ else {
+ const size = await sh("slurp")
+ if (!size)
+ return
+
+ await sh(`wayshot -f ${file} -s "${size}"`)
+ }
+
+ bash(`wl-copy < ${file}`)
+
+ Utils.notify({
+ image: file,
+ summary: "Screenshot",
+ body: file,
+ actions: {
+ "Show in Files": () => sh(`xdg-open ${this.#screenshots}`),
+ "View": () => sh(`xdg-open ${file}`),
+ "Edit": () => {
+ if (dependencies("swappy"))
+ sh(`swappy -f ${file}`)
+ },
+ },
+ })
+ }
+}
+
+const recorder = new Recorder
+Object.assign(globalThis, { recorder })
+export default recorder
diff --git a/linux/home/.config/ags/service/wallpaper.ts b/linux/home/.config/ags/service/wallpaper.ts
new file mode 100644
index 0000000..865c6d9
--- /dev/null
+++ b/linux/home/.config/ags/service/wallpaper.ts
@@ -0,0 +1,127 @@
+import options from 'options';
+import { dependencies, sh } from 'lib/utils';
+
+export type Resolution = 1920 | 1366 | 3840;
+export type Market = 'random' | 'en-US' | 'ja-JP' | 'en-AU' | 'en-GB' | 'de-DE' | 'en-NZ' | 'en-CA';
+
+const WP = `${Utils.HOME}/pictures/wallpapers`;
+const Cache = `${Utils.HOME}/Pictures/Wallpapers/Bing`;
+
+class Wallpaper extends Service {
+ static {
+ Service.register(
+ this,
+ {},
+ {
+ wallpaper: ['string'],
+ },
+ );
+ }
+
+ #blockMonitor = false;
+
+ #wallpaper() {
+ if (!dependencies('swww')) return;
+
+ sh('hyprctl cursorpos').then(pos => {
+ sh(['swww', 'img', '--transition-type', 'grow', '--transition-pos', pos.replace(' ', ''), WP]).then(() => {
+ this.changed('wallpaper');
+ });
+ });
+ }
+
+ async #setWallpaper(path: string) {
+ this.#blockMonitor = true;
+
+ await sh(`cp "${path}" "${WP}"`);
+ this.#wallpaper();
+
+ this.#blockMonitor = false;
+ }
+
+ async #fetchBing() {
+ // Check if wallpaper functionality is enabled
+ if (!options.wallpaper.enable.value) {
+ console.log('Wallpaper functionality is disabled.');
+ return;
+ }
+
+ try {
+ const res = await Utils.fetch('https://bing.biturl.top/', {
+ params: {
+ resolution: options.wallpaper.resolution.value,
+ format: 'json',
+ image_format: 'jpg',
+ index: 'random',
+ mkt: options.wallpaper.market.value,
+ },
+ });
+
+ if (!res.ok) {
+ console.warn('Failed to fetch from Bing:', res.statusText);
+ return;
+ }
+
+ const data = await res.json();
+ const { url } = data;
+
+ if (!url) {
+ console.warn('No URL found in Bing response:', data);
+ return;
+ }
+
+ const file = `${Cache}/${url.replace('https://www.bing.com/th?id=', '')}`;
+
+ Utils.ensureDirectory(Cache);
+
+ if (!(await Utils.fileExists(file))) {
+ await sh(`curl "${url}" --output "${file}"`);
+ await this.#setWallpaper(file);
+ } else {
+ console.log(`Wallpaper already exists: ${file}`);
+ }
+ } catch (error) {
+ console.error('Error fetching wallpaper:', error);
+ }
+ }
+
+ readonly random = () => {
+ // Check if wallpaper functionality is enabled
+ if (!options.wallpaper.enable.value) {
+ console.log('Wallpaper functionality is disabled.');
+ return;
+ }
+ this.#fetchBing();
+ };
+
+ readonly set = (path: string) => {
+ this.#setWallpaper(path);
+ };
+
+ get wallpaper() {
+ return WP;
+ }
+ constructor() {
+ super();
+
+ // Respect wallpaper.enable option
+ if (!options.wallpaper.enable.value) {
+ console.log('Wallpaper functionality is disabled, not starting swww-daemon.');
+ return;
+ }
+
+ if (!dependencies('swww')) return;
+
+ // Monitor and set wallpaper if enabled
+ Utils.monitorFile(WP, () => {
+ if (!this.#blockMonitor) this.#wallpaper();
+ });
+
+ // Start swww-daemon only when wallpaper is enabled
+ Utils.execAsync('swww-daemon')
+ .then(this.#wallpaper)
+ .catch(() => null);
+ }
+}
+
+export default new Wallpaper();
diff --git a/linux/home/.config/ags/service/weather.ts b/linux/home/.config/ags/service/weather.ts
new file mode 100644
index 0000000..14f2df2
--- /dev/null
+++ b/linux/home/.config/ags/service/weather.ts
@@ -0,0 +1,59 @@
+import options from "options"
+
+const { interval, key, cities, unit } = options.datemenu.weather
+
+class Weather extends Service {
+ static {
+ Service.register(this, {}, {
+ "forecasts": ["jsobject"],
+ })
+ }
+
+ #forecasts: Forecast[] = []
+ get forecasts() { return this.#forecasts }
+
+ async #fetch(placeid: number) {
+ const url = "https://api.openweathermap.org/data/2.5/forecast"
+ const res = await Utils.fetch(url, {
+ params: {
+ id: placeid,
+ appid: key.value,
+ untis: unit.value,
+ },
+ })
+ return await res.json()
+ }
+
+ constructor() {
+ super()
+ if (!key.value)
+ return this
+
+ Utils.interval(interval.value, () => {
+ Promise.all(cities.value.map(this.#fetch)).then(forecasts => {
+ this.#forecasts = forecasts as Forecast[]
+ this.changed("forecasts")
+ })
+ })
+ }
+}
+
+export default new Weather
+
+type Forecast = {
+ city: {
+ name: string,
+ }
+ list: Array<{
+ dt: number
+ main: {
+ temp: number
+ feels_like: number
+ },
+ weather: Array<{
+ main: string,
+ description: string,
+ icon: string,
+ }>
+ }>
+}
diff --git a/linux/home/.config/ags/style/extra.scss b/linux/home/.config/ags/style/extra.scss
new file mode 100644
index 0000000..e7f9d44
--- /dev/null
+++ b/linux/home/.config/ags/style/extra.scss
@@ -0,0 +1,67 @@
+@import './mixins/button.scss';
+
+* {
+ font-size: $font-size;
+ font-family: $font-name;
+}
+
+separator {
+ &.horizontal {
+ min-height: $border-width;
+ }
+
+ &.vertical {
+ min-width: $border-width;
+ }
+}
+
+window.popup {
+ >* {
+ border: none;
+ box-shadow: none;
+ }
+
+ menu {
+ border-radius: $popover-radius;
+ background-color: $bg;
+ padding: $popover-padding;
+ border: $border-width solid $popover-border-color;
+
+ separator {
+ background-color: $border-color;
+ }
+
+ menuitem {
+ @include button;
+ padding: $spacing * .5;
+ margin: ($spacing * .5) 0;
+
+ &:first-child {
+ margin-top: 0;
+ }
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+ }
+}
+
+tooltip {
+ * {
+ all: unset;
+ }
+
+ background-color: transparent;
+ border: none;
+
+ >*>* {
+ background-color: $bg;
+ border-radius: $radius;
+ border: $border-width solid $popover-border-color;
+ color: $fg;
+ padding: 8px;
+ margin: 4px;
+ box-shadow: 0 0 3px 0 $shadow-color;
+ }
+}
diff --git a/linux/home/.config/ags/style/mixins/a11y-button.scss b/linux/home/.config/ags/style/mixins/a11y-button.scss
new file mode 100644
index 0000000..00b24c6
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/a11y-button.scss
@@ -0,0 +1,48 @@
+@import './button';
+
+@mixin accs-button($flat: false, $reactive: true) {
+ @include unset;
+ color: $fg;
+
+ >* {
+ border-radius: $radius;
+ transition: $transition;
+
+ @if $flat {
+ background-color: transparent;
+ box-shadow: none;
+ }
+
+ @else {
+ background-color: $widget-bg;
+ box-shadow: inset 0 0 0 $border-width $border-color;
+ }
+ }
+
+
+ @if $reactive {
+
+ &:focus>*,
+ &.focused>* {
+ @include button-focus;
+ }
+
+ &:hover>* {
+ @include button-hover;
+ }
+
+ &:active,
+ &.active,
+ &.on,
+ &:checked {
+ >* {
+ @include button-active;
+ }
+
+ &:hover>* {
+ box-shadow: inset 0 0 0 $border-width $border-color,
+ inset 0 0 0 99px $hover-bg;
+ }
+ }
+ }
+}
diff --git a/linux/home/.config/ags/style/mixins/button.scss b/linux/home/.config/ags/style/mixins/button.scss
new file mode 100644
index 0000000..79ec275
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/button.scss
@@ -0,0 +1,70 @@
+@mixin button-focus() {
+ box-shadow: inset 0 0 0 $border-width $primary-bg;
+ background-color: $hover-bg;
+ color: $hover-fg;
+}
+
+@mixin button-hover() {
+ box-shadow: inset 0 0 0 $border-width $border-color;
+ background-color: $hover-bg;
+ color: $hover-fg;
+}
+
+@mixin button-active() {
+ box-shadow: inset 0 0 0 $border-width $border-color;
+ background-image: $active-gradient;
+ background-color: $primary-bg;
+ color: $primary-fg;
+}
+
+@mixin button-disabled() {
+ box-shadow: none;
+ background-color: transparent;
+ color: transparentize($fg, 0.7);
+}
+
+@mixin button($flat: false, $reactive: true, $radius: $radius, $focusable: true) {
+ all: unset;
+ transition: $transition;
+ border-radius: $radius;
+ color: $fg;
+
+ @if $flat {
+ background-color: transparent;
+ background-image: none;
+ box-shadow: none;
+ }
+
+ @else {
+ background-color: $widget-bg;
+ box-shadow: inset 0 0 0 $border-width $border-color;
+ }
+
+ @if $reactive {
+ @if $focusable {
+ &:focus {
+ @include button-focus;
+ }
+ }
+
+ &:hover {
+ @include button-hover;
+ }
+
+ &:active,
+ &.on,
+ &.active,
+ &:checked {
+ @include button-active;
+
+ &:hover {
+ box-shadow: inset 0 0 0 $border-width $border-color,
+ inset 0 0 0 99px $hover-bg;
+ }
+ }
+ }
+
+ &:disabled {
+ @include button-disabled;
+ }
+}
diff --git a/linux/home/.config/ags/style/mixins/floating-widget.scss b/linux/home/.config/ags/style/mixins/floating-widget.scss
new file mode 100644
index 0000000..613668d
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/floating-widget.scss
@@ -0,0 +1,12 @@
+@mixin floating-widget {
+ @if $shadows {
+ box-shadow: 0 0 5px 0 $shadow-color;
+ }
+
+ margin: max($spacing, 8px);
+ border: $border-width solid $popover-border-color;
+ background-color: $bg;
+ color: $fg;
+ border-radius: $popover-radius;
+ padding: $popover-padding;
+}
diff --git a/linux/home/.config/ags/style/mixins/hidden.scss b/linux/home/.config/ags/style/mixins/hidden.scss
new file mode 100644
index 0000000..ea6a42c
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/hidden.scss
@@ -0,0 +1,15 @@
+@mixin hidden {
+ background-color: transparent;
+ background-image: none;
+ border-color: transparent;
+ box-shadow: none;
+ -gtk-icon-transform: scale(0);
+
+ * {
+ background-color: transparent;
+ background-image: none;
+ border-color: transparent;
+ box-shadow: none;
+ -gtk-icon-transform: scale(0);
+ }
+}
diff --git a/linux/home/.config/ags/style/mixins/media.scss b/linux/home/.config/ags/style/mixins/media.scss
new file mode 100644
index 0000000..3178029
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/media.scss
@@ -0,0 +1,42 @@
+@mixin media() {
+ @include widget;
+ padding: $padding;
+
+ .cover {
+ @if $shadows {
+ box-shadow: 2px 2px 2px 0 $shadow-color;
+ }
+
+ background-size: cover;
+ background-position: center;
+ border-radius: $radius*0.8;
+ margin-right: $spacing;
+ }
+
+ button {
+ @include button($flat: true);
+ padding: $padding * .5;
+
+ &.play-pause {
+ margin: 0 ($spacing * .5);
+ }
+
+ image {
+ font-size: 1.2em;
+ }
+ }
+
+ .artist {
+ color: transparentize($fg, .2);
+ font-size: .9em;
+ }
+
+ scale {
+ @include slider($width: .5em, $slider: false, $gradient: linear-gradient($fg, $fg));
+ margin-bottom: $padding * .5;
+
+ trough {
+ border: none;
+ }
+ }
+}
diff --git a/linux/home/.config/ags/style/mixins/scrollable.scss b/linux/home/.config/ags/style/mixins/scrollable.scss
new file mode 100644
index 0000000..b66f246
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/scrollable.scss
@@ -0,0 +1,42 @@
+@mixin scrollable($top: false, $bottom: false) {
+
+ @if $top and $shadows {
+ undershoot.top {
+ background: linear-gradient(to bottom, $shadow-color, transparent, transparent, transparent, transparent, transparent);
+ }
+ }
+
+ @if $bottom and $shadows {
+ undershoot.bottom {
+ background: linear-gradient(to top, $shadow-color, transparent, transparent, transparent, transparent, transparent);
+ }
+ }
+
+ scrollbar,
+ scrollbar * {
+ all: unset;
+ }
+
+ scrollbar.vertical {
+ transition: $transition;
+ background-color: transparentize($bg, 0.7);
+
+ &:hover {
+ background-color: transparentize($bg, 0.3);
+
+ slider {
+ background-color: transparentize($fg, 0.3);
+ min-width: .6em;
+ }
+ }
+ }
+
+
+ scrollbar.vertical slider {
+ background-color: transparentize($fg, 0.5);
+ border-radius: $radius;
+ min-width: .4em;
+ min-height: 2em;
+ transition: $transition;
+ }
+}
diff --git a/linux/home/.config/ags/style/mixins/slider.scss b/linux/home/.config/ags/style/mixins/slider.scss
new file mode 100644
index 0000000..b90e566
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/slider.scss
@@ -0,0 +1,74 @@
+@import './unset';
+
+@mixin slider($width: 0.7em, $slider-width: .5em, $gradient: $active-gradient, $slider: true, $focusable: true, $radius: $radius) {
+ @include unset($rec: true);
+
+ trough {
+ transition: $transition;
+ border-radius: $radius;
+ border: $border;
+ background-color: $widget-bg;
+ min-height: $width;
+ min-width: $width;
+
+ highlight,
+ progress {
+ border-radius: max($radius - $border-width, 0);
+ background-image: $gradient;
+ min-height: $width;
+ min-width: $width;
+ }
+ }
+
+ slider {
+ box-shadow: none;
+ background-color: transparent;
+ border: $border-width solid transparent;
+ transition: $transition;
+ border-radius: $radius;
+ min-height: $width;
+ min-width: $width;
+ margin: -$slider-width;
+ }
+
+ &:hover {
+ trough {
+ background-color: $hover-bg;
+ }
+
+ slider {
+ @if $slider {
+ background-color: $fg;
+ border-color: $border-color;
+
+ @if $shadows {
+ box-shadow: 0 0 3px 0 $shadow-color;
+ }
+ }
+ }
+ }
+
+ &:disabled {
+
+ highlight,
+ progress {
+ background-color: transparentize($fg, 0.4);
+ background-image: none;
+ }
+ }
+
+ @if $focusable {
+ trough:focus {
+ background-color: $hover-bg;
+ box-shadow: inset 0 0 0 $border-width $primary-bg;
+
+ slider {
+ @if $slider {
+ background-color: $fg;
+ box-shadow: inset 0 0 0 $border-width $primary-bg;
+ }
+ }
+ }
+
+ }
+}
diff --git a/linux/home/.config/ags/style/mixins/spacing.scss b/linux/home/.config/ags/style/mixins/spacing.scss
new file mode 100644
index 0000000..4096fba
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/spacing.scss
@@ -0,0 +1,53 @@
+@mixin spacing($multiplier: 1, $spacing: $spacing, $rec: false) {
+ &.horizontal>* {
+ margin: 0 calc($spacing * $multiplier / 2);
+
+ &:first-child {
+ margin-left: 0;
+ }
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+
+ &.vertical>* {
+ margin: calc($spacing * $multiplier / 2) 0;
+
+ &:first-child {
+ margin-top: 0;
+ }
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ @if $rec {
+ box {
+ &.horizontal>* {
+ margin: 0 $spacing * $multiplier / 2;
+
+ &:first-child {
+ margin-left: 0;
+ }
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+
+ &.vertical>* {
+ margin: $spacing * $multiplier / 2 0;
+
+ &:first-child {
+ margin-top: 0;
+ }
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+ }
+ }
+}
diff --git a/linux/home/.config/ags/style/mixins/switch.scss b/linux/home/.config/ags/style/mixins/switch.scss
new file mode 100644
index 0000000..2abf360
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/switch.scss
@@ -0,0 +1,16 @@
+@import './button';
+
+@mixin switch {
+ @include button;
+
+ slider {
+ background-color: $primary-fg;
+ border-radius: $radius;
+ min-width: 24px;
+ min-height: 24px;
+ }
+
+ image {
+ color: transparent;
+ }
+}
diff --git a/linux/home/.config/ags/style/mixins/unset.scss b/linux/home/.config/ags/style/mixins/unset.scss
new file mode 100644
index 0000000..eb80af5
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/unset.scss
@@ -0,0 +1,9 @@
+@mixin unset($rec: false) {
+ all: unset;
+
+ @if $rec {
+ * {
+ all: unset
+ }
+ }
+}
diff --git a/linux/home/.config/ags/style/mixins/widget.scss b/linux/home/.config/ags/style/mixins/widget.scss
new file mode 100644
index 0000000..053f1aa
--- /dev/null
+++ b/linux/home/.config/ags/style/mixins/widget.scss
@@ -0,0 +1,7 @@
+@mixin widget {
+ transition: $transition;
+ border-radius: $radius;
+ color: $fg;
+ background-color: $widget-bg;
+ border: $border;
+}
diff --git a/linux/home/.config/ags/style/style.ts b/linux/home/.config/ags/style/style.ts
new file mode 100644
index 0000000..a9b94fe
--- /dev/null
+++ b/linux/home/.config/ags/style/style.ts
@@ -0,0 +1,103 @@
+/* eslint-disable max-len */
+import { type Opt } from "lib/option"
+import options from "options"
+import { bash, dependencies, sh } from "lib/utils"
+
+const deps = [
+ "font",
+ "theme",
+ "bar.flatButtons",
+ "bar.position",
+ "bar.battery.charging",
+ "bar.battery.blocks",
+]
+
+const {
+ dark,
+ light,
+ blur,
+ scheme,
+ padding,
+ spacing,
+ radius,
+ shadows,
+ widget,
+ border,
+} = options.theme
+
+const popoverPaddingMultiplier = 1.6
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const t = (dark: Opt<any> | string, light: Opt<any> | string) => scheme.value === "dark"
+ ? `${dark}` : `${light}`
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const $ = (name: string, value: string | Opt<any>) => `$${name}: ${value};`
+
+const variables = () => [
+ $("bg", blur.value ? `transparentize(${t(dark.bg, light.bg)}, ${blur.value / 100})` : t(dark.bg, light.bg)),
+ $("fg", t(dark.fg, light.fg)),
+
+ $("primary-bg", t(dark.primary.bg, light.primary.bg)),
+ $("primary-fg", t(dark.primary.fg, light.primary.fg)),
+
+ $("error-bg", t(dark.error.bg, light.error.bg)),
+ $("error-fg", t(dark.error.fg, light.error.fg)),
+
+ $("scheme", scheme),
+ $("padding", `${padding}pt`),
+ $("spacing", `${spacing}pt`),
+ $("radius", `${radius}px`),
+ $("transition", `${options.transition}ms`),
+
+ $("shadows", `${shadows}`),
+
+ $("widget-bg", `transparentize(${t(dark.widget, light.widget)}, ${widget.opacity.value / 100})`),
+
+ $("hover-bg", `transparentize(${t(dark.widget, light.widget)}, ${(widget.opacity.value * .9) / 100})`),
+ $("hover-fg", `lighten(${t(dark.fg, light.fg)}, 8%)`),
+
+ $("border-width", `${border.width}px`),
+ $("border-color", `transparentize(${t(dark.border, light.border)}, ${border.opacity.value / 100})`),
+ $("border", "$border-width solid $border-color"),
+
+ $("active-gradient", `linear-gradient(to right, ${t(dark.primary.bg, light.primary.bg)}, darken(${t(dark.primary.bg, light.primary.bg)}, 4%))`),
+ $("shadow-color", t("rgba(0,0,0,.6)", "rgba(0,0,0,.4)")),
+ $("text-shadow", t("2pt 2pt 2pt $shadow-color", "none")),
+
+ $("popover-border-color", `transparentize(${t(dark.border, light.border)}, ${Math.max(((border.opacity.value - 1) / 100), 0)})`),
+ $("popover-padding", `$padding * ${popoverPaddingMultiplier}`),
+ $("popover-radius", radius.value === 0 ? "0" : "$radius + $popover-padding"),
+
+ $("font-size", `${options.font.size}pt`),
+ $("font-name", options.font.name),
+
+ // etc
+ $("charging-bg", options.bar.battery.charging),
+ $("bar-battery-blocks", options.bar.battery.blocks),
+ $("bar-position", options.bar.position),
+ $("hyprland-gaps-multiplier", options.hyprland.gaps),
+]
+
+async function resetCss() {
+ if (!dependencies("sass", "fd"))
+ return
+
+ try {
+ const vars = `${TMP}/variables.scss`
+ await Utils.writeFile(variables().join("\n"), vars)
+
+ const fd = await sh(`fd ".scss" ${App.configDir}`)
+ const files = fd.split(/\s+/).map(f => `@import '${f}';`)
+ const scss = [`@import '${vars}';`, ...files].join("\n")
+ const css = await bash`echo "${scss}" | sass --stdin`
+
+ App.applyCss(css, true)
+ } catch (error) {
+ logError(error)
+ }
+}
+
+Utils.monitorFile(App.configDir, resetCss)
+options.handler(deps, resetCss)
+await resetCss()
diff --git a/linux/home/.config/ags/tsconfig.json b/linux/home/.config/ags/tsconfig.json
new file mode 100644
index 0000000..1708aa3
--- /dev/null
+++ b/linux/home/.config/ags/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ES2022",
+ "lib": [
+ "ES2022"
+ ],
+ "allowJs": true,
+ "checkJs": true,
+ "strict": true,
+ "noImplicitAny": false,
+ "baseUrl": ".",
+ "typeRoots": [
+ "./types",
+ "./node_modules/@girs"
+ ],
+ "skipLibCheck": true
+ }
+}
diff --git a/linux/home/.config/ags/widget/PopupWindow.ts b/linux/home/.config/ags/widget/PopupWindow.ts
new file mode 100644
index 0000000..b53b6fd
--- /dev/null
+++ b/linux/home/.config/ags/widget/PopupWindow.ts
@@ -0,0 +1,156 @@
+import { type WindowProps } from "types/widgets/window"
+import { type RevealerProps } from "types/widgets/revealer"
+import { type EventBoxProps } from "types/widgets/eventbox"
+import type Gtk from "gi://Gtk?version=3.0"
+import options from "options"
+
+type Transition = RevealerProps["transition"]
+type Child = WindowProps["child"]
+
+type PopupWindowProps = Omit<WindowProps, "name"> & {
+ name: string
+ layout?: keyof ReturnType<typeof Layout>
+ transition?: Transition,
+}
+
+export const Padding = (name: string, {
+ css = "",
+ hexpand = true,
+ vexpand = true,
+}: EventBoxProps = {}) => Widget.EventBox({
+ hexpand,
+ vexpand,
+ can_focus: false,
+ child: Widget.Box({ css }),
+ setup: w => w.on("button-press-event", () => App.toggleWindow(name)),
+})
+
+const PopupRevealer = (
+ name: string,
+ child: Child,
+ transition: Transition = "slide_down",
+) => Widget.Box(
+ { css: "padding: 1px;" },
+ Widget.Revealer({
+ transition,
+ child: Widget.Box({
+ class_name: "window-content",
+ child,
+ }),
+ transitionDuration: options.transition.bind(),
+ setup: self => self.hook(App, (_, wname, visible) => {
+ if (wname === name)
+ self.reveal_child = visible
+ }),
+ }),
+)
+
+const Layout = (name: string, child: Child, transition?: Transition) => ({
+ "center": () => Widget.CenterBox({},
+ Padding(name),
+ Widget.CenterBox(
+ { vertical: true },
+ Padding(name),
+ PopupRevealer(name, child, transition),
+ Padding(name),
+ ),
+ Padding(name),
+ ),
+ "top": () => Widget.CenterBox({},
+ Padding(name),
+ Widget.Box(
+ { vertical: true },
+ PopupRevealer(name, child, transition),
+ Padding(name),
+ ),
+ Padding(name),
+ ),
+ "top-right": () => Widget.Box({},
+ Padding(name),
+ Widget.Box(
+ {
+ hexpand: false,
+ vertical: true,
+ },
+ PopupRevealer(name, child, transition),
+ Padding(name),
+ ),
+ ),
+ "top-center": () => Widget.Box({},
+ Padding(name),
+ Widget.Box(
+ {
+ hexpand: false,
+ vertical: true,
+ },
+ PopupRevealer(name, child, transition),
+ Padding(name),
+ ),
+ Padding(name),
+ ),
+ "top-left": () => Widget.Box({},
+ Widget.Box(
+ {
+ hexpand: false,
+ vertical: true,
+ },
+ PopupRevealer(name, child, transition),
+ Padding(name),
+ ),
+ Padding(name),
+ ),
+ "bottom-left": () => Widget.Box({},
+ Widget.Box(
+ {
+ hexpand: false,
+ vertical: true,
+ },
+ Padding(name),
+ PopupRevealer(name, child, transition),
+ ),
+ Padding(name),
+ ),
+ "bottom-center": () => Widget.Box({},
+ Padding(name),
+ Widget.Box(
+ {
+ hexpand: false,
+ vertical: true,
+ },
+ Padding(name),
+ PopupRevealer(name, child, transition),
+ ),
+ Padding(name),
+ ),
+ "bottom-right": () => Widget.Box({},
+ Padding(name),
+ Widget.Box(
+ {
+ hexpand: false,
+ vertical: true,
+ },
+ Padding(name),
+ PopupRevealer(name, child, transition),
+ ),
+ ),
+})
+
+export default ({
+ name,
+ child,
+ layout = "center",
+ transition,
+ exclusivity = "ignore",
+ ...props
+}: PopupWindowProps) => Widget.Window<Gtk.Widget>({
+ name,
+ class_names: [name, "popup-window"],
+ setup: w => w.keybind("Escape", () => App.closeWindow(name)),
+ visible: false,
+ keymode: "on-demand",
+ exclusivity,
+ layer: "top",
+ anchor: ["top", "bottom", "right", "left"],
+ child: Layout(name, child, transition)[layout](),
+ ...props,
+})
diff --git a/linux/home/.config/ags/widget/RegularWindow.ts b/linux/home/.config/ags/widget/RegularWindow.ts
new file mode 100644
index 0000000..1e4225d
--- /dev/null
+++ b/linux/home/.config/ags/widget/RegularWindow.ts
@@ -0,0 +1,3 @@
+import Gtk from "gi://Gtk?version=3.0"
+
+export default Widget.subclass<typeof Gtk.Window, Gtk.Window.ConstructorProperties>(Gtk.Window)
diff --git a/linux/home/.config/ags/widget/bar/Bar.ts b/linux/home/.config/ags/widget/bar/Bar.ts
new file mode 100644
index 0000000..9343a36
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/Bar.ts
@@ -0,0 +1,57 @@
+import BatteryBar from "./buttons/BatteryBar"
+import ColorPicker from "./buttons/ColorPicker"
+import Date from "./buttons/Date"
+import Launcher from "./buttons/Launcher"
+import Media from "./buttons/Media"
+import PowerMenu from "./buttons/PowerMenu"
+import SysTray from "./buttons/SysTray"
+import SystemIndicators from "./buttons/SystemIndicators"
+import Taskbar from "./buttons/Taskbar"
+import Workspaces from "./buttons/Workspaces"
+import ScreenRecord from "./buttons/ScreenRecord"
+import Messages from "./buttons/Messages"
+import options from "options"
+
+const { start, center, end } = options.bar.layout
+const pos = options.bar.position.bind()
+
+export type BarWidget = keyof typeof widget
+
+const widget = {
+ battery: BatteryBar,
+ colorpicker: ColorPicker,
+ date: Date,
+ launcher: Launcher,
+ media: Media,
+ powermenu: PowerMenu,
+ systray: SysTray,
+ system: SystemIndicators,
+ taskbar: Taskbar,
+ workspaces: Workspaces,
+ screenrecord: ScreenRecord,
+ messages: Messages,
+ expander: () => Widget.Box({ expand: true }),
+}
+
+export default (monitor: number) => Widget.Window({
+ monitor,
+ class_name: "bar",
+ name: `bar${monitor}`,
+ exclusivity: "exclusive",
+ anchor: pos.as(pos => [pos, "right", "left"]),
+ child: Widget.CenterBox({
+ css: "min-width: 2px; min-height: 2px;",
+ startWidget: Widget.Box({
+ hexpand: true,
+ children: start.bind().as(s => s.map(w => widget[w]())),
+ }),
+ centerWidget: Widget.Box({
+ hpack: "center",
+ children: center.bind().as(c => c.map(w => widget[w]())),
+ }),
+ endWidget: Widget.Box({
+ hexpand: true,
+ children: end.bind().as(e => e.map(w => widget[w]())),
+ }),
+ }),
+})
diff --git a/linux/home/.config/ags/widget/bar/PanelButton.ts b/linux/home/.config/ags/widget/bar/PanelButton.ts
new file mode 100644
index 0000000..332b46d
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/PanelButton.ts
@@ -0,0 +1,46 @@
+import options from "options"
+import { ButtonProps } from "types/widgets/button"
+
+type PanelButtonProps = ButtonProps & {
+ window?: string,
+ flat?: boolean
+}
+
+export default ({
+ window = "",
+ flat,
+ child,
+ setup,
+ ...rest
+}: PanelButtonProps) => Widget.Button({
+ child: Widget.Box({ child }),
+ setup: self => {
+ let open = false
+
+ self.toggleClassName("panel-button")
+ self.toggleClassName(window)
+
+ self.hook(options.bar.flatButtons, () => {
+ self.toggleClassName("flat", flat ?? options.bar.flatButtons.value)
+ })
+
+ self.hook(App, (_, win, visible) => {
+ if (win !== window)
+ return
+
+ if (open && !visible) {
+ open = false
+ self.toggleClassName("active", false)
+ }
+
+ if (visible) {
+ open = true
+ self.toggleClassName("active")
+ }
+ })
+
+ if (setup)
+ setup(self)
+ },
+ ...rest,
+})
diff --git a/linux/home/.config/ags/widget/bar/ScreenCorners.ts b/linux/home/.config/ags/widget/bar/ScreenCorners.ts
new file mode 100644
index 0000000..1b35e50
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/ScreenCorners.ts
@@ -0,0 +1,25 @@
+import options from "options"
+
+const { corners } = options.bar
+
+export default (monitor: number) => Widget.Window({
+ monitor,
+ name: `corner${monitor}`,
+ class_name: "screen-corner",
+ anchor: ["top", "bottom", "right", "left"],
+ click_through: true,
+ child: Widget.Box({
+ class_name: "shadow",
+ child: Widget.Box({
+ class_name: "border",
+ expand: true,
+ child: Widget.Box({
+ class_name: "corner",
+ expand: true,
+ }),
+ }),
+ }),
+ setup: self => self.hook(corners, () => {
+ self.toggleClassName("corners", corners.value)
+ }),
+})
diff --git a/linux/home/.config/ags/widget/bar/bar.scss b/linux/home/.config/ags/widget/bar/bar.scss
new file mode 100644
index 0000000..5c6c2cd
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/bar.scss
@@ -0,0 +1,242 @@
+@use 'sass:color';
+
+$bar-spacing: $spacing * 0.3;
+$button-radius: $radius;
+
+@mixin panel-button($flat: true, $reactive: true) {
+ @include accs-button($flat, $reactive);
+
+ >* {
+ border-radius: $button-radius;
+ margin: $bar-spacing;
+ background-color: $bg;
+ }
+
+ label,
+ image {
+ font-weight: bold;
+ }
+
+ >* {
+ padding: $padding * 0.8 $padding * 1.2;
+ }
+}
+
+.bar {
+ .panel-button {
+ @include panel-button;
+
+ &:not(.flat) {
+ @include accs-button($flat: false);
+ }
+ }
+
+ .launcher {
+ .colored {
+ color: transparentize($primary-bg, 0.2);
+ }
+
+ &:hover .colored {
+ color: $primary-bg;
+ }
+
+ &:active .colored,
+ &.active .colored {
+ color: $primary-fg;
+ }
+ }
+
+ .workspaces {
+ label {
+ font-size: 0;
+ min-width: 5pt;
+ min-height: 5pt;
+ border-radius: $radius * 0.6;
+ box-shadow: inset 0 0 0 $border-width $border-color;
+ margin: 0 $padding * 0.5;
+ transition: $transition * 0.5;
+ background-color: transparentize($fg, 0.8);
+
+ &.occupied {
+ background-color: transparentize($fg, 0.2);
+ min-width: 7pt;
+ min-height: 7pt;
+ }
+
+ &.active {
+ // background-color: $primary-bg;
+ background-image: $active-gradient;
+ min-width: 20pt;
+ min-height: 12pt;
+ }
+
+ &.inctive {
+ // background-color: $primary-bg;
+ background-image: $active-gradient;
+ min-width: 20pt;
+ min-height: 12pt;
+ }
+ }
+
+ &.inactive,
+ &:active {
+ label {
+ background-color: transparentize($primary-fg, 0.3);
+
+ &.occupied {
+ background-color: transparentize($primary-fg, 0.15);
+ }
+
+ &.active {
+ background-color: $primary-fg;
+ }
+
+ &.inactive {
+ background-color: $primary-fg;
+ }
+ }
+ }
+ }
+
+ .media label {
+ margin: 0 ($spacing * 0.5);
+ }
+
+ .taskbar .indicator.active {
+ background-color: $primary-bg;
+ border-radius: $radius;
+ min-height: 4pt;
+ min-width: 6pt;
+ margin: 2pt;
+ }
+
+ .powermenu.colored,
+ .recorder {
+ image {
+ color: transparentize($error-bg, 0.3);
+ }
+
+ &:hover image {
+ color: transparentize($error-bg, 0.15);
+ }
+
+ &:active image {
+ color: $primary-fg;
+ }
+ }
+
+ .quicksettings>box>box {
+ @include spacing($spacing: if($bar-spacing==0, $padding / 2, $bar-spacing));
+ }
+
+ .quicksettings:not(.active):not(:active) {
+ .bluetooth {
+ color: $primary-bg;
+
+ label {
+ font-size: $font-size * 0.7;
+ color: $fg;
+ text-shadow: $text-shadow;
+ }
+ }
+ }
+
+ .battery-bar {
+ >* {
+ padding: 0;
+ }
+
+ &.bar-hidden>box {
+ padding: 0 $spacing * 0.5;
+
+ image {
+ margin: 0;
+ }
+ }
+
+ levelbar * {
+ all: unset;
+ transition: $transition;
+ }
+
+ .whole {
+ @if $shadows {
+ image {
+ -gtk-icon-shadow: $text-shadow;
+ }
+
+ label {
+ text-shadow: $text-shadow;
+ }
+ }
+ }
+
+ .regular image {
+ margin-left: $spacing * 0.5;
+ }
+
+ trough {
+ @include widget;
+ min-height: 12pt;
+ min-width: 12pt;
+ }
+
+ .regular trough {
+ margin-right: $spacing * 0.5;
+ }
+
+ block {
+ margin: 0;
+
+ &:last-child {
+ border-radius: 0 $button-radius $button-radius 0;
+ }
+
+ &:first-child {
+ border-radius: $button-radius 0 0 $button-radius;
+ }
+ }
+
+ .vertical {
+ block {
+ &:last-child {
+ border-radius: 0 0 $button-radius $button-radius;
+ }
+
+ &:first-child {
+ border-radius: $button-radius $button-radius 0 0;
+ }
+ }
+ }
+
+ @for $i from 1 through $bar-battery-blocks {
+ block:nth-child(#{$i}).filled {
+ background-color: color.mix($bg, $primary-bg, $i * 3);
+ }
+
+ &.low block:nth-child(#{$i}).filled {
+ background-color: color.mix($bg, $error-bg, $i * 3);
+ }
+
+ &.charging block:nth-child(#{$i}).filled {
+ background-color: color.mix($bg, $charging-bg, $i * 3);
+ }
+
+ &:active .regular block:nth-child(#{$i}).filled {
+ background-color: color.mix($bg, $primary-fg, $i * 3);
+ }
+ }
+
+ &.low image {
+ color: $error-bg;
+ }
+
+ &.charging image {
+ color: $charging-bg;
+ }
+
+ &:active image {
+ color: $primary-fg;
+ }
+ }
+}
diff --git a/linux/home/.config/ags/widget/bar/buttons/BatteryBar.ts b/linux/home/.config/ags/widget/bar/buttons/BatteryBar.ts
new file mode 100644
index 0000000..18de329
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/BatteryBar.ts
@@ -0,0 +1,94 @@
+import icons from "lib/icons"
+import options from "options"
+import PanelButton from "../PanelButton"
+
+const battery = await Service.import("battery")
+const { bar, percentage, blocks, width, low } = options.bar.battery
+
+const Indicator = () => Widget.Icon({
+ setup: self => self.hook(battery, () => {
+ self.icon = battery.charging || battery.charged
+ ? icons.battery.charging
+ : battery.icon_name
+ }),
+})
+
+const PercentLabel = () => Widget.Revealer({
+ transition: "slide_right",
+ click_through: true,
+ reveal_child: percentage.bind(),
+ child: Widget.Label({
+ label: battery.bind("percent").as(p => `${p}%`),
+ }),
+})
+
+const LevelBar = () => {
+ const level = Widget.LevelBar({
+ bar_mode: "discrete",
+ max_value: blocks.bind(),
+ visible: bar.bind().as(b => b !== "hidden"),
+ value: battery.bind("percent").as(p => (p / 100) * blocks.value),
+ })
+ const update = () => {
+ level.value = (battery.percent / 100) * blocks.value
+ level.css = `block { min-width: ${width.value / blocks.value}pt; }`
+ }
+ return level
+ .hook(width, update)
+ .hook(blocks, update)
+ .hook(bar, () => {
+ level.vpack = bar.value === "whole" ? "fill" : "center"
+ level.hpack = bar.value === "whole" ? "fill" : "center"
+ })
+}
+
+const WholeButton = () => Widget.Overlay({
+ vexpand: true,
+ child: LevelBar(),
+ class_name: "whole",
+ pass_through: true,
+ overlay: Widget.Box({
+ hpack: "center",
+ children: [
+ Widget.Icon({
+ icon: icons.battery.charging,
+ visible: Utils.merge([
+ battery.bind("charging"),
+ battery.bind("charged"),
+ ], (ing, ed) => ing || ed),
+ }),
+ Widget.Box({
+ hpack: "center",
+ vpack: "center",
+ child: PercentLabel(),
+ }),
+ ],
+ }),
+})
+
+const Regular = () => Widget.Box({
+ class_name: "regular",
+ children: [
+ Indicator(),
+ PercentLabel(),
+ LevelBar(),
+ ],
+})
+
+export default () => PanelButton({
+ class_name: "battery-bar",
+ hexpand: false,
+ on_clicked: () => { percentage.value = !percentage.value },
+ visible: battery.bind("available"),
+ child: Widget.Box({
+ expand: true,
+ visible: battery.bind("available"),
+ child: bar.bind().as(b => b === "whole" ? WholeButton() : Regular()),
+ }),
+ setup: self => self
+ .hook(bar, w => w.toggleClassName("bar-hidden", bar.value === "hidden"))
+ .hook(battery, w => {
+ w.toggleClassName("charging", battery.charging || battery.charged)
+ w.toggleClassName("low", battery.percent < low.value)
+ }),
+})
diff --git a/linux/home/.config/ags/widget/bar/buttons/ColorPicker.ts b/linux/home/.config/ags/widget/bar/buttons/ColorPicker.ts
new file mode 100644
index 0000000..5b1f3f6
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/ColorPicker.ts
@@ -0,0 +1,37 @@
+import PanelButton from "../PanelButton"
+import colorpicker from "service/colorpicker"
+import Gdk from "gi://Gdk"
+
+const css = (color: string) => `
+* {
+ background-color: ${color};
+ color: transparent;
+}
+*:hover {
+ color: white;
+ text-shadow: 2px 2px 3px rgba(0,0,0,.8);
+}`
+
+export default () => {
+ const menu = Widget.Menu({
+ class_name: "colorpicker",
+ children: colorpicker.bind("colors").as(c => c.map(color => Widget.MenuItem({
+ child: Widget.Label(color),
+ css: css(color),
+ on_activate: () => colorpicker.wlCopy(color),
+ }))),
+ })
+
+ return PanelButton({
+ class_name: "color-picker",
+ child: Widget.Icon("color-select-symbolic"),
+ tooltip_text: colorpicker.bind("colors").as(v => `${v.length} colors`),
+ on_clicked: colorpicker.pick,
+ on_secondary_click: self => {
+ if (colorpicker.colors.length === 0)
+ return
+
+ menu.popup_at_widget(self, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null)
+ },
+ })
+}
diff --git a/linux/home/.config/ags/widget/bar/buttons/Date.ts b/linux/home/.config/ags/widget/bar/buttons/Date.ts
new file mode 100644
index 0000000..44c2540
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/Date.ts
@@ -0,0 +1,15 @@
+import { clock } from "lib/variables"
+import PanelButton from "../PanelButton"
+import options from "options"
+
+const { format, action } = options.bar.date
+const time = Utils.derive([clock, format], (c, f) => c.format(f) || "")
+
+export default () => PanelButton({
+ window: "datemenu",
+ on_clicked: action.bind(),
+ child: Widget.Label({
+ justification: "center",
+ label: time.bind(),
+ }),
+})
diff --git a/linux/home/.config/ags/widget/bar/buttons/Launcher.ts b/linux/home/.config/ags/widget/bar/buttons/Launcher.ts
new file mode 100644
index 0000000..f3fee6b
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/Launcher.ts
@@ -0,0 +1,49 @@
+import PanelButton from "../PanelButton"
+import options from "options"
+import nix from "service/nix"
+
+const { icon, label, action } = options.bar.launcher
+
+function Spinner() {
+ const child = Widget.Icon({
+ icon: icon.icon.bind(),
+ class_name: Utils.merge([
+ icon.colored.bind(),
+ nix.bind("ready"),
+ ], (c, r) => `${c ? "colored" : ""} ${r ? "" : "spinning"}`),
+ css: `
+ @keyframes spin {
+ to { -gtk-icon-transform: rotate(1turn); }
+ }
+
+ image.spinning {
+ animation-name: spin;
+ animation-duration: 1s;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ }
+ `,
+ })
+
+ return Widget.Revealer({
+ transition: "slide_left",
+ child,
+ reveal_child: Utils.merge([
+ icon.icon.bind(),
+ nix.bind("ready"),
+ ], (i, r) => Boolean(i || r)),
+ })
+}
+
+export default () => PanelButton({
+ window: "launcher",
+ on_clicked: action.bind(),
+ child: Widget.Box([
+ Spinner(),
+ Widget.Label({
+ class_name: label.colored.bind().as(c => c ? "colored" : ""),
+ visible: label.label.bind().as(v => !!v),
+ label: label.label.bind(),
+ }),
+ ]),
+})
diff --git a/linux/home/.config/ags/widget/bar/buttons/Media.ts b/linux/home/.config/ags/widget/bar/buttons/Media.ts
new file mode 100644
index 0000000..b3aab61
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/Media.ts
@@ -0,0 +1,92 @@
+import { type MprisPlayer } from "types/service/mpris"
+import PanelButton from "../PanelButton"
+import options from "options"
+import icons from "lib/icons"
+import { icon } from "lib/utils"
+
+const mpris = await Service.import("mpris")
+const { length, direction, preferred, monochrome, format } = options.bar.media
+
+const getPlayer = (name = preferred.value) =>
+ mpris.getPlayer(name) || mpris.players[0] || null
+
+const Content = (player: MprisPlayer) => {
+ const revealer = Widget.Revealer({
+ click_through: true,
+ visible: length.bind().as(l => l > 0),
+ transition: direction.bind().as(d => `slide_${d}` as const),
+ setup: self => {
+ let current = ""
+ self.hook(player, () => {
+ if (current === player.track_title)
+ return
+
+ current = player.track_title
+ self.reveal_child = true
+ Utils.timeout(3000, () => {
+ !self.is_destroyed && (self.reveal_child = false)
+ })
+ })
+ },
+ child: Widget.Label({
+ truncate: "end",
+ max_width_chars: length.bind().as(n => n > 0 ? n : -1),
+ label: Utils.merge([
+ player.bind("track_title"),
+ player.bind("track_artists"),
+ format.bind(),
+ ], () => `${format}`
+ .replace("{title}", player.track_title)
+ .replace("{artists}", player.track_artists.join(", "))
+ .replace("{artist}", player.track_artists[0] || "")
+ .replace("{album}", player.track_album)
+ .replace("{name}", player.name)
+ .replace("{identity}", player.identity),
+ ),
+ }),
+ })
+
+ const playericon = Widget.Icon({
+ icon: Utils.merge([player.bind("entry"), monochrome.bind()], (entry => {
+ const name = `${entry}${monochrome.value ? "-symbolic" : ""}`
+ return icon(name, icons.fallback.audio)
+ })),
+ })
+
+ return Widget.Box({
+ attribute: { revealer },
+ children: direction.bind().as(d => d === "right"
+ ? [playericon, revealer] : [revealer, playericon]),
+ })
+}
+
+export default () => {
+ let player = getPlayer()
+
+ const btn = PanelButton({
+ class_name: "media",
+ child: Widget.Icon(icons.fallback.audio),
+ })
+
+ const update = () => {
+ player = getPlayer()
+ btn.visible = !!player
+
+ if (!player)
+ return
+
+ const content = Content(player)
+ const { revealer } = content.attribute
+ btn.child = content
+ btn.on_primary_click = () => { player.playPause() }
+ btn.on_secondary_click = () => { player.playPause() }
+ btn.on_scroll_up = () => { player.next() }
+ btn.on_scroll_down = () => { player.previous() }
+ btn.on_hover = () => { revealer.reveal_child = true }
+ btn.on_hover_lost = () => { revealer.reveal_child = false }
+ }
+
+ return btn
+ .hook(preferred, update)
+ .hook(mpris, update, "notify::players")
+}
diff --git a/linux/home/.config/ags/widget/bar/buttons/Messages.ts b/linux/home/.config/ags/widget/bar/buttons/Messages.ts
new file mode 100644
index 0000000..a8971e9
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/Messages.ts
@@ -0,0 +1,16 @@
+import icons from "lib/icons"
+import PanelButton from "../PanelButton"
+import options from "options"
+
+const n = await Service.import("notifications")
+const notifs = n.bind("notifications")
+const action = options.bar.messages.action.bind()
+
+export default () => PanelButton({
+ class_name: "messages",
+ on_clicked: action,
+ visible: notifs.as(n => n.length > 0),
+ child: Widget.Box([
+ Widget.Icon(icons.notifications.message),
+ ]),
+})
diff --git a/linux/home/.config/ags/widget/bar/buttons/PowerMenu.ts b/linux/home/.config/ags/widget/bar/buttons/PowerMenu.ts
new file mode 100644
index 0000000..4432ade
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/PowerMenu.ts
@@ -0,0 +1,15 @@
+import icons from "lib/icons"
+import PanelButton from "../PanelButton"
+import options from "options"
+
+const { monochrome, action } = options.bar.powermenu
+
+export default () => PanelButton({
+ window: "powermenu",
+ on_clicked: action.bind(),
+ child: Widget.Icon(icons.powermenu.shutdown),
+ setup: self => self.hook(monochrome, () => {
+ self.toggleClassName("colored", !monochrome.value)
+ self.toggleClassName("box")
+ }),
+})
diff --git a/linux/home/.config/ags/widget/bar/buttons/ScreenRecord.ts b/linux/home/.config/ags/widget/bar/buttons/ScreenRecord.ts
new file mode 100644
index 0000000..1d6eb36
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/ScreenRecord.ts
@@ -0,0 +1,21 @@
+import PanelButton from "../PanelButton"
+import screenrecord from "service/screenrecord"
+import icons from "lib/icons"
+
+export default () => PanelButton({
+ class_name: "recorder",
+ on_clicked: () => screenrecord.stop(),
+ visible: screenrecord.bind("recording"),
+ child: Widget.Box({
+ children: [
+ Widget.Icon(icons.recorder.recording),
+ Widget.Label({
+ label: screenrecord.bind("timer").as(time => {
+ const sec = time % 60
+ const min = Math.floor(time / 60)
+ return `${min}:${sec < 10 ? "0" + sec : sec}`
+ }),
+ }),
+ ],
+ }),
+})
diff --git a/linux/home/.config/ags/widget/bar/buttons/SysTray.ts b/linux/home/.config/ags/widget/bar/buttons/SysTray.ts
new file mode 100644
index 0000000..9f569d1
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/SysTray.ts
@@ -0,0 +1,39 @@
+import { type TrayItem } from "types/service/systemtray"
+import PanelButton from "../PanelButton"
+import Gdk from "gi://Gdk"
+import options from "options"
+
+const systemtray = await Service.import("systemtray")
+const { ignore } = options.bar.systray
+
+const SysTrayItem = (item: TrayItem) => PanelButton({
+ class_name: "tray-item",
+ child: Widget.Icon({ icon: item.bind("icon") }),
+ tooltip_markup: item.bind("tooltip_markup"),
+ setup: self => {
+ const { menu } = item
+ if (!menu)
+ return
+
+ const id = menu.connect("popped-up", () => {
+ self.toggleClassName("active")
+ menu.connect("notify::visible", () => {
+ self.toggleClassName("active", menu.visible)
+ })
+ menu.disconnect(id!)
+ })
+
+ self.connect("destroy", () => menu.disconnect(id))
+ },
+
+ on_primary_click: btn => item.menu?.popup_at_widget(
+ btn, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null),
+
+ on_secondary_click: btn => item.menu?.popup_at_widget(
+ btn, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null),
+})
+
+export default () => Widget.Box()
+ .bind("children", systemtray, "items", i => i
+ .filter(({ id }) => !ignore.value.includes(id))
+ .map(SysTrayItem))
diff --git a/linux/home/.config/ags/widget/bar/buttons/SystemIndicators.ts b/linux/home/.config/ags/widget/bar/buttons/SystemIndicators.ts
new file mode 100644
index 0000000..cc98548
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/SystemIndicators.ts
@@ -0,0 +1,107 @@
+import PanelButton from '../PanelButton';
+import icons from 'lib/icons';
+import asusctl from 'service/asusctl';
+
+const notifications = await Service.import('notifications');
+const bluetooth = await Service.import('bluetooth');
+const audio = await Service.import('audio');
+const network = await Service.import('network');
+const powerprof = await Service.import('powerprofiles');
+
+const ProfileIndicator = () => {
+ const visible = asusctl.available ? asusctl.bind('profile').as(p => p !== 'Balanced') : powerprof.bind('active_profile').as(p => p !== 'balanced');
+
+ const icon = asusctl.available ? asusctl.bind('profile').as(p => icons.asusctl.profile[p]) : powerprof.bind('active_profile').as(p => icons.powerprofile[p]);
+
+ return Widget.Icon({ visible, icon });
+};
+
+const ModeIndicator = () => {
+ if (!asusctl.available) {
+ return Widget.Icon({
+ setup(self) {
+ Utils.idle(() => (self.visible = false));
+ },
+ });
+ }
+
+ return Widget.Icon({
+ visible: asusctl.bind('mode').as(m => m !== 'Hybrid'),
+ icon: asusctl.bind('mode').as(m => icons.asusctl.mode[m]),
+ });
+};
+
+const MicrophoneIndicator = () =>
+ Widget.Icon()
+ .hook(audio, self => (self.visible = audio.recorders.length > 0 || audio.microphone.is_muted || false))
+ .hook(audio.microphone, self => {
+ const vol = audio.microphone.is_muted ? 0 : audio.microphone.volume;
+ const { muted, low, medium, high } = icons.audio.mic;
+ const cons = [
+ [67, high],
+ [34, medium],
+ [1, low],
+ [0, muted],
+ ] as const;
+ self.icon = cons.find(([n]) => n <= vol * 100)?.[1] || '';
+ });
+
+const DNDIndicator = () =>
+ Widget.Icon({
+ visible: notifications.bind('dnd'),
+ icon: icons.notifications.silent,
+ });
+
+const BluetoothIndicator = () =>
+ Widget.Overlay({
+ class_name: 'bluetooth',
+ passThrough: true,
+ child: Widget.Icon({
+ icon: icons.bluetooth.enabled,
+ visible: bluetooth.bind('enabled'),
+ }),
+ overlay: Widget.Label({
+ hpack: 'end',
+ vpack: 'start',
+ label: bluetooth.bind('connected_devices').as(c => `${c.length}`),
+ visible: bluetooth.bind('connected_devices').as(c => c.length > 0),
+ }),
+ });
+
+const NetworkIndicator = () =>
+ Widget.Icon().hook(network, self => {
+ const icon = network[network.primary || 'wifi']?.icon_name;
+ self.icon = icon || '';
+ self.visible = !!icon;
+ });
+
+const AudioIndicator = () =>
+ Widget.Icon().hook(audio.speaker, self => {
+ const vol = audio.speaker.is_muted ? 0 : audio.speaker.volume;
+ const { muted, low, medium, high, overamplified } = icons.audio.volume;
+ const cons = [
+ [101, overamplified],
+ [67, high],
+ [34, medium],
+ [1, low],
+ [0, muted],
+ ] as const;
+ self.icon = cons.find(([n]) => n <= vol * 100)?.[1] || '';
+ });
+
+export default () =>
+ PanelButton({
+ window: 'quicksettings',
+ on_clicked: () => App.toggleWindow('quicksettings'),
+ on_scroll_up: () => (audio.speaker.volume += 0.02),
+ on_scroll_down: () => (audio.speaker.volume -= 0.02),
+ child: Widget.Box([
+ //ProfileIndicator(),
+ ModeIndicator(),
+ DNDIndicator(),
+ BluetoothIndicator(),
+ MicrophoneIndicator(),
+ AudioIndicator(),
+ NetworkIndicator(),
+ ]),
+ });
diff --git a/linux/home/.config/ags/widget/bar/buttons/Taskbar.ts b/linux/home/.config/ags/widget/bar/buttons/Taskbar.ts
new file mode 100644
index 0000000..b9c65fa
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/Taskbar.ts
@@ -0,0 +1,90 @@
+import { launchApp, icon } from "lib/utils"
+import icons from "lib/icons"
+import options from "options"
+import PanelButton from "../PanelButton"
+
+const hyprland = await Service.import("hyprland")
+const apps = await Service.import("applications")
+const { monochrome, exclusive, iconSize } = options.bar.taskbar
+const { position } = options.bar
+
+const focus = (address: string) => hyprland.messageAsync(
+ `dispatch focuswindow address:${address}`)
+
+const DummyItem = (address: string) => Widget.Box({
+ attribute: { address },
+ visible: false,
+})
+
+const AppItem = (address: string) => {
+ const client = hyprland.getClient(address)
+ if (!client || client.class === "")
+ return DummyItem(address)
+
+ const app = apps.list.find(app => app.match(client.class))
+
+ const btn = PanelButton({
+ class_name: "panel-button",
+ tooltip_text: Utils.watch(client.title, hyprland, () =>
+ hyprland.getClient(address)?.title || "",
+ ),
+ on_primary_click: () => focus(address),
+ on_middle_click: () => app && launchApp(app),
+ child: Widget.Icon({
+ size: iconSize.bind(),
+ icon: monochrome.bind().as(m => icon(
+ (app?.icon_name || client.class) + (m ? "-symbolic" : ""),
+ icons.fallback.executable + (m ? "-symbolic" : ""),
+ )),
+ }),
+ })
+
+ return Widget.Box(
+ {
+ attribute: { address },
+ visible: Utils.watch(true, [exclusive, hyprland], () => {
+ return exclusive.value
+ ? hyprland.active.workspace.id === client.workspace.id
+ : true
+ }),
+ },
+ Widget.Overlay({
+ child: btn,
+ pass_through: true,
+ overlay: Widget.Box({
+ className: "indicator",
+ hpack: "center",
+ vpack: position.bind().as(p => p === "top" ? "start" : "end"),
+ setup: w => w.hook(hyprland, () => {
+ w.toggleClassName("active", hyprland.active.client.address === address)
+ }),
+ }),
+ }),
+ )
+}
+
+function sortItems<T extends { attribute: { address: string } }>(arr: T[]) {
+ return arr.sort(({ attribute: a }, { attribute: b }) => {
+ const aclient = hyprland.getClient(a.address)!
+ const bclient = hyprland.getClient(b.address)!
+ return aclient.workspace.id - bclient.workspace.id
+ })
+}
+
+export default () => Widget.Box({
+ class_name: "taskbar",
+ children: sortItems(hyprland.clients.map(c => AppItem(c.address))),
+ setup: w => w
+ .hook(hyprland, (w, address?: string) => {
+ if (typeof address === "string")
+ w.children = w.children.filter(ch => ch.attribute.address !== address)
+ }, "client-removed")
+ .hook(hyprland, (w, address?: string) => {
+ if (typeof address === "string")
+ w.children = sortItems([...w.children, AppItem(address)])
+ }, "client-added")
+ .hook(hyprland, (w, event?: string) => {
+ if (event === "movewindow")
+ w.children = sortItems(w.children)
+ }, "event"),
+})
diff --git a/linux/home/.config/ags/widget/bar/buttons/Workspaces.ts b/linux/home/.config/ags/widget/bar/buttons/Workspaces.ts
new file mode 100644
index 0000000..a59f61b
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/buttons/Workspaces.ts
@@ -0,0 +1,66 @@
+import PanelButton from '../PanelButton';
+import options from 'options';
+import { sh, range } from 'lib/utils';
+
+const hyprland = await Service.import('hyprland');
+const { workspaces } = options.bar.workspaces;
+
+const dispatch = arg => {
+ sh(`hyprctl dispatch workspace ${arg}`);
+};
+
+const Workspaces = ws =>
+ Widget.Box({
+ children: range(ws || 20).map(i =>
+ Widget.Label({
+ attribute: i,
+ vpack: 'center',
+ label: `${i}`,
+ setup: self => {
+ const updateState = () => {
+ const monitorData = JSON.parse(hyprland.message('j/monitors'));
+ const activeWorkspaceId = monitorData[0]?.activeWorkspace?.id;
+ const workspaceData = hyprland.getWorkspace(i);
+
+ if (activeWorkspaceId !== undefined) {
+ self.toggleClassName('active', activeWorkspaceId === i);
+ }
+ self.toggleClassName('occupied', (workspaceData?.windows || 0) > 0);
+ };
+
+ // Hook to Hyprland for updates
+ self.hook(hyprland, updateState);
+
+ // Initial update
+ updateState();
+ },
+ }),
+ ),
+ setup: box => {
+ box.hook(hyprland, () => {
+ const monitorData = JSON.parse(hyprland.message('j/monitors'));
+ const activeWorkspaceId = monitorData[0]?.activeWorkspace?.id;
+
+ if (activeWorkspaceId !== undefined) {
+ for (const btn of box.children) {
+ const workspaceId = btn.attribute;
+ btn.toggleClassName('active', workspaceId === activeWorkspaceId);
+
+ if (ws === 0) {
+ btn.visible = hyprland.workspaces.some(workspace => workspace.id === workspaceId);
+ }
+ }
+ }
+ });
+ },
+ });
+
+export default () =>
+ PanelButton({
+ window: 'overview',
+ class_name: 'workspaces',
+ on_scroll_up: () => dispatch('m+1'),
+ on_scroll_down: () => dispatch('m-1'),
+ on_clicked: () => App.toggleWindow('overview'),
+ child: workspaces.bind().as(Workspaces),
+ });
diff --git a/linux/home/.config/ags/widget/bar/screencorner.scss b/linux/home/.config/ags/widget/bar/screencorner.scss
new file mode 100644
index 0000000..93cd459
--- /dev/null
+++ b/linux/home/.config/ags/widget/bar/screencorner.scss
@@ -0,0 +1,51 @@
+$_shadow-size: $padding;
+$_radius: $radius * $hyprland-gaps-multiplier;
+$_margin: 99px;
+
+window.screen-corner {
+ box.shadow {
+ margin-right: $_margin * -1;
+ margin-left: $_margin * -1;
+
+ @if $shadows {
+ box-shadow: inset 0 0 $_shadow-size 0 transparent;
+ }
+
+ @if $bar-position =='top' {
+ margin-bottom: $_margin * -1;
+ }
+
+ @if $bar-position =='bottom' {
+ margin-top: $_margin * -1;
+ }
+ }
+
+ box.border {
+ @if $bar-position =='top' {
+ border-top: $border-width none $bg;
+ //border-top: $border-width solid $bg;
+ }
+
+ @if $bar-position =='bottom' {
+ border-bottom: $border-width solid $bg;
+ }
+
+ margin-right: $_margin;
+ margin-left: $_margin;
+ }
+
+ box.corner {
+ box-shadow: 0 0 0 $border-width $border-color;
+ }
+
+ &.corners {
+ box.border {
+ border-radius: if($radius>0, $radius * $hyprland-gaps-multiplier, 0);
+ box-shadow: 0 0 0 $_radius $bg;
+ }
+
+ box.corner {
+ border-radius: if($radius>0, $radius * $hyprland-gaps-multiplier, 0);
+ }
+ }
+}
diff --git a/linux/home/.config/ags/widget/datemenu/DateColumn.ts b/linux/home/.config/ags/widget/datemenu/DateColumn.ts
new file mode 100644
index 0000000..a462302
--- /dev/null
+++ b/linux/home/.config/ags/widget/datemenu/DateColumn.ts
@@ -0,0 +1,58 @@
+import { clock, uptime } from 'lib/variables';
+import GLib from 'gi://GLib';
+import Gtk from 'gi://Gtk';
+
+function up(up: number) {
+ const h = Math.floor(up / 60);
+ const m = Math.floor(up % 60);
+ return `uptime: ${h}:${m < 10 ? '0' + m : m}`;
+}
+
+export default () =>
+ Widget.Box({
+ vertical: true,
+ class_name: 'date-column vertical',
+ children: [
+ Widget.Box({
+ class_name: 'clock-box',
+ vertical: true,
+ children: [
+ Widget.Label({
+ class_name: 'clock',
+ label: clock.bind().as(t => t.format('%H:%M')!),
+ }),
+ Widget.Label({
+ class_name: 'uptime',
+ label: uptime.bind().as(up),
+ }),
+ ],
+ }),
+ Widget.Box({
+ class_name: 'calendar',
+ children: [
+ (() => {
+ const calendar = Widget.Calendar({
+ hexpand: true,
+ hpack: 'center',
+ });
+
+ // Get today's date and mark it
+ const today = new Date();
+ calendar.select_day(today.getDate());
+ calendar.select_month(today.getMonth(), today.getFullYear());
+ calendar.mark_day(today.getDate()); // This should trigger styling
+
+ // Prevent scrolling from triggering GNOME Calendar
+ const eventBox = Widget.EventBox({
+ child: calendar,
+ onPrimaryClick: () => {
+ GLib.spawn_command_line_async('gnome-calendar');
+ },
+ });
+
+ return eventBox;
+ })(),
+ ],
+ }),
+ ],
+ });
diff --git a/linux/home/.config/ags/widget/datemenu/DateMenu.ts b/linux/home/.config/ags/widget/datemenu/DateMenu.ts
new file mode 100644
index 0000000..f7fdf6d
--- /dev/null
+++ b/linux/home/.config/ags/widget/datemenu/DateMenu.ts
@@ -0,0 +1,36 @@
+import PopupWindow from "widget/PopupWindow"
+import NotificationColumn from "./NotificationColumn"
+import DateColumn from "./DateColumn"
+import options from "options"
+
+const { bar, datemenu } = options
+const pos = bar.position.bind()
+const layout = Utils.derive([bar.position, datemenu.position], (bar, qs) =>
+ `${bar}-${qs}` as const,
+)
+
+const Settings = () => Widget.Box({
+ class_name: "datemenu horizontal",
+ vexpand: false,
+ children: [
+ NotificationColumn(),
+ Widget.Separator({ orientation: 1 }),
+ DateColumn(),
+ ],
+})
+
+const DateMenu = () => PopupWindow({
+ name: "datemenu",
+ exclusivity: "exclusive",
+ transition: pos.as(pos => pos === "top" ? "slide_down" : "slide_up"),
+ layout: layout.value,
+ child: Settings(),
+})
+
+export function setupDateMenu() {
+ App.addWindow(DateMenu())
+ layout.connect("changed", () => {
+ App.removeWindow("datemenu")
+ App.addWindow(DateMenu())
+ })
+}
diff --git a/linux/home/.config/ags/widget/datemenu/NotificationColumn.ts b/linux/home/.config/ags/widget/datemenu/NotificationColumn.ts
new file mode 100644
index 0000000..07d6829
--- /dev/null
+++ b/linux/home/.config/ags/widget/datemenu/NotificationColumn.ts
@@ -0,0 +1,113 @@
+import { type Notification as Notif } from "types/service/notifications"
+import Notification from "widget/notifications/Notification"
+import options from "options"
+import icons from "lib/icons"
+
+const notifications = await Service.import("notifications")
+const notifs = notifications.bind("notifications")
+
+const Animated = (n: Notif) => Widget.Revealer({
+ transition_duration: options.transition.value,
+ transition: "slide_down",
+ child: Notification(n),
+ setup: self => Utils.timeout(options.transition.value, () => {
+ if (!self.is_destroyed)
+ self.reveal_child = true
+ }),
+})
+
+const ClearButton = () => Widget.Button({
+ on_clicked: notifications.clear,
+ sensitive: notifs.as(n => n.length > 0),
+ child: Widget.Box({
+ children: [
+ Widget.Label("Clear "),
+ Widget.Icon({
+ icon: notifs.as(n => icons.trash[n.length > 0 ? "full" : "empty"]),
+ }),
+ ],
+ }),
+})
+
+const Header = () => Widget.Box({
+ class_name: "header",
+ children: [
+ Widget.Label({ label: "Notifications", hexpand: true, xalign: 0 }),
+ ClearButton(),
+ ],
+})
+
+const NotificationList = () => {
+ const map: Map<number, ReturnType<typeof Animated>> = new Map
+ const box = Widget.Box({
+ vertical: true,
+ children: notifications.notifications.map(n => {
+ const w = Animated(n)
+ map.set(n.id, w)
+ return w
+ }),
+ visible: notifs.as(n => n.length > 0),
+ })
+
+ function remove(_: unknown, id: number) {
+ const n = map.get(id)
+ if (n) {
+ n.reveal_child = false
+ Utils.timeout(options.transition.value, () => {
+ n.destroy()
+ map.delete(id)
+ })
+ }
+ }
+
+ return box
+ .hook(notifications, remove, "closed")
+ .hook(notifications, (_, id: number) => {
+ if (id !== undefined) {
+ if (map.has(id))
+ remove(null, id)
+
+ const n = notifications.getNotification(id)!
+
+ const w = Animated(n)
+ map.set(id, w)
+ box.children = [w, ...box.children]
+ }
+ }, "notified")
+}
+
+const Placeholder = () => Widget.Box({
+ class_name: "placeholder",
+ vertical: true,
+ vpack: "center",
+ hpack: "center",
+ vexpand: true,
+ hexpand: true,
+ visible: notifs.as(n => n.length === 0),
+ children: [
+ Widget.Icon(icons.notifications.silent),
+ Widget.Label("Your inbox is empty"),
+ ],
+})
+
+export default () => Widget.Box({
+ class_name: "notifications",
+ css: options.notifications.width.bind().as(w => `min-width: ${w}px`),
+ vertical: true,
+ children: [
+ Header(),
+ Widget.Scrollable({
+ vexpand: true,
+ hscroll: "never",
+ class_name: "notification-scrollable",
+ child: Widget.Box({
+ class_name: "notification-list vertical",
+ vertical: true,
+ children: [
+ NotificationList(),
+ Placeholder(),
+ ],
+ }),
+ }),
+ ],
+})
diff --git a/linux/home/.config/ags/widget/datemenu/datemenu.scss b/linux/home/.config/ags/widget/datemenu/datemenu.scss
new file mode 100644
index 0000000..6fd9257
--- /dev/null
+++ b/linux/home/.config/ags/widget/datemenu/datemenu.scss
@@ -0,0 +1,110 @@
+@import "../notifications/notifications.scss";
+
+@mixin calendar {
+ @include widget;
+ padding: $padding*2 $padding*2 0;
+
+ calendar {
+ all: unset;
+
+ &.button {
+ @include button($flat: true);
+ }
+
+ &:selected {
+ box-shadow: inset 0 -8px 0 0 transparentize($primary-bg, 0.5),
+ inset 0 0 0 1px $primary-bg;
+ border-radius: $radius*0.6;
+ }
+
+ &.header {
+ background-color: transparent;
+ border: none;
+ color: transparentize($fg, 0.5);
+ }
+
+ &.highlight {
+ background-color: transparent;
+ color: transparentize($primary-bg, 0.5);
+ }
+
+ &:indeterminate {
+ color: transparentize($fg, 0.9);
+ }
+
+ font-size: 1.1em;
+ padding: .2em;
+ }
+}
+
+window#datemenu .datemenu {
+ @include floating-widget;
+
+ .notifications {
+ .header {
+ margin-bottom: $spacing;
+ margin-right: $spacing;
+
+ >label {
+ margin-left: $radius * .5;
+ }
+
+ button {
+ @include button;
+ padding: $padding*.7 $padding;
+ }
+ }
+
+ .notification-scrollable {
+ @include scrollable($top: true, $bottom: true);
+ }
+
+ .notification-list {
+ margin-right: $spacing;
+ }
+
+ .notification {
+ @include notification;
+ @include widget;
+ padding: $padding;
+ margin-bottom: $spacing;
+ }
+
+ .placeholder {
+ image {
+ font-size: 7em;
+ }
+
+ label {
+ font-size: 1.2em;
+ }
+ }
+ }
+
+
+ separator {
+ background-color: $popover-border-color;
+ border-radius: $radius;
+ margin-right: $spacing;
+ }
+
+ .datemenu {
+ @include spacing;
+ }
+
+ .clock-box {
+ padding: $padding;
+
+ .clock {
+ font-size: 5em;
+ }
+
+ .uptime {
+ color: transparentize($fg, 0.2);
+ }
+ }
+
+ .calendar {
+ @include calendar;
+ }
+}
diff --git a/linux/home/.config/ags/widget/desktop/Desktop.ts b/linux/home/.config/ags/widget/desktop/Desktop.ts
new file mode 100644
index 0000000..f711967
--- /dev/null
+++ b/linux/home/.config/ags/widget/desktop/Desktop.ts
@@ -0,0 +1,40 @@
+import options from "options"
+import { matugen } from "lib/matugen"
+const mpris = await Service.import("mpris")
+
+const pref = () => options.bar.media.preferred.value
+
+export default (monitor: number) => Widget.Window({
+ monitor,
+ layer: "bottom",
+ name: `desktop${monitor}`,
+ class_name: "desktop",
+ anchor: ["top", "bottom", "left", "right"],
+ child: Widget.Box({
+ expand: true,
+ css: options.theme.dark.primary.bg.bind().as(c => `
+ transition: 500ms;
+ background-color: ${c}`),
+ child: Widget.Box({
+ class_name: "wallpaper",
+ expand: true,
+ vpack: "center",
+ hpack: "center",
+ setup: self => self
+ .hook(mpris, () => {
+ const img = mpris.getPlayer(pref())!.cover_path
+ matugen("image", img)
+ Utils.timeout(500, () => self.css = `
+ background-image: url('${img}');
+ background-size: contain;
+ background-repeat: no-repeat;
+ transition: 200ms;
+ min-width: 700px;
+ min-height: 700px;
+ border-radius: 30px;
+ box-shadow: 25px 25px 30px 0 rgba(0,0,0,0.5);`,
+ )
+ }),
+ }),
+ }),
+})
diff --git a/linux/home/.config/ags/widget/dock/Dock.ts b/linux/home/.config/ags/widget/dock/Dock.ts
new file mode 100644
index 0000000..c55f89f
--- /dev/null
+++ b/linux/home/.config/ags/widget/dock/Dock.ts
@@ -0,0 +1,150 @@
+import { launchApp } from "lib/utils";
+import options from "options";
+import * as Gtk from "gi://Gtk?version=3.0";
+import { type ButtonProps } from "types/widgets/button";
+import { type BoxProps } from "types/widgets/box";
+
+const hyprland = await Service.import("hyprland");
+const applications = await Service.import("applications");
+
+const focus = (address: string) => hyprland.messageAsync(`dispatch focuswindow address:${address}`);
+
+const AppButton = ({ icon, pinned = false, term, ...rest }: ButtonProps & { term?: string }): Gtk.Button & ButtonProps => {
+ const { iconSize } = options.dock;
+
+ const buttonBox = Widget.Box({
+ class_name: 'box',
+ child: Widget.Icon({
+ icon,
+ size: iconSize,
+ }),
+ });
+
+ const button = Widget.Button({
+ ...rest,
+ class_name: '',
+ child: pinned ? buttonBox : Widget.Overlay({
+ child: buttonBox,
+ pass_through: false,
+ overlays: [],
+ }),
+ });
+
+ return Object.assign(button, {});
+};
+
+const createAppButton = ({ app, term, ...params }) => {
+ return AppButton({
+ icon: app.icon_name || '',
+ term,
+ ...params,
+ });
+};
+
+const filterValidClients = (clients: any[]) => {
+ return clients.filter(client => (
+ client.mapped &&
+ [client.class, client.title, client.initialClass].every(prop => typeof prop === 'string' && prop !== '')
+ ));
+};
+
+const Taskbar = (): Gtk.Box & BoxProps => {
+ const addedApps = new Set<string>();
+
+ const updateTaskbar = (clients: any[]) => {
+ const validClients = filterValidClients(clients);
+
+ const focusedAddress = hyprland?.active.client?.address;
+ const running = validClients.filter(client => client.mapped);
+ const focused = running.find(client => client.address === focusedAddress);
+
+ return validClients.map(client => {
+ if (![client.class, client.title, client.initialClass].every(prop => typeof prop === 'string' && prop !== '')) {
+ return null;
+ }
+
+ if (addedApps.has(client.title)) {
+ return null;
+ }
+
+ for (const appName of options.dock.pinnedApps.value) {
+ if (!appName || typeof appName !== 'string') {
+ continue;
+ }
+
+ if (client.class.includes(appName) || client.title.includes(appName)
+ || client.initialClass.includes(appName)) {
+ return null;
+ }
+ }
+
+ const matchingApp = applications?.list.find(app => (
+ app.match(client.title) || app.match(client.class) || app.match(client.initialClass)
+ ));
+
+ if (matchingApp) {
+ addedApps.add(client.title);
+ return createAppButton({
+ app: matchingApp,
+ term: matchingApp.title,
+ on_primary_click: () => {
+ const clickAddress = client.address || focusedAddress;
+ clickAddress && focus(clickAddress);
+ },
+ on_secondary_click: () => launchApp(matchingApp),
+ });
+ a
+ }
+ return null;
+ });
+ };
+
+ return Widget.Box({
+ vertical: false,
+ })
+ .bind('children', hyprland, 'clients', updateTaskbar);
+};
+
+const PinnedApps = (): Gtk.Box & BoxProps => {
+ const updatePinnedApps = (pinnedApps: string[]) => {
+ return pinnedApps
+ .map(term => ({ app: applications?.query(term)?.[0], term }))
+ .filter(({ app }) => app)
+ .map(({ app, term = true }) => createAppButton({
+ app,
+ term,
+ pinned: true,
+ on_primary_click: () => {
+ const matchingClients = hyprland?.clients.filter(client => (
+ typeof client.class === 'string' &&
+ typeof client.title === 'string' &&
+ typeof client.initialClass === 'string' &&
+ (client.class.includes(term) || client.title.includes(term) || client.initialClass.includes(term))
+ ));
+
+ if (matchingClients.length > 0) {
+ const { address } = matchingClients[0];
+ address && focus(address);
+ } else {
+ launchApp(app);
+ }
+ },
+ on_secondary_click: () => launchApp(app),
+ }));
+ };
+
+ return Widget.Box({
+ class_name: 'pins',
+ vertical: false,
+ homogeneous: true,
+ })
+ .bind('children', options.dock.pinnedApps, 'value', updatePinnedApps);
+};
+
+const Dock = (): Gtk.Box & BoxProps => Widget.Box({
+ class_name: 'dock',
+ vertical: false,
+ children: [PinnedApps(), Taskbar()],
+});
+
+export default Dock;
diff --git a/linux/home/.config/ags/widget/dock/FloatingDock.ts b/linux/home/.config/ags/widget/dock/FloatingDock.ts
new file mode 100644
index 0000000..369f56f
--- /dev/null
+++ b/linux/home/.config/ags/widget/dock/FloatingDock.ts
@@ -0,0 +1,70 @@
+import options from 'options';
+import Dock from './Dock.ts';
+const hyprland = await Service.import('hyprland');
+const apps = await Service.import('applications');
+
+const { Gdk, Gtk } = imports.gi;
+import type Gtk from 'gi://Gtk?version=3.0';
+import { type WindowProps } from 'types/widgets/window';
+import { type RevealerProps } from 'types/widgets/revealer';
+import { type EventBoxProps } from 'types/widgets/eventbox';
+
+/** @param {number} monitor */
+const FloatingDock = (monitor: number): Gtk.Window & WindowProps => {
+ const update = () => {
+ const ws = Hyprland.getWorkspace(Hyprland.active.workspace.id);
+ if (Hyprland.getMonitor(monitor)?.name === ws?.monitor) self.reveal_child = ws?.windows === 0;
+ };
+ const revealer: Gtk.Revealer & RevealerProps = Widget.Revealer({
+ transition: 'slide_up',
+ transitionDuration: 90,
+ child: Dock(),
+ setup: self => self.hook(hyprland, update, 'client-added').hook(hyprland, update, 'client-removed').hook(hyprland.active.workspace, update),
+ });
+
+ const window = Widget.Window({
+ monitor,
+ //halign: 'fill',
+ halign: 'end',
+ //layer: "overlay",
+ layer: 'dock',
+ name: `dock${monitor}`,
+ click_through: false,
+ class_name: 'floating-dock',
+ // class_name: 'floating-dock-no-gap',
+ // class_name: "f-dock-wrap",
+
+ typeHint: Gdk.WindowTypeHint.DOCK,
+ exclusivity: 'false',
+
+ anchor: ['bottom'],
+ child: Widget.Box({
+ vertical: false,
+ halign: 'bottom',
+ hpack: 'start',
+ children: [
+ revealer,
+ Widget.Box({
+ class_name: 'padding',
+ css: 'padding: 9px; margin: 0;',
+ vertical: false,
+ halign: 'bottom',
+ hpack: 'start',
+ }),
+ ],
+ }),
+ });
+
+ window
+ .on('enter-notify-event', () => {
+ revealer.reveal_child = true;
+ })
+ .on('leave-notify-event', () => {
+ revealer.reveal_child = false;
+ })
+ .bind('visible', options.bar.position, 'value', v => v !== 'left');
+
+ return window;
+};
+
+export default FloatingDock;
diff --git a/linux/home/.config/ags/widget/dock/ToolBox.ts b/linux/home/.config/ags/widget/dock/ToolBox.ts
new file mode 100644
index 0000000..51fda72
--- /dev/null
+++ b/linux/home/.config/ags/widget/dock/ToolBox.ts
@@ -0,0 +1,122 @@
+import options from "options";
+import { sh } from "lib/utils";
+import * as Gtk from "gi://Gtk?version=3.0";
+import { type ButtonProps } from "types/widgets/button";
+import { type BoxProps } from "types/widgets/box";
+
+const hyprland = await Service.import("hyprland");
+const { icons } = options.dock.toolbox;
+const buttonToggles = {};
+
+const dispatch = (action: string, arg: string) => {
+ //console.log(`Performing action: ${action} with argument: ${arg}`);
+ sh(`hyprctl dispatch ${action} ${arg}`);
+};
+
+const keyword = (action: string, arg: string) => {
+ //console.log(`Performing action: ${action} with argument: ${arg}`);
+ sh(`hyprctl keyword ${action} ${arg}`);
+};
+
+const ToggleSwitch = (buttonIndex, actionOn, argOn, actionOff, argOff, actionExec) => {
+ buttonToggles[buttonIndex] = !buttonToggles[buttonIndex];
+ const { action, arg } = buttonToggles[buttonIndex] ? { action: actionOn, arg: argOn } : { action: actionOff, arg: argOff };
+ actionExec(action, arg);
+};
+
+const ToggleOnMulti = (buttonIndex, actionOn, argOn, actionOff, argOff, actionExec) => {
+ buttonToggles[buttonIndex] = !buttonToggles[buttonIndex];
+ if (buttonToggles[buttonIndex]) {
+ actionOn.forEach(({ action, arg }) => {
+ actionExec(action, arg);
+ });
+ } else {
+ actionExec(actionOff, argOff);
+ }
+};
+
+const execAction = (trigger, actionIndex, actionOn, argOn, actionOff, argOff, action, arg, actionExec) => {
+ switch (trigger) {
+ case 'toggleOn-multi':
+ ToggleOnMulti(actionIndex, actionOn, argOn, actionOff, argOff, actionExec);
+ break;
+ case 'toggle-switch':
+ ToggleSwitch(actionIndex, actionOn, argOn, actionOff, argOff, actionExec);
+ break;
+ case 'oneshot':
+ actionExec(action, arg);
+ break;
+ default:
+ break;
+ }
+};
+
+const buttonConfigs = [
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 0, action: 'killactive', arg: '' },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 1, action: 'exec hyprctl', arg: 'kill' },
+ {
+ actionExec: keyword,
+ trigger: 'toggle-switch',
+ actionIndex: 2,
+ actionOn: 'monitor', argOn: 'eDP-1,2736x1824,0x0,0,transform,1',
+ actionOff: 'monitor', argOff: 'eDP-1,2736x1824,0x0,0,transform,0'
+ },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 3, action: 'workspace', arg: 'r-1' },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 4, action: 'workspace', arg: 'r+1' },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 5, action: 'movewindow', arg: 'l' },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 6, action: 'movewindow', arg: 'r' },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 7, action: 'movewindow', arg: 'u' },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 8, action: 'movewindow', arg: 'd' },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 9, action: 'swapnext', arg: 'next' },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 10, action: 'togglesplit', arg: '' },
+ {
+ actionExec: dispatch,
+ trigger: 'toggleOn-multi',
+ actionIndex: 11,
+ actionOn: [
+ { action: 'setfloating', arg: 'active' },
+ { action: 'resizeactive', arg: 'exact 90% 90%' },
+ { action: 'centerwindow', arg: '' },
+ ],
+ actionOff: 'settiled', argOff: 'active'
+ },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 12, action: 'pin', arg: '' },
+ { actionExec: dispatch, trigger: 'oneshot', actionIndex: 13, action: 'fullscreen', arg: '0' },
+ {
+ actionExec: dispatch,
+ trigger: 'toggle-switch',
+ actionIndex: 14,
+ actionOn: 'exec', argOn: 'wvctl 1',
+ actionOff: 'exec', argOff: 'wvctl 0'
+ },
+];
+
+const ToolBox = () => {
+ const ToolBoxButtons = () => {
+ const buttons = buttonConfigs.map(({ actionIndex, actionOn, argOn, actionOff, argOff, actionExec, trigger, action, arg }) => {
+ const execActionWrapper = () => execAction(trigger, actionIndex, actionOn, argOn, actionOff, argOff, action, arg, actionExec);
+
+ return Widget.Button({
+ child: Widget.Icon({
+ icon: icons[actionIndex].bind(),
+ }),
+ on_clicked: execActionWrapper,
+ });
+ });
+
+ return Widget.Box({
+ vertical: true,
+ homogeneous: true,
+ children: buttons,
+ });
+ };
+
+ return Widget.Box({
+ class_name: "toolbox",
+ vertical: true,
+ homogeneous: true,
+ children: [ToolBoxButtons()],
+ });
+};
+
+export default ToolBox;
diff --git a/linux/home/.config/ags/widget/dock/ToolBoxDock.ts b/linux/home/.config/ags/widget/dock/ToolBoxDock.ts
new file mode 100644
index 0000000..21beaeb
--- /dev/null
+++ b/linux/home/.config/ags/widget/dock/ToolBoxDock.ts
@@ -0,0 +1,57 @@
+import options from "options";
+import ToolBox from "./ToolBox.ts";
+const hyprland = await Service.import("hyprland");
+const apps = await Service.import("applications");
+
+import type Gtk from "gi://Gtk?version=3.0";
+import { type WindowProps } from "types/widgets/window";
+import { type RevealerProps } from "types/widgets/revealer";
+import { type EventBoxProps } from "types/widgets/eventbox";
+
+/** @param {number} monitor */
+const ToolBoxDock = (monitor: number): Gtk.Window & WindowProps => {
+
+ const revealer: Gtk.Revealer & RevealerProps = Widget.Revealer({
+ transition: 'slide_left',
+ transitionDuration: 50,
+ child: ToolBox(),
+ });
+
+ const window = Widget.Window({
+ monitor,
+ halign: 'fill',
+ layer: "overlay",
+ name: `toolbox${monitor}`,
+ click_through: false,
+ class_name: 'floating-toolbox',
+ anchor: ['right'],
+ child: Widget.Box({
+ vertical: true,
+ halign: 'top',
+ hpack: 'fill',
+ children: [
+ revealer,
+ Widget.Box({
+ class_name: 'padding',
+ css: 'padding: 14px;',
+ vertical: true,
+ halign: 'top',
+ hpack: 'fill',
+ }),
+ ],
+ }),
+ });
+
+ window
+ .on('enter-notify-event', () => {
+ revealer.reveal_child = true;
+ })
+ .on('leave-notify-event', () => {
+ revealer.reveal_child = false;
+ })
+ .bind('visible', options.bar.position, 'value', v => v !== 'left');
+
+ return window;
+};
+
+export default ToolBoxDock;
diff --git a/linux/home/.config/ags/widget/dock/dock.scss b/linux/home/.config/ags/widget/dock/dock.scss
new file mode 100644
index 0000000..9dc6256
--- /dev/null
+++ b/linux/home/.config/ags/widget/dock/dock.scss
@@ -0,0 +1,73 @@
+@use 'sass:color';
+
+.floating-dock {
+ padding-left: 0.2rem;
+ padding-right: 0.2rem;
+ padding-top: 0.3rem;
+ padding-bottom: 0.3rem;
+ border-radius: 1rem;
+}
+
+.dock {
+ // @include floating-widget;
+ border-radius: $radius;
+ background-color: transparentize($bg, 0.07);
+ min-width: 0;
+ padding: 6;
+
+ // Common styles for both PinnedApps and Taskbar buttons
+ button {
+ @include button($flat: true);
+ border-radius: 4;
+ padding: 2;
+
+ .box {
+ margin: 0em;
+ min-width: 1em;
+ min-height: 0em;
+ padding: 0;
+ }
+
+ image {
+ margin: 0px;
+ }
+
+ .indicator {
+ background-color: transparentize($primary-bg, 0.3);
+ border-radius: $radius;
+ min-height: 1pt;
+ min-width: 16pt;
+ margin: 1pt;
+ }
+ }
+}
+
+.toolbox {
+ // @include floating-widget;
+ border-radius: $radius;
+ background-color: transparentize($bg, 0.07);
+ min-width: 0;
+ padding: 6;
+
+ // Common styles for both PinnedApps and Taskbar buttons
+ button {
+ @include button($flat: true);
+ border-radius: 0;
+ padding: 0;
+
+ image {
+ margin: 0px;
+ font-size: 32px;
+ }
+
+ &:hover,
+ &:active,
+ &:focus {
+ // Override hover, active, and focus styles for buttons in .toolbox
+ background-color: transparent;
+ border: none;
+ box-shadow: none;
+ outline: none;
+ }
+ }
+}
diff --git a/linux/home/.config/ags/widget/launcher/AppLauncher.ts b/linux/home/.config/ags/widget/launcher/AppLauncher.ts
new file mode 100644
index 0000000..08258de
--- /dev/null
+++ b/linux/home/.config/ags/widget/launcher/AppLauncher.ts
@@ -0,0 +1,125 @@
+import { type Application } from 'types/service/applications';
+import { launchApp, icon } from 'lib/utils';
+import options from 'options';
+import icons from 'lib/icons';
+
+const apps = await Service.import('applications');
+const { query } = apps;
+const { iconSize } = options.launcher.apps;
+
+const QuickAppButton = (app: Application) =>
+ Widget.Button({
+ hexpand: true,
+ tooltip_text: app.name,
+ on_clicked: () => {
+ App.closeWindow('launcher');
+ launchApp(app);
+ },
+ child: Widget.Icon({
+ size: iconSize.bind(),
+ icon: icon(app.icon_name, icons.fallback.executable),
+ }),
+ });
+
+const AppItem = (app: Application) => {
+ const title = Widget.Label({
+ class_name: 'title',
+ label: app.name,
+ hexpand: true,
+ xalign: 0,
+ vpack: 'center',
+ truncate: 'end',
+ });
+
+ const description = Widget.Label({
+ class_name: 'description',
+ label: app.description || '',
+ hexpand: true,
+ wrap: true,
+ max_width_chars: 30,
+ xalign: 0,
+ justification: 'left',
+ vpack: 'center',
+ });
+
+ const appicon = Widget.Icon({
+ icon: icon(app.icon_name, icons.fallback.executable),
+ size: iconSize.bind(),
+ });
+
+ const textBox = Widget.Box({
+ vertical: true,
+ vpack: 'center',
+ children: app.description ? [title, description] : [title],
+ });
+
+ return Widget.Button({
+ class_name: 'app-item',
+ attribute: { app },
+ child: Widget.Box({
+ children: [appicon, textBox],
+ }),
+ on_clicked: () => {
+ App.closeWindow('launcher');
+ launchApp(app);
+ },
+ });
+};
+export function Favorites() {
+ const favs = options.launcher.apps.favorites.bind();
+ return Widget.Revealer({
+ visible: favs.as(f => f.length > 0),
+ child: Widget.Box({
+ vertical: true,
+ children: favs.as(favs =>
+ favs.flatMap(fs => [
+ Widget.Separator(),
+ Widget.Box({
+ class_name: 'quicklaunch horizontal',
+ children: fs
+ .map(f => query(f)?.[0])
+ .filter(f => f)
+ .map(QuickAppButton),
+ }),
+ ]),
+ ),
+ }),
+ });
+}
+
+export function Launcher() {
+ const applist = Variable(query(''));
+ const max = options.launcher.apps.max;
+ let first = applist.value[0];
+
+ function SeparatedAppItem(app: Application) {
+ return Widget.Revealer({ attribute: { app } }, Widget.Box({ vertical: true }, Widget.Separator(), AppItem(app)));
+ }
+
+ const list = Widget.Box({
+ vertical: true,
+ children: applist.bind().as(list => list.map(SeparatedAppItem)),
+ setup: self => self.hook(apps, () => (applist.value = query('')), 'notify::frequents'),
+ });
+
+ return Object.assign(list, {
+ filter(text: string | null) {
+ first = query(text || '')[0];
+ list.children.reduce((i, item) => {
+ if (!text || i >= max.value) {
+ item.reveal_child = false;
+ return i;
+ }
+ if (item.attribute.app.match(text)) {
+ item.reveal_child = true;
+ return ++i;
+ }
+ item.reveal_child = false;
+ return i;
+ }, 0);
+ },
+ launchFirst() {
+ launchApp(first);
+ },
+ });
+}
diff --git a/linux/home/.config/ags/widget/launcher/Launcher.ts b/linux/home/.config/ags/widget/launcher/Launcher.ts
new file mode 100644
index 0000000..90b4d58
--- /dev/null
+++ b/linux/home/.config/ags/widget/launcher/Launcher.ts
@@ -0,0 +1,134 @@
+import { type Binding } from 'lib/utils';
+import PopupWindow, { Padding } from 'widget/PopupWindow';
+import icons from 'lib/icons';
+import options from 'options';
+import nix from 'service/nix';
+import * as AppLauncher from './AppLauncher';
+import * as NixRun from './NixRun';
+import * as ShRun from './ShRun';
+
+const { width, margin } = options.launcher;
+const isnix = nix.available;
+
+function Launcher() {
+ const favs = AppLauncher.Favorites();
+ const applauncher = AppLauncher.Launcher();
+ const sh = ShRun.ShRun();
+ const shicon = ShRun.Icon();
+ const nix = NixRun.NixRun();
+ const nixload = NixRun.Spinner();
+
+ function HelpButton(cmd: string, desc: string | Binding<string>) {
+ return Widget.Box(
+ { vertical: true },
+ Widget.Separator(),
+ Widget.Button(
+ {
+ class_name: 'help',
+ on_clicked: () => {
+ entry.grab_focus();
+ entry.text = `:${cmd} `;
+ entry.set_position(-1);
+ },
+ },
+ Widget.Box([
+ Widget.Label({
+ class_name: 'name',
+ label: `:${cmd}`,
+ }),
+ Widget.Label({
+ hexpand: true,
+ hpack: 'end',
+ class_name: 'description',
+ label: desc,
+ }),
+ ]),
+ ),
+ );
+ }
+
+ const help = Widget.Revealer({
+ child: Widget.Box(
+ { vertical: true },
+ HelpButton('sh', 'run a binary'),
+ isnix
+ ? HelpButton(
+ 'nx',
+ options.launcher.nix.pkgs.bind().as(pkg => `run a nix package from ${pkg}`),
+ )
+ : Widget.Box(),
+ ),
+ });
+
+ const entry = Widget.Entry({
+ hexpand: true,
+ primary_icon_name: icons.ui.search,
+ on_accept: ({ text }) => {
+ if (text?.startsWith(':nx')) nix.run(text.substring(3));
+ else if (text?.startsWith(':sh')) sh.run(text.substring(3));
+ else applauncher.launchFirst();
+
+ App.toggleWindow('launcher');
+ entry.text = '';
+ },
+ on_change: ({ text }) => {
+ text ||= '';
+ favs.reveal_child = text === '';
+ help.reveal_child = text.split(' ').length === 1 && text?.startsWith(':');
+
+ if (text?.startsWith(':nx')) nix.filter(text.substring(3));
+ else nix.filter('');
+
+ if (text?.startsWith(':sh')) sh.filter(text.substring(3));
+ else sh.filter('');
+
+ if (!text?.startsWith(':')) applauncher.filter(text);
+ },
+ });
+
+ function focus() {
+ entry.text = '';
+ entry.set_position(-1);
+ entry.select_region(0, -1);
+ entry.grab_focus();
+ favs.reveal_child = true;
+ }
+
+ const layout = Widget.Box({
+ css: width.bind().as(v => `min-width: ${v}pt;`),
+ class_name: 'launcher',
+ vertical: true,
+ vpack: 'start',
+ setup: self =>
+ self.hook(App, (_, win, visible) => {
+ if (win !== 'launcher') return;
+
+ entry.text = '';
+ if (visible) focus();
+ }),
+ children: [
+ Widget.Box([entry, nixload, shicon]),
+ favs,
+ help,
+ applauncher,
+ //nix,
+ sh,
+ ],
+ });
+
+ return Widget.Box(
+ { vertical: true, css: 'padding: 1px' },
+ Padding('applauncher', {
+ css: margin.bind().as(v => `min-height: ${v}pt;`),
+ vexpand: false,
+ }),
+ layout,
+ );
+}
+
+export default () =>
+ PopupWindow({
+ name: 'launcher',
+ layout: 'top',
+ child: Launcher(),
+ });
diff --git a/linux/home/.config/ags/widget/launcher/NixRun.ts b/linux/home/.config/ags/widget/launcher/NixRun.ts
new file mode 100644
index 0000000..cec9e09
--- /dev/null
+++ b/linux/home/.config/ags/widget/launcher/NixRun.ts
@@ -0,0 +1,118 @@
+import icons from "lib/icons"
+import nix, { type Nixpkg } from "service/nix"
+
+const iconVisible = Variable(false)
+
+function Item(pkg: Nixpkg) {
+ const name = Widget.Label({
+ class_name: "name",
+ label: pkg.name.split(".").at(-1),
+ })
+
+ const subpkg = pkg.name.includes(".") ? Widget.Label({
+ class_name: "description",
+ hpack: "end",
+ hexpand: true,
+ label: ` ${pkg.name.split(".").slice(0, -1).join(".")}`,
+ }) : null
+
+ const version = Widget.Label({
+ class_name: "version",
+ label: pkg.version,
+ hexpand: true,
+ hpack: "end",
+ })
+
+ const description = pkg.description ? Widget.Label({
+ class_name: "description",
+ label: pkg.description,
+ justification: "left",
+ wrap: true,
+ hpack: "start",
+ max_width_chars: 40,
+ }) : null
+
+ return Widget.Box(
+ {
+ attribute: { name: pkg.name },
+ vertical: true,
+ },
+ Widget.Separator(),
+ Widget.Button(
+ {
+ class_name: "nix-item",
+ on_clicked: () => {
+ nix.run(pkg.name)
+ App.closeWindow("launcher")
+ },
+ },
+ Widget.Box(
+ { vertical: true },
+ Widget.Box([name, version]),
+ Widget.Box([
+ description as ReturnType<typeof Widget.Label>,
+ subpkg as ReturnType<typeof Widget.Label>,
+ ]),
+ ),
+ ),
+ )
+}
+
+export function Spinner() {
+ const icon = Widget.Icon({
+ icon: icons.nix.nix,
+ class_name: "spinner",
+ css: `
+ @keyframes spin {
+ to { -gtk-icon-transform: rotate(1turn); }
+ }
+
+ image.spinning {
+ animation-name: spin;
+ animation-duration: 1s;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ }
+ `,
+ setup: self => self.hook(nix, () => {
+ self.toggleClassName("spinning", !nix.ready)
+ }),
+ })
+
+ return Widget.Revealer({
+ transition: "slide_left",
+ child: icon,
+ reveal_child: Utils.merge([
+ nix.bind("ready"),
+ iconVisible.bind(),
+ ], (ready, show) => !ready || show),
+ })
+}
+
+export function NixRun() {
+ const list = Widget.Box<ReturnType<typeof Item>>({
+ vertical: true,
+ })
+
+ const revealer = Widget.Revealer({
+ child: list,
+ })
+
+ async function filter(term: string) {
+ iconVisible.value = Boolean(term)
+
+ if (!term)
+ revealer.reveal_child = false
+
+ if (term.trim()) {
+ const found = await nix.query(term)
+ list.children = found.map(k => Item(nix.db[k]))
+ revealer.reveal_child = true
+ }
+ }
+
+ return Object.assign(revealer, {
+ filter,
+ run: nix.run,
+ })
+}
diff --git a/linux/home/.config/ags/widget/launcher/ShRun.ts b/linux/home/.config/ags/widget/launcher/ShRun.ts
new file mode 100644
index 0000000..c4215ef
--- /dev/null
+++ b/linux/home/.config/ags/widget/launcher/ShRun.ts
@@ -0,0 +1,89 @@
+import icons from "lib/icons"
+import options from "options"
+import { bash, dependencies } from "lib/utils"
+
+const iconVisible = Variable(false)
+
+const MAX = options.launcher.sh.max
+const BINS = `${Utils.CACHE_DIR}/binaries`
+bash("{ IFS=:; ls -H $PATH; } | sort ")
+ .then(bins => Utils.writeFile(bins, BINS))
+
+async function query(filter: string) {
+ if (!dependencies("fzf"))
+ return [] as string[]
+
+ return bash(`cat ${BINS} | fzf -f ${filter} | head -n ${MAX}`)
+ .then(str => Array.from(new Set(str.split("\n").filter(i => i)).values()))
+ .catch(err => { print(err); return [] })
+}
+
+function run(args: string) {
+ Utils.execAsync(args)
+ .then(out => {
+ print(`:sh ${args.trim()}:`)
+ print(out)
+ })
+ .catch(err => {
+ Utils.notify("ShRun Error", err, icons.app.terminal)
+ })
+}
+
+function Item(bin: string) {
+ return Widget.Box(
+ {
+ attribute: { bin },
+ vertical: true,
+ },
+ Widget.Separator(),
+ Widget.Button({
+ child: Widget.Label({
+ label: bin,
+ hpack: "start",
+ }),
+ class_name: "sh-item",
+ on_clicked: () => {
+ Utils.execAsync(bin)
+ App.closeWindow("launcher")
+ },
+ }),
+ )
+}
+
+export function Icon() {
+ const icon = Widget.Icon({
+ icon: icons.app.terminal,
+ class_name: "spinner",
+ })
+
+ return Widget.Revealer({
+ transition: "slide_left",
+ child: icon,
+ reveal_child: iconVisible.bind(),
+ })
+}
+
+export function ShRun() {
+ const list = Widget.Box<ReturnType<typeof Item>>({
+ vertical: true,
+ })
+
+ const revealer = Widget.Revealer({
+ child: list,
+ })
+
+ async function filter(term: string) {
+ iconVisible.value = Boolean(term)
+
+ if (!term)
+ revealer.reveal_child = false
+
+ if (term.trim()) {
+ const found = await query(term)
+ list.children = found.map(Item)
+ revealer.reveal_child = true
+ }
+ }
+
+ return Object.assign(revealer, { filter, run })
+}
diff --git a/linux/home/.config/ags/widget/launcher/launcher.scss b/linux/home/.config/ags/widget/launcher/launcher.scss
new file mode 100644
index 0000000..926abc3
--- /dev/null
+++ b/linux/home/.config/ags/widget/launcher/launcher.scss
@@ -0,0 +1,143 @@
+@use "sass:math";
+@use "sass:color";
+
+window#launcher .launcher {
+ @include floating_widget;
+
+ .quicklaunch {
+ @include spacing;
+
+ button {
+ @include button($flat: true);
+ padding: $padding;
+ }
+ }
+
+ entry {
+ @include button;
+ padding: $padding;
+ margin: $spacing;
+
+ selection {
+ color: color.mix($fg, $bg, 50%);
+ background-color: transparent;
+ }
+
+ label,
+ image {
+ color: $fg;
+ }
+ }
+
+ image.spinner {
+ color: $primary-bg;
+ margin-right: $spacing;
+ }
+
+ separator {
+ margin: 4pt 0;
+ background-color: $popover-border-color;
+ }
+
+ button.app-item {
+ @include button($flat: true, $reactive: false);
+
+ >box {
+ @include spacing(0.5);
+ }
+
+ transition: $transition;
+ padding: $padding;
+
+ label {
+ transition: $transition;
+
+ &.title {
+ color: $fg;
+ }
+
+ &.description {
+ color: transparentize($fg, 0.3);
+ }
+ }
+
+ image {
+ transition: $transition;
+ }
+
+ &:hover,
+ &:focus {
+ .title {
+ color: $primary-bg;
+ }
+
+ .description {
+ color: transparentize($primary-bg, .4);
+ }
+
+ image {
+ -gtk-icon-shadow: 2px 2px $primary-bg;
+ }
+ }
+
+ &:active {
+ background-color: transparentize($primary-bg, 0.5);
+ border-radius: $radius;
+ box-shadow: inset 0 0 0 $border-width $border-color;
+
+ .title {
+ color: $fg;
+ }
+ }
+ }
+
+ button.help,
+ button.nix-item {
+ @include button($flat: true, $reactive: false);
+ padding: 0 ($padding * .5);
+
+ label {
+ transition: $transition;
+ color: $fg;
+ }
+
+ .name {
+ font-size: 1.2em;
+ font-weight: bold;
+ }
+
+ .description {
+ color: transparentize($fg, .3)
+ }
+
+ &:hover,
+ &:focus {
+ label {
+ text-shadow: $text-shadow;
+ }
+
+ .name,
+ .version {
+ color: $primary-bg;
+ }
+
+ .description {
+ color: transparentize($primary-bg, .3)
+ }
+ }
+ }
+
+ button.sh-item {
+ @include button($flat: true, $reactive: false);
+ padding: 0 ($padding * .5);
+
+ transition: $transition;
+ color: $fg;
+
+ &:hover,
+ &:focus {
+ color: $primary-bg;
+ text-shadow: $text-shadow;
+ }
+ }
+}
diff --git a/linux/home/.config/ags/widget/notifications/Notification.ts b/linux/home/.config/ags/widget/notifications/Notification.ts
new file mode 100644
index 0000000..c1c8dd8
--- /dev/null
+++ b/linux/home/.config/ags/widget/notifications/Notification.ts
@@ -0,0 +1,138 @@
+import { type Notification } from "types/service/notifications"
+import GLib from "gi://GLib"
+import icons from "lib/icons"
+
+const time = (time: number, format = "%H:%M") => GLib.DateTime
+ .new_from_unix_local(time)
+ .format(format)
+
+const NotificationIcon = ({ app_entry, app_icon, image }: Notification) => {
+ if (image) {
+ return Widget.Box({
+ vpack: "start",
+ hexpand: false,
+ class_name: "icon img",
+ css: `
+ background-image: url("${image}");
+ background-size: cover;
+ background-repeat: no-repeat;
+ background-position: center;
+ min-width: 78px;
+ min-height: 78px;
+ `,
+ })
+ }
+
+ let icon = icons.fallback.notification
+ if (Utils.lookUpIcon(app_icon))
+ icon = app_icon
+
+ if (Utils.lookUpIcon(app_entry || ""))
+ icon = app_entry || ""
+
+ return Widget.Box({
+ vpack: "start",
+ hexpand: false,
+ class_name: "icon",
+ css: `
+ min-width: 78px;
+ min-height: 78px;
+ `,
+ child: Widget.Icon({
+ icon,
+ size: 58,
+ hpack: "center", hexpand: true,
+ vpack: "center", vexpand: true,
+ }),
+ })
+}
+
+export default (notification: Notification) => {
+ const content = Widget.Box({
+ class_name: "content",
+ children: [
+ NotificationIcon(notification),
+ Widget.Box({
+ hexpand: true,
+ vertical: true,
+ children: [
+ Widget.Box({
+ children: [
+ Widget.Label({
+ class_name: "title",
+ xalign: 0,
+ justification: "left",
+ hexpand: true,
+ max_width_chars: 24,
+ truncate: "end",
+ wrap: true,
+ label: notification.summary.trim(),
+ use_markup: true,
+ }),
+ Widget.Label({
+ class_name: "time",
+ vpack: "start",
+ label: time(notification.time),
+ }),
+ Widget.Button({
+ class_name: "close-button",
+ vpack: "start",
+ child: Widget.Icon("window-close-symbolic"),
+ on_clicked: notification.close,
+ }),
+ ],
+ }),
+ Widget.Label({
+ class_name: "description",
+ hexpand: true,
+ use_markup: true,
+ xalign: 0,
+ justification: "left",
+ label: notification.body.trim(),
+ max_width_chars: 24,
+ wrap: true,
+ }),
+ ],
+ }),
+ ],
+ })
+
+ const actionsbox = notification.actions.length > 0 ? Widget.Revealer({
+ transition: "slide_down",
+ child: Widget.EventBox({
+ child: Widget.Box({
+ class_name: "actions horizontal",
+ children: notification.actions.map(action => Widget.Button({
+ class_name: "action-button",
+ on_clicked: () => notification.invoke(action.id),
+ hexpand: true,
+ child: Widget.Label(action.label),
+ })),
+ }),
+ }),
+ }) : null
+
+ const eventbox = Widget.EventBox({
+ vexpand: false,
+ on_primary_click: notification.dismiss,
+ on_hover() {
+ if (actionsbox)
+ actionsbox.reveal_child = true
+ },
+ on_hover_lost() {
+ if (actionsbox)
+ actionsbox.reveal_child = true
+
+ notification.dismiss()
+ },
+ child: Widget.Box({
+ vertical: true,
+ children: actionsbox ? [content, actionsbox] : [content],
+ }),
+ })
+
+ return Widget.Box({
+ class_name: `notification ${notification.urgency}`,
+ child: eventbox,
+ })
+}
diff --git a/linux/home/.config/ags/widget/notifications/NotificationPopups.ts b/linux/home/.config/ags/widget/notifications/NotificationPopups.ts
new file mode 100644
index 0000000..a4a2b54
--- /dev/null
+++ b/linux/home/.config/ags/widget/notifications/NotificationPopups.ts
@@ -0,0 +1,90 @@
+import Notification from "./Notification"
+import options from "options"
+
+const notifications = await Service.import("notifications")
+const { transition } = options
+const { position } = options.notifications
+const { timeout, idle } = Utils
+
+function Animated(id: number) {
+ const n = notifications.getNotification(id)!
+ const widget = Notification(n)
+
+ const inner = Widget.Revealer({
+ transition: "slide_left",
+ transition_duration: transition.value,
+ child: widget,
+ })
+
+ const outer = Widget.Revealer({
+ transition: "slide_down",
+ transition_duration: transition.value,
+ child: inner,
+ })
+
+ const box = Widget.Box({
+ hpack: "end",
+ child: outer,
+ })
+
+ idle(() => {
+ outer.reveal_child = true
+ timeout(transition.value, () => {
+ inner.reveal_child = true
+ })
+ })
+
+ return Object.assign(box, {
+ dismiss() {
+ inner.reveal_child = false
+ timeout(transition.value, () => {
+ outer.reveal_child = false
+ timeout(transition.value, () => {
+ box.destroy()
+ })
+ })
+ },
+ })
+}
+
+function PopupList() {
+ const map: Map<number, ReturnType<typeof Animated>> = new Map
+ const box = Widget.Box({
+ hpack: "end",
+ vertical: true,
+ css: options.notifications.width.bind().as(w => `min-width: ${w}px;`),
+ })
+
+ function remove(_: unknown, id: number) {
+ map.get(id)?.dismiss()
+ map.delete(id)
+ }
+
+ return box
+ .hook(notifications, (_, id: number) => {
+ if (id !== undefined) {
+ if (map.has(id))
+ remove(null, id)
+
+ if (notifications.dnd)
+ return
+
+ const w = Animated(id)
+ map.set(id, w)
+ box.children = [w, ...box.children]
+ }
+ }, "notified")
+ .hook(notifications, remove, "dismissed")
+ .hook(notifications, remove, "closed")
+}
+
+export default (monitor: number) => Widget.Window({
+ monitor,
+ name: `notifications${monitor}`,
+ anchor: position.bind(),
+ class_name: "notifications",
+ child: Widget.Box({
+ css: "padding: 2px;",
+ child: PopupList(),
+ }),
+})
diff --git a/linux/home/.config/ags/widget/notifications/notifications.scss b/linux/home/.config/ags/widget/notifications/notifications.scss
new file mode 100644
index 0000000..369932f
--- /dev/null
+++ b/linux/home/.config/ags/widget/notifications/notifications.scss
@@ -0,0 +1,79 @@
+@mixin notification() {
+ &.critical {
+ box-shadow: inset 0 0 0.5em 0 $error-bg;
+ }
+
+ &:hover button.close-button {
+ @include button-hover;
+ background-color: transparentize($error-bg, 0.5);
+ }
+
+ .content {
+ .title {
+ margin-right: $spacing;
+ color: $fg;
+ font-size: 1.1em;
+ }
+
+ .time {
+ color: transparentize($fg, 0.2);
+ }
+
+ .description {
+ font-size: 0.9em;
+ color: transparentize($fg, 0.2);
+ }
+
+ .icon {
+ border-radius: $radius * 0.8;
+ margin-right: $spacing;
+
+ &.img {
+ border: $border;
+ }
+ }
+ }
+
+ box.actions {
+ @include spacing(0.5);
+ margin-top: $spacing;
+
+ button {
+ @include button;
+ border-radius: $radius * 0.8;
+ font-size: 1.2em;
+ padding: $padding * 0.7;
+ }
+ }
+
+ button.close-button {
+ @include button($flat: true);
+ margin-left: $spacing / 2;
+ border-radius: $radius * 0.8;
+ min-width: 1.2em;
+ min-height: 1.2em;
+
+ &:hover {
+ background-color: transparentize($error-bg, 0.2);
+ }
+
+ &:active {
+ background-image: none;
+ background-color: $error-bg;
+ }
+ }
+}
+
+window.notifications {
+ @include unset;
+
+ .notification {
+ @include notification;
+ @include floating-widget;
+ border-radius: $radius;
+
+ .description {
+ min-width: 200px;
+ }
+ }
+}
diff --git a/linux/home/.config/ags/widget/osd/OSD.ts b/linux/home/.config/ags/widget/osd/OSD.ts
new file mode 100644
index 0000000..8239a08
--- /dev/null
+++ b/linux/home/.config/ags/widget/osd/OSD.ts
@@ -0,0 +1,111 @@
+import { icon } from "lib/utils"
+import icons from "lib/icons"
+import Progress from "./Progress"
+import brightness from "service/brightness"
+import options from "options"
+
+const audio = await Service.import("audio")
+const { progress, microphone } = options.osd
+
+const DELAY = 2500
+
+function OnScreenProgress(vertical: boolean) {
+ const indicator = Widget.Icon({
+ size: 42,
+ vpack: "start",
+ })
+ const progress = Progress({
+ vertical,
+ width: vertical ? 42 : 300,
+ height: vertical ? 300 : 42,
+ child: indicator,
+ })
+
+ const revealer = Widget.Revealer({
+ transition: "slide_left",
+ child: progress,
+ })
+
+ let count = 0
+ function show(value: number, icon: string) {
+ revealer.reveal_child = true
+ indicator.icon = icon
+ progress.setValue(value)
+ count++
+ Utils.timeout(DELAY, () => {
+ count--
+
+ if (count === 0)
+ revealer.reveal_child = false
+ })
+ }
+
+ return revealer
+ .hook(brightness, () => show(
+ brightness.screen,
+ icons.brightness.screen,
+ ), "notify::screen")
+ .hook(brightness, () => show(
+ brightness.kbd,
+ icons.brightness.keyboard,
+ ), "notify::kbd")
+ .hook(audio.speaker, () => show(
+ audio.speaker.volume,
+ icon(audio.speaker.icon_name || "", icons.audio.type.speaker),
+ ), "notify::volume")
+}
+
+function MicrophoneMute() {
+ const icon = Widget.Icon({
+ class_name: "microphone",
+ })
+
+ const revealer = Widget.Revealer({
+ transition: "slide_up",
+ child: icon,
+ })
+
+ let count = 0
+ let mute = audio.microphone.stream?.is_muted ?? false
+
+ return revealer.hook(audio.microphone, () => Utils.idle(() => {
+ if (mute !== audio.microphone.stream?.is_muted) {
+ mute = audio.microphone.stream!.is_muted
+ icon.icon = icons.audio.mic[mute ? "muted" : "high"]
+ revealer.reveal_child = true
+ count++
+
+ Utils.timeout(DELAY, () => {
+ count--
+ if (count === 0)
+ revealer.reveal_child = false
+ })
+ }
+ }))
+}
+
+export default (monitor: number) => Widget.Window({
+ monitor,
+ name: `indicator${monitor}`,
+ class_name: "indicator",
+ layer: "overlay",
+ click_through: true,
+ anchor: ["right", "left", "top", "bottom"],
+ child: Widget.Box({
+ css: "padding: 2px;",
+ expand: true,
+ child: Widget.Overlay(
+ { child: Widget.Box({ expand: true }) },
+ Widget.Box({
+ hpack: progress.pack.h.bind(),
+ vpack: progress.pack.v.bind(),
+ child: progress.vertical.bind().as(OnScreenProgress),
+ }),
+ Widget.Box({
+ hpack: microphone.pack.h.bind(),
+ vpack: microphone.pack.v.bind(),
+ child: MicrophoneMute(),
+ }),
+ ),
+ }),
+})
diff --git a/linux/home/.config/ags/widget/osd/Progress.ts b/linux/home/.config/ags/widget/osd/Progress.ts
new file mode 100644
index 0000000..bcf27da
--- /dev/null
+++ b/linux/home/.config/ags/widget/osd/Progress.ts
@@ -0,0 +1,74 @@
+import type Gtk from "gi://Gtk?version=3.0"
+import GLib from "gi://GLib?version=2.0"
+import { range } from "lib/utils"
+import options from "options"
+
+type ProgressProps = {
+ height?: number
+ width?: number
+ vertical?: boolean
+ child: Gtk.Widget
+}
+
+export default ({
+ height = 18,
+ width = 180,
+ vertical = false,
+ child,
+}: ProgressProps) => {
+ const fill = Widget.Box({
+ class_name: "fill",
+ hexpand: vertical,
+ vexpand: !vertical,
+ hpack: vertical ? "fill" : "start",
+ vpack: vertical ? "end" : "fill",
+ child,
+ })
+
+ const container = Widget.Box({
+ class_name: "progress",
+ child: fill,
+ css: `
+ min-width: ${width}px;
+ min-height: ${height}px;
+ `,
+ })
+
+ let fill_size = 0
+ let animations: number[] = []
+
+ return Object.assign(container, {
+ setValue(value: number) {
+ if (value < 0)
+ return
+
+ if (animations.length > 0) {
+ for (const id of animations)
+ GLib.source_remove(id)
+
+ animations = []
+ }
+
+ const axis = vertical ? "height" : "width"
+ const axisv = vertical ? height : width
+ const min = vertical ? width : height
+ const preferred = (axisv - min) * value + min
+
+ if (!fill_size) {
+ fill_size = preferred
+ fill.css = `min-${axis}: ${preferred}px;`
+ return
+ }
+
+ const frames = options.transition.value / 10
+ const goal = preferred - fill_size
+ const step = goal / frames
+
+ animations = range(frames, 0).map(i => Utils.timeout(5 * i, () => {
+ fill_size += step
+ fill.css = `min-${axis}: ${fill_size}px`
+ animations.shift()
+ }))
+ },
+ })
+}
diff --git a/linux/home/.config/ags/widget/osd/osd.scss b/linux/home/.config/ags/widget/osd/osd.scss
new file mode 100644
index 0000000..33df6d2
--- /dev/null
+++ b/linux/home/.config/ags/widget/osd/osd.scss
@@ -0,0 +1,26 @@
+window.indicator {
+ .progress {
+ @include floating-widget;
+ padding: $padding * 0.5;
+ border-radius: if($radius >0, calc($radius + $padding * 0.5), 0);
+ @debug $radius;
+
+ .fill {
+ border-radius: $radius;
+ background-color: $primary-bg;
+ color: $primary-fg;
+
+ image {
+ -gtk-icon-transform: scale(0.7);
+ }
+ }
+ }
+
+ .microphone {
+ @include floating-widget;
+ margin: $spacing * 2;
+ padding: $popover-padding * 2;
+ font-size: 58px;
+ color: transparentize($fg, 0.1);
+ }
+}
diff --git a/linux/home/.config/ags/widget/overview/Overview.ts b/linux/home/.config/ags/widget/overview/Overview.ts
new file mode 100644
index 0000000..8911920
--- /dev/null
+++ b/linux/home/.config/ags/widget/overview/Overview.ts
@@ -0,0 +1,41 @@
+import PopupWindow from "widget/PopupWindow"
+import Workspace from "./Workspace"
+import options from "options"
+import { range } from "lib/utils"
+
+const hyprland = await Service.import("hyprland")
+
+const Overview = (ws: number) => Widget.Box({
+ class_name: "overview horizontal",
+ children: ws > 0
+ ? range(ws).map(Workspace)
+ : hyprland.workspaces
+ .map(({ id }) => Workspace(id))
+ .sort((a, b) => a.attribute.id - b.attribute.id),
+
+ setup: w => {
+ if (ws > 0)
+ return
+
+ w.hook(hyprland, (w, id?: string) => {
+ if (id === undefined)
+ return
+
+ w.children = w.children
+ .filter(ch => ch.attribute.id !== Number(id))
+ }, "workspace-removed")
+ w.hook(hyprland, (w, id?: string) => {
+ if (id === undefined)
+ return
+
+ w.children = [...w.children, Workspace(Number(id))]
+ .sort((a, b) => a.attribute.id - b.attribute.id)
+ }, "workspace-added")
+ },
+})
+
+export default () => PopupWindow({
+ name: "overview",
+ layout: "center",
+ child: options.overview.workspaces.bind().as(Overview),
+})
diff --git a/linux/home/.config/ags/widget/overview/Window.ts b/linux/home/.config/ags/widget/overview/Window.ts
new file mode 100644
index 0000000..02f71eb
--- /dev/null
+++ b/linux/home/.config/ags/widget/overview/Window.ts
@@ -0,0 +1,48 @@
+import { type Client } from "types/service/hyprland"
+import { createSurfaceFromWidget, icon } from "lib/utils"
+import Gdk from "gi://Gdk"
+import Gtk from "gi://Gtk?version=3.0"
+import options from "options"
+import icons from "lib/icons"
+
+const monochrome = options.overview.monochromeIcon
+const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)]
+const hyprland = await Service.import("hyprland")
+const apps = await Service.import("applications")
+const dispatch = (args: string) => hyprland.messageAsync(`dispatch ${args}`)
+
+export default ({ address, size: [w, h], class: c, title }: Client) => Widget.Button({
+ class_name: "client",
+ attribute: { address },
+ tooltip_text: `${title}`,
+ child: Widget.Icon({
+ css: options.overview.scale.bind().as(v => `
+ min-width: ${(v / 100) * w}px;
+ min-height: ${(v / 100) * h}px;
+ `),
+ icon: monochrome.bind().as(m => {
+ const app = apps.list.find(app => app.match(c))
+ if (!app)
+ return icons.fallback.executable + (m ? "-symbolic" : "")
+
+
+ return icon(
+ app.icon_name + (m ? "-symbolic" : ""),
+ icons.fallback.executable + (m ? "-symbolic" : ""),
+ )
+ }),
+ }),
+ on_secondary_click: () => dispatch(`closewindow address:${address}`),
+ on_clicked: () => {
+ dispatch(`focuswindow address:${address}`)
+ App.closeWindow("overview")
+ },
+ setup: btn => btn
+ .on("drag-data-get", (_w, _c, data) => data.set_text(address, address.length))
+ .on("drag-begin", (_, context) => {
+ Gtk.drag_set_icon_surface(context, createSurfaceFromWidget(btn))
+ btn.toggleClassName("hidden", true)
+ })
+ .on("drag-end", () => btn.toggleClassName("hidden", false))
+ .drag_source_set(Gdk.ModifierType.BUTTON1_MASK, TARGET, Gdk.DragAction.COPY),
+})
diff --git a/linux/home/.config/ags/widget/overview/Workspace.ts b/linux/home/.config/ags/widget/overview/Workspace.ts
new file mode 100644
index 0000000..1b8d60b
--- /dev/null
+++ b/linux/home/.config/ags/widget/overview/Workspace.ts
@@ -0,0 +1,76 @@
+import Window from "./Window"
+import Gdk from "gi://Gdk"
+import Gtk from "gi://Gtk?version=3.0"
+import options from "options"
+
+const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)]
+const scale = (size: number) => (options.overview.scale.value / 100) * size
+const hyprland = await Service.import("hyprland")
+
+const dispatch = (args: string) => hyprland.messageAsync(`dispatch ${args}`)
+
+const size = (id: number) => {
+ const def = { h: 1080, w: 1920 }
+ const ws = hyprland.getWorkspace(id)
+ if (!ws)
+ return def
+
+ const mon = hyprland.getMonitor(ws.monitorID)
+ return mon ? { h: mon.height, w: mon.width } : def
+}
+
+export default (id: number) => {
+ const fixed = Widget.Fixed()
+
+ // TODO: early return if position is unchaged
+ async function update() {
+ const json = await hyprland.messageAsync("j/clients").catch(() => null)
+ if (!json)
+ return
+
+ fixed.get_children().forEach(ch => ch.destroy())
+ const clients = JSON.parse(json) as typeof hyprland.clients
+ clients
+ .filter(({ workspace }) => workspace.id === id)
+ .forEach(c => {
+ const x = c.at[0] - (hyprland.getMonitor(c.monitor)?.x || 0)
+ const y = c.at[1] - (hyprland.getMonitor(c.monitor)?.y || 0)
+ c.mapped && fixed.put(Window(c), scale(x), scale(y))
+ })
+ fixed.show_all()
+ }
+
+ return Widget.Box({
+ attribute: { id },
+ tooltipText: `${id}`,
+ class_name: "workspace",
+ vpack: "center",
+ css: options.overview.scale.bind().as(v => `
+ min-width: ${(v / 100) * size(id).w}px;
+ min-height: ${(v / 100) * size(id).h}px;
+ `),
+ setup(box) {
+ box.hook(options.overview.scale, update)
+ box.hook(hyprland, update, "notify::clients")
+ box.hook(hyprland.active.client, update)
+ box.hook(hyprland.active.workspace, () => {
+ box.toggleClassName("active", hyprland.active.workspace.id === id)
+ })
+ },
+ child: Widget.EventBox({
+ expand: true,
+ on_primary_click: () => {
+ App.closeWindow("overview")
+ dispatch(`workspace ${id}`)
+ },
+ setup: eventbox => {
+ eventbox.drag_dest_set(Gtk.DestDefaults.ALL, TARGET, Gdk.DragAction.COPY)
+ eventbox.connect("drag-data-received", (_w, _c, _x, _y, data) => {
+ const address = new TextDecoder().decode(data.get_data())
+ dispatch(`movetoworkspacesilent ${id},address:${address}`)
+ })
+ },
+ child: fixed,
+ }),
+ })
+}
diff --git a/linux/home/.config/ags/widget/overview/overview.scss b/linux/home/.config/ags/widget/overview/overview.scss
new file mode 100644
index 0000000..4665b52
--- /dev/null
+++ b/linux/home/.config/ags/widget/overview/overview.scss
@@ -0,0 +1,34 @@
+window#overview .overview {
+ @include floating-widget;
+ @include spacing;
+
+ .workspace {
+ &.active>widget {
+ border-color: $primary-bg;
+ }
+
+ >widget {
+ @include widget;
+ border-radius: if($radius ==0, 0, $radius + $padding);
+
+ &:hover {
+ background-color: $hover-bg;
+ }
+
+ &:drop(active) {
+ border-color: $primary-bg;
+ }
+ }
+ }
+
+ .client {
+ @include button;
+ border-radius: $radius;
+ margin: $padding;
+
+ &.hidden {
+ @include hidden;
+ transition: 0;
+ }
+ }
+}
diff --git a/linux/home/.config/ags/widget/powermenu/PowerMenu.ts b/linux/home/.config/ags/widget/powermenu/PowerMenu.ts
new file mode 100644
index 0000000..fe0a0e9
--- /dev/null
+++ b/linux/home/.config/ags/widget/powermenu/PowerMenu.ts
@@ -0,0 +1,56 @@
+import PopupWindow from "widget/PopupWindow"
+import powermenu, { type Action } from "service/powermenu"
+import icons from "lib/icons"
+import options from "options"
+import type Gtk from "gi://Gtk?version=3.0"
+
+const { layout, labels } = options.powermenu
+
+const SysButton = (action: Action, label: string) => Widget.Button({
+ on_clicked: () => powermenu.action(action),
+ child: Widget.Box({
+ vertical: true,
+ class_name: "system-button",
+ children: [
+ Widget.Icon(icons.powermenu[action]),
+ Widget.Label({
+ label,
+ visible: labels.bind(),
+ }),
+ ],
+ }),
+})
+
+export default () => PopupWindow({
+ name: "powermenu",
+ transition: "crossfade",
+ child: Widget.Box<Gtk.Widget>({
+ class_name: "powermenu horizontal",
+ setup: self => self.hook(layout, () => {
+ self.toggleClassName("box", layout.value === "box")
+ self.toggleClassName("line", layout.value === "line")
+ }),
+ children: layout.bind().as(layout => {
+ switch (layout) {
+ case "line": return [
+ SysButton("shutdown", "Shutdown"),
+ SysButton("logout", "Log Out"),
+ SysButton("reboot", "Reboot"),
+ SysButton("sleep", "Sleep"),
+ ]
+ case "box": return [
+ Widget.Box(
+ { vertical: true },
+ SysButton("shutdown", "Shutdown"),
+ SysButton("logout", "Log Out"),
+ ),
+ Widget.Box(
+ { vertical: true },
+ SysButton("reboot", "Reboot"),
+ SysButton("sleep", "Sleep"),
+ ),
+ ]
+ }
+ }),
+ }),
+})
diff --git a/linux/home/.config/ags/widget/powermenu/Verification.ts b/linux/home/.config/ags/widget/powermenu/Verification.ts
new file mode 100644
index 0000000..e85c81a
--- /dev/null
+++ b/linux/home/.config/ags/widget/powermenu/Verification.ts
@@ -0,0 +1,47 @@
+import PopupWindow from "widget/PopupWindow"
+import powermenu from "service/powermenu"
+
+export default () => PopupWindow({
+ name: "verification",
+ transition: "crossfade",
+ child: Widget.Box({
+ class_name: "verification",
+ vertical: true,
+ children: [
+ Widget.Box({
+ class_name: "text-box",
+ vertical: true,
+ children: [
+ Widget.Label({
+ class_name: "title",
+ label: powermenu.bind("title"),
+ }),
+ Widget.Label({
+ class_name: "desc",
+ label: "Are you sure?",
+ }),
+ ],
+ }),
+ Widget.Box({
+ class_name: "buttons horizontal",
+ vexpand: true,
+ vpack: "end",
+ homogeneous: true,
+ children: [
+ Widget.Button({
+ child: Widget.Label("No"),
+ on_clicked: () => App.toggleWindow("verification"),
+ setup: self => self.hook(App, (_, name: string, visible: boolean) => {
+ if (name === "verification" && visible)
+ self.grab_focus()
+ }),
+ }),
+ Widget.Button({
+ child: Widget.Label("Yes"),
+ on_clicked: () => Utils.exec(powermenu.cmd),
+ }),
+ ],
+ }),
+ ],
+ }),
+})
diff --git a/linux/home/.config/ags/widget/powermenu/powermenu.scss b/linux/home/.config/ags/widget/powermenu/powermenu.scss
new file mode 100644
index 0000000..d5ce0de
--- /dev/null
+++ b/linux/home/.config/ags/widget/powermenu/powermenu.scss
@@ -0,0 +1,110 @@
+window#powermenu,
+window#verification {
+ // the fraction has to be more than hyprland ignorealpha
+ background-color: rgba(0, 0, 0, .4);
+}
+
+window#verification .verification {
+ @include floating-widget;
+ padding: $popover-padding * 1.5;
+ min-width: 300px;
+ min-height: 100px;
+
+ .text-box {
+ margin-bottom: $spacing;
+
+ .title {
+ font-size: 1.6em;
+ }
+
+ .desc {
+ color: transparentize($fg, 0.1);
+ font-size: 1.1em;
+ }
+ }
+
+ .buttons {
+ @include spacing;
+ margin-top: $padding;
+
+ button {
+ @include button;
+ font-size: 1.5em;
+ padding: $padding;
+ }
+ }
+}
+
+window#powermenu .powermenu {
+ @include floating-widget;
+
+ &.line {
+ padding: $popover-padding * 1.5;
+
+ button {
+ padding: $popover-padding;
+ }
+
+ label {
+ margin-bottom: $spacing * -.5;
+ }
+ }
+
+ &.box {
+ padding: $popover-padding * 2;
+
+ button {
+ padding: $popover-padding * 1.5;
+ }
+
+ label {
+ margin-bottom: $spacing * -1;
+ }
+ }
+
+ button {
+ @include unset;
+
+ image {
+ @include button;
+ border-radius: $radius + ($popover-padding * 1.4);
+ min-width: 1.7em;
+ min-height: 1.7em;
+ font-size: 4em;
+ }
+
+ label,
+ image {
+ color: transparentize($fg, 0.1);
+ }
+
+ label {
+ margin-top: $spacing * .3;
+ }
+
+ &:hover {
+ image {
+ @include button-hover;
+ }
+
+ label {
+ color: $fg;
+ }
+ }
+
+ &:focus image {
+ @include button-focus;
+ }
+
+ &:active image {
+ @include button-active;
+ }
+
+ &:focus,
+ &:active {
+ label {
+ color: $primary-bg;
+ }
+ }
+ }
+}
diff --git a/linux/home/.config/ags/widget/quicksettings/QuickSettings.ts b/linux/home/.config/ags/widget/quicksettings/QuickSettings.ts
new file mode 100644
index 0000000..2c0d6ac
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/QuickSettings.ts
@@ -0,0 +1,84 @@
+import type Gtk from "gi://Gtk?version=3.0"
+import { ProfileSelector, ProfileToggle } from "./widgets/PowerProfile"
+import { Header } from "./widgets/Header"
+import { Volume, Microhone, SinkSelector, AppMixer } from "./widgets/Volume"
+import { Brightness } from "./widgets/Brightness"
+import { NetworkToggle, WifiSelection } from "./widgets/Network"
+import { BluetoothToggle, BluetoothDevices } from "./widgets/Bluetooth"
+import { DND } from "./widgets/DND"
+import { DarkModeToggle } from "./widgets/DarkMode"
+import { MicMute } from "./widgets/MicMute"
+import { Media } from "./widgets/Media"
+import PopupWindow from "widget/PopupWindow"
+import options from "options"
+
+const { bar, quicksettings } = options
+const media = (await Service.import("mpris")).bind("players")
+const layout = Utils.derive([bar.position, quicksettings.position], (bar, qs) =>
+ `${bar}-${qs}` as const,
+)
+
+const Row = (
+ toggles: Array<() => Gtk.Widget> = [],
+ menus: Array<() => Gtk.Widget> = [],
+) => Widget.Box({
+ vertical: true,
+ children: [
+ Widget.Box({
+ homogeneous: true,
+ class_name: "row horizontal",
+ children: toggles.map(w => w()),
+ }),
+ ...menus.map(w => w()),
+ ],
+})
+
+const Settings = () => Widget.Box({
+ vertical: true,
+ class_name: "quicksettings vertical",
+ css: quicksettings.width.bind().as(w => `min-width: ${w}px;`),
+ children: [
+ Header(),
+ Widget.Box({
+ class_name: "sliders-box vertical",
+ vertical: true,
+ children: [
+ Row(
+ [Volume],
+ [SinkSelector, AppMixer],
+ ),
+ Microhone(),
+ Brightness(),
+ ],
+ }),
+ Row(
+ [NetworkToggle, BluetoothToggle],
+ [WifiSelection, BluetoothDevices],
+ ),
+ Row(
+ [ProfileToggle, DarkModeToggle],
+ [ProfileSelector],
+ ),
+ Row([MicMute, DND]),
+ Widget.Box({
+ visible: media.as(l => l.length > 0),
+ child: Media(),
+ }),
+ ],
+})
+
+const QuickSettings = () => PopupWindow({
+ name: "quicksettings",
+ exclusivity: "exclusive",
+ transition: bar.position.bind().as(pos => pos === "top" ? "slide_down" : "slide_up"),
+ layout: layout.value,
+ child: Settings(),
+})
+
+export function setupQuickSettings() {
+ App.addWindow(QuickSettings())
+ layout.connect("changed", () => {
+ App.removeWindow("quicksettings")
+ App.addWindow(QuickSettings())
+ })
+}
diff --git a/linux/home/.config/ags/widget/quicksettings/ToggleButton.ts b/linux/home/.config/ags/widget/quicksettings/ToggleButton.ts
new file mode 100644
index 0000000..62a2e67
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/ToggleButton.ts
@@ -0,0 +1,154 @@
+import { type Props as IconProps } from "types/widgets/icon"
+import { type Props as LabelProps } from "types/widgets/label"
+import type GObject from "gi://GObject?version=2.0"
+import type Gtk from "gi://Gtk?version=3.0"
+import icons from "lib/icons"
+
+export const opened = Variable("")
+App.connect("window-toggled", (_, name: string, visible: boolean) => {
+ if (name === "quicksettings" && !visible)
+ Utils.timeout(500, () => opened.value = "")
+})
+
+export const Arrow = (name: string, activate?: false | (() => void)) => {
+ let deg = 0
+ let iconOpened = false
+ const icon = Widget.Icon(icons.ui.arrow.right).hook(opened, () => {
+ if (opened.value === name && !iconOpened || opened.value !== name && iconOpened) {
+ const step = opened.value === name ? 10 : -10
+ iconOpened = !iconOpened
+ for (let i = 0; i < 9; ++i) {
+ Utils.timeout(15 * i, () => {
+ deg += step
+ icon.setCss(`-gtk-icon-transform: rotate(${deg}deg);`)
+ })
+ }
+ }
+ })
+ return Widget.Button({
+ child: icon,
+ class_name: "arrow",
+ on_clicked: () => {
+ opened.value = opened.value === name ? "" : name
+ if (typeof activate === "function")
+ activate()
+ },
+ })
+}
+
+type ArrowToggleButtonProps = {
+ name: string
+ icon: IconProps["icon"]
+ label: LabelProps["label"]
+ activate: () => void
+ deactivate: () => void
+ activateOnArrow?: boolean
+ connection: [GObject.Object, () => boolean]
+}
+export const ArrowToggleButton = ({
+ name,
+ icon,
+ label,
+ activate,
+ deactivate,
+ activateOnArrow = true,
+ connection: [service, condition],
+}: ArrowToggleButtonProps) => Widget.Box({
+ class_name: "toggle-button",
+ setup: self => self.hook(service, () => {
+ self.toggleClassName("active", condition())
+ }),
+ children: [
+ Widget.Button({
+ child: Widget.Box({
+ hexpand: true,
+ children: [
+ Widget.Icon({
+ class_name: "icon",
+ icon,
+ }),
+ Widget.Label({
+ class_name: "label",
+ max_width_chars: 10,
+ truncate: "end",
+ label,
+ }),
+ ],
+ }),
+ on_clicked: () => {
+ if (condition()) {
+ deactivate()
+ if (opened.value === name)
+ opened.value = ""
+ } else {
+ activate()
+ }
+ },
+ }),
+ Arrow(name, activateOnArrow && activate),
+ ],
+})
+
+type MenuProps = {
+ name: string
+ icon: IconProps["icon"]
+ title: LabelProps["label"]
+ content: Gtk.Widget[]
+}
+export const Menu = ({ name, icon, title, content }: MenuProps) => Widget.Revealer({
+ transition: "slide_down",
+ reveal_child: opened.bind().as(v => v === name),
+ child: Widget.Box({
+ class_names: ["menu", name],
+ vertical: true,
+ children: [
+ Widget.Box({
+ class_name: "title-box",
+ children: [
+ Widget.Icon({
+ class_name: "icon",
+ icon,
+ }),
+ Widget.Label({
+ class_name: "title",
+ truncate: "end",
+ label: title,
+ }),
+ ],
+ }),
+ Widget.Separator(),
+ Widget.Box({
+ vertical: true,
+ class_name: "content vertical",
+ children: content,
+ }),
+ ],
+ }),
+})
+
+type SimpleToggleButtonProps = {
+ icon: IconProps["icon"]
+ label: LabelProps["label"]
+ toggle: () => void
+ connection: [GObject.Object, () => boolean]
+}
+export const SimpleToggleButton = ({
+ icon,
+ label,
+ toggle,
+ connection: [service, condition],
+}: SimpleToggleButtonProps) => Widget.Button({
+ on_clicked: toggle,
+ class_name: "simple-toggle",
+ setup: self => self.hook(service, () => {
+ self.toggleClassName("active", condition())
+ }),
+ child: Widget.Box([
+ Widget.Icon({ icon }),
+ Widget.Label({
+ max_width_chars: 10,
+ truncate: "end",
+ label,
+ }),
+ ]),
+})
diff --git a/linux/home/.config/ags/widget/quicksettings/quicksettings.scss b/linux/home/.config/ags/widget/quicksettings/quicksettings.scss
new file mode 100644
index 0000000..bd18ff1
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/quicksettings.scss
@@ -0,0 +1,177 @@
+window#quicksettings .quicksettings {
+ @include floating-widget;
+ @include spacing;
+
+ padding: $popover-padding * 1.4;
+
+ .avatar {
+ @include widget;
+ border-radius: $radius * 3;
+ }
+
+ .header {
+ @include spacing(.5);
+ color: transparentize($fg, .15);
+
+ button {
+ @include button;
+ padding: $padding;
+
+ image {
+ font-size: 1.4em;
+ }
+ }
+ }
+
+ .sliders-box {
+ @include widget;
+ padding: $padding;
+
+ button {
+ @include button($flat: true);
+ padding: $padding * .5;
+ }
+
+ .volume button.arrow:last-child {
+ margin-left: $spacing * .4;
+ }
+
+ .volume,
+ .brightness {
+ padding: $padding * .5;
+ }
+
+ scale {
+ @include slider;
+ margin: 0 ($spacing * .5);
+
+ &.muted highlight {
+ background-image: none;
+ background-color: transparentize($fg, $amount: .2);
+ }
+ }
+ }
+
+ .row {
+ @include spacing;
+ }
+
+ .menu {
+ @include unset;
+ @include widget;
+ padding: $padding;
+ margin-top: $spacing;
+
+ .icon {
+ margin: 0 ($spacing * .5);
+ margin-left: $spacing * .2;
+ }
+
+ .title {
+ font-weight: bold;
+ }
+
+ separator {
+ margin: ($radius * .5);
+ background-color: $border-color;
+ }
+
+ button {
+ @include button($flat: true);
+ padding: ($padding * .5);
+
+ image:first-child {
+ margin-right: $spacing * .5;
+ }
+ }
+
+ .bluetooth-devices {
+ @include spacing(.5);
+ }
+
+ switch {
+ @include switch;
+ }
+ }
+
+ .sliders-box .menu {
+ margin: ($spacing * .5) 0;
+
+ &.app-mixer {
+ .mixer-item {
+ padding: $padding * .5;
+ padding-left: 0;
+ padding-right: $padding * 2;
+
+ scale {
+ @include slider($width: .5em);
+ }
+
+ image {
+ font-size: 1.2em;
+ margin: 0 $padding;
+ }
+ }
+ }
+ }
+
+ .toggle-button {
+ @include button;
+ font-weight: bold;
+
+ image {
+ font-size: 1.3em;
+ }
+
+ label {
+ margin-left: $spacing * .3;
+ }
+
+ button {
+ @include button($flat: true);
+
+ &:first-child {
+ padding: $padding * 1.2;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+
+ &:last-child {
+ padding: $padding * .5;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+ }
+
+ &.active {
+ background-color: $primary-bg;
+
+ label,
+ image {
+ color: $primary-fg;
+ }
+ }
+ }
+
+ .simple-toggle {
+ @include button;
+ font-weight: bold;
+ padding: $padding * 1.2;
+
+ label {
+ margin-left: $spacing * .3;
+ }
+
+ image {
+ font-size: 1.3em;
+ }
+ }
+
+ .media {
+ @include spacing;
+
+ .player {
+ @include media;
+ }
+ }
+}
diff --git a/linux/home/.config/ags/widget/quicksettings/widgets/Bluetooth.ts b/linux/home/.config/ags/widget/quicksettings/widgets/Bluetooth.ts
new file mode 100644
index 0000000..649e654
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/widgets/Bluetooth.ts
@@ -0,0 +1,61 @@
+import { type BluetoothDevice } from "types/service/bluetooth"
+import { Menu, ArrowToggleButton } from "../ToggleButton"
+import icons from "lib/icons"
+
+const bluetooth = await Service.import("bluetooth")
+
+export const BluetoothToggle = () => ArrowToggleButton({
+ name: "bluetooth",
+ icon: bluetooth.bind("enabled").as(p => icons.bluetooth[p ? "enabled" : "disabled"]),
+ label: Utils.watch("Disabled", bluetooth, () => {
+ if (!bluetooth.enabled)
+ return "Disabled"
+
+ if (bluetooth.connected_devices.length === 1)
+ return bluetooth.connected_devices[0].alias
+
+ return `${bluetooth.connected_devices.length} Connected`
+ }),
+ connection: [bluetooth, () => bluetooth.enabled],
+ deactivate: () => bluetooth.enabled = false,
+ activate: () => bluetooth.enabled = true,
+})
+
+const DeviceItem = (device: BluetoothDevice) => Widget.Box({
+ children: [
+ Widget.Icon(device.icon_name + "-symbolic"),
+ Widget.Label(device.name),
+ Widget.Label({
+ label: `${device.battery_percentage}%`,
+ visible: device.bind("battery_percentage").as(p => p > 0),
+ }),
+ Widget.Box({ hexpand: true }),
+ Widget.Spinner({
+ active: device.bind("connecting"),
+ visible: device.bind("connecting"),
+ }),
+ Widget.Switch({
+ active: device.connected,
+ visible: device.bind("connecting").as(p => !p),
+ setup: self => self.on("notify::active", () => {
+ device.setConnection(self.active)
+ }),
+ }),
+ ],
+})
+
+export const BluetoothDevices = () => Menu({
+ name: "bluetooth",
+ icon: icons.bluetooth.disabled,
+ title: "Bluetooth",
+ content: [
+ Widget.Box({
+ class_name: "bluetooth-devices",
+ hexpand: true,
+ vertical: true,
+ children: bluetooth.bind("devices").as(ds => ds
+ .filter(d => d.name)
+ .map(DeviceItem)),
+ }),
+ ],
+})
diff --git a/linux/home/.config/ags/widget/quicksettings/widgets/Brightness.ts b/linux/home/.config/ags/widget/quicksettings/widgets/Brightness.ts
new file mode 100644
index 0000000..a3ce565
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/widgets/Brightness.ts
@@ -0,0 +1,23 @@
+import icons from "lib/icons"
+import brightness from "service/brightness"
+
+const BrightnessSlider = () => Widget.Slider({
+ draw_value: false,
+ hexpand: true,
+ value: brightness.bind("screen"),
+ on_change: ({ value }) => brightness.screen = value,
+})
+
+export const Brightness = () => Widget.Box({
+ class_name: "brightness",
+ children: [
+ Widget.Button({
+ vpack: "center",
+ child: Widget.Icon(icons.brightness.indicator),
+ on_clicked: () => brightness.screen = 0,
+ tooltip_text: brightness.bind("screen").as(v =>
+ `Screen Brightness: ${Math.floor(v * 100)}%`),
+ }),
+ BrightnessSlider(),
+ ],
+})
diff --git a/linux/home/.config/ags/widget/quicksettings/widgets/DND.ts b/linux/home/.config/ags/widget/quicksettings/widgets/DND.ts
new file mode 100644
index 0000000..7fc1fd0
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/widgets/DND.ts
@@ -0,0 +1,12 @@
+import { SimpleToggleButton } from "../ToggleButton"
+import icons from "lib/icons"
+
+const n = await Service.import("notifications")
+const dnd = n.bind("dnd")
+
+export const DND = () => SimpleToggleButton({
+ icon: dnd.as(dnd => icons.notifications[dnd ? "silent" : "noisy"]),
+ label: dnd.as(dnd => dnd ? "Silent" : "Noisy"),
+ toggle: () => n.dnd = !n.dnd,
+ connection: [n, () => n.dnd],
+})
diff --git a/linux/home/.config/ags/widget/quicksettings/widgets/DarkMode.ts b/linux/home/.config/ags/widget/quicksettings/widgets/DarkMode.ts
new file mode 100644
index 0000000..9ec94df
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/widgets/DarkMode.ts
@@ -0,0 +1,12 @@
+import { SimpleToggleButton } from "../ToggleButton"
+import icons from "lib/icons"
+import options from "options"
+
+const { scheme } = options.theme
+
+export const DarkModeToggle = () => SimpleToggleButton({
+ icon: scheme.bind().as(s => icons.color[s]),
+ label: scheme.bind().as(s => s === "dark" ? "Dark" : "Light"),
+ toggle: () => scheme.value = scheme.value === "dark" ? "light" : "dark",
+ connection: [scheme, () => scheme.value === "dark"],
+})
diff --git a/linux/home/.config/ags/widget/quicksettings/widgets/Header.ts b/linux/home/.config/ags/widget/quicksettings/widgets/Header.ts
new file mode 100644
index 0000000..44c26f2
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/widgets/Header.ts
@@ -0,0 +1,69 @@
+import icons from "lib/icons"
+import { uptime } from "lib/variables"
+import options from "options"
+import powermenu, { Action } from "service/powermenu"
+
+const battery = await Service.import("battery")
+const { image, size } = options.quicksettings.avatar
+
+function up(up: number) {
+ const h = Math.floor(up / 60)
+ const m = Math.floor(up % 60)
+ return `${h}h ${m < 10 ? "0" + m : m}m`
+}
+
+const Avatar = () => Widget.Box({
+ class_name: "avatar",
+ css: Utils.merge([image.bind(), size.bind()], (img, size) => `
+ min-width: ${size}px;
+ min-height: ${size}px;
+ background-image: url('${img}');
+ background-size: cover;
+ `),
+})
+
+const SysButton = (action: Action) => Widget.Button({
+ vpack: "center",
+ child: Widget.Icon(icons.powermenu[action]),
+ on_clicked: () => powermenu.action(action),
+})
+
+export const Header = () => Widget.Box(
+ { class_name: "header horizontal" },
+ Avatar(),
+ Widget.Box({
+ vertical: true,
+ vpack: "center",
+ children: [
+ Widget.Box({
+ visible: battery.bind("available"),
+ children: [
+ Widget.Icon({ icon: battery.bind("icon_name") }),
+ Widget.Label({ label: battery.bind("percent").as(p => `${p}%`) }),
+ ],
+ }),
+ Widget.Box([
+ Widget.Icon({ icon: icons.ui.time }),
+ Widget.Label({ label: uptime.bind().as(up) }),
+
+ //Widget.Label({ label: `${user.name}\n` }),
+ // //Widget.Label({ label: uptime.bind().value }),
+ // Widget.Label({ label: `${user.name}\n ${uptime.bind().value}` }),
+ // //Widget.Icon({ icon: icons.ui.time }),
+ ]),
+
+ ],
+ }),
+ Widget.Box({ hexpand: true }),
+ Widget.Button({
+ vpack: "center",
+ child: Widget.Icon(icons.ui.settings),
+ on_clicked: () => {
+ App.closeWindow("quicksettings")
+ App.closeWindow("settings-dialog")
+ App.openWindow("settings-dialog")
+ },
+ }),
+ SysButton("logout"),
+ SysButton("shutdown"),
+)
diff --git a/linux/home/.config/ags/widget/quicksettings/widgets/Media.ts b/linux/home/.config/ags/widget/quicksettings/widgets/Media.ts
new file mode 100644
index 0000000..52254ea
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/widgets/Media.ts
@@ -0,0 +1,153 @@
+import { type MprisPlayer } from "types/service/mpris"
+import icons from "lib/icons"
+import options from "options"
+import { icon } from "lib/utils"
+
+const mpris = await Service.import("mpris")
+const players = mpris.bind("players")
+const { media } = options.quicksettings
+
+function lengthStr(length: number) {
+ const min = Math.floor(length / 60)
+ const sec = Math.floor(length % 60)
+ const sec0 = sec < 10 ? "0" : ""
+ return `${min}:${sec0}${sec}`
+}
+
+const Player = (player: MprisPlayer) => {
+ const cover = Widget.Box({
+ class_name: "cover",
+ vpack: "start",
+ css: Utils.merge([
+ player.bind("cover_path"),
+ player.bind("track_cover_url"),
+ media.coverSize.bind(),
+ ], (path, url, size) => `
+ min-width: ${size}px;
+ min-height: ${size}px;
+ background-image: url('${path || url}');
+ `),
+ })
+
+ const title = Widget.Label({
+ class_name: "title",
+ max_width_chars: 20,
+ truncate: "end",
+ hpack: "start",
+ label: player.bind("track_title"),
+ })
+
+ const artist = Widget.Label({
+ class_name: "artist",
+ max_width_chars: 20,
+ truncate: "end",
+ hpack: "start",
+ label: player.bind("track_artists").as(a => a.join(", ")),
+ })
+
+ const positionSlider = Widget.Slider({
+ class_name: "position",
+ draw_value: false,
+ on_change: ({ value }) => player.position = value * player.length,
+ setup: self => {
+ const update = () => {
+ const { length, position } = player
+ self.visible = length > 0
+ self.value = length > 0 ? position / length : 0
+ }
+ self.hook(player, update)
+ self.hook(player, update, "position")
+ self.poll(1000, update)
+ },
+ })
+
+ const positionLabel = Widget.Label({
+ class_name: "position",
+ hpack: "start",
+ setup: self => {
+ const update = (_: unknown, time?: number) => {
+ self.label = lengthStr(time || player.position)
+ self.visible = player.length > 0
+ }
+ self.hook(player, update, "position")
+ self.poll(1000, update)
+ },
+ })
+
+ const lengthLabel = Widget.Label({
+ class_name: "length",
+ hpack: "end",
+ visible: player.bind("length").as(l => l > 0),
+ label: player.bind("length").as(lengthStr),
+ })
+
+ const playericon = Widget.Icon({
+ class_name: "icon",
+ hexpand: true,
+ hpack: "end",
+ vpack: "start",
+ tooltip_text: player.identity || "",
+ icon: Utils.merge([player.bind("entry"), media.monochromeIcon.bind()], (e, s) => {
+ const name = `${e}${s ? "-symbolic" : ""}`
+ return icon(name, icons.fallback.audio)
+ }),
+ })
+
+ const playPause = Widget.Button({
+ class_name: "play-pause",
+ on_clicked: () => player.playPause(),
+ visible: player.bind("can_play"),
+ child: Widget.Icon({
+ icon: player.bind("play_back_status").as(s => {
+ switch (s) {
+ case "Playing": return icons.mpris.playing
+ case "Paused":
+ case "Stopped": return icons.mpris.stopped
+ }
+ }),
+ }),
+ })
+
+ const prev = Widget.Button({
+ on_clicked: () => player.previous(),
+ visible: player.bind("can_go_prev"),
+ child: Widget.Icon(icons.mpris.prev),
+ })
+
+ const next = Widget.Button({
+ on_clicked: () => player.next(),
+ visible: player.bind("can_go_next"),
+ child: Widget.Icon(icons.mpris.next),
+ })
+
+ return Widget.Box(
+ { class_name: "player", vexpand: false },
+ cover,
+ Widget.Box(
+ { vertical: true },
+ Widget.Box([
+ title,
+ playericon,
+ ]),
+ artist,
+ Widget.Box({ vexpand: true }),
+ positionSlider,
+ Widget.CenterBox({
+ class_name: "footer horizontal",
+ start_widget: positionLabel,
+ center_widget: Widget.Box([
+ prev,
+ playPause,
+ next,
+ ]),
+ end_widget: lengthLabel,
+ }),
+ ),
+ )
+}
+
+export const Media = () => Widget.Box({
+ vertical: true,
+ class_name: "media vertical",
+ children: players.as(p => p.map(Player)),
+})
diff --git a/linux/home/.config/ags/widget/quicksettings/widgets/MicMute.ts b/linux/home/.config/ags/widget/quicksettings/widgets/MicMute.ts
new file mode 100644
index 0000000..b6e9454
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/widgets/MicMute.ts
@@ -0,0 +1,18 @@
+import { SimpleToggleButton } from "../ToggleButton"
+import icons from "lib/icons"
+const { microphone } = await Service.import("audio")
+
+const icon = () => microphone.is_muted || microphone.stream?.is_muted
+ ? icons.audio.mic.muted
+ : icons.audio.mic.high
+
+const label = () => microphone.is_muted || microphone.stream?.is_muted
+ ? "Muted"
+ : "Unmuted"
+
+export const MicMute = () => SimpleToggleButton({
+ icon: Utils.watch(icon(), microphone, icon),
+ label: Utils.watch(label(), microphone, label),
+ toggle: () => microphone.is_muted = !microphone.is_muted,
+ connection: [microphone, () => microphone?.is_muted || false],
+})
diff --git a/linux/home/.config/ags/widget/quicksettings/widgets/Network.ts b/linux/home/.config/ags/widget/quicksettings/widgets/Network.ts
new file mode 100644
index 0000000..eb14ab4
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/widgets/Network.ts
@@ -0,0 +1,61 @@
+import { Menu, ArrowToggleButton } from "../ToggleButton"
+import icons from "lib/icons.js"
+import { dependencies, sh } from "lib/utils"
+import options from "options"
+const { wifi } = await Service.import("network")
+
+export const NetworkToggle = () => ArrowToggleButton({
+ name: "network",
+ icon: wifi.bind("icon_name"),
+ label: wifi.bind("ssid").as(ssid => ssid || "Not Connected"),
+ connection: [wifi, () => wifi.enabled],
+ deactivate: () => wifi.enabled = false,
+ activate: () => {
+ wifi.enabled = true
+ wifi.scan()
+ },
+})
+
+export const WifiSelection = () => Menu({
+ name: "network",
+ icon: wifi.bind("icon_name"),
+ title: "Wifi Selection",
+ content: [
+ Widget.Box({
+ vertical: true,
+ setup: self => self.hook(wifi, () => self.children =
+ wifi.access_points.map(ap => Widget.Button({
+ on_clicked: () => {
+ if (dependencies("nmcli"))
+ Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`)
+ },
+ child: Widget.Box({
+ children: [
+ Widget.Icon(ap.iconName),
+ Widget.Label(ap.ssid || ""),
+ Widget.Icon({
+ icon: icons.ui.tick,
+ hexpand: true,
+ hpack: "end",
+ setup: self => Utils.idle(() => {
+ if (!self.is_destroyed)
+ self.visible = ap.active
+ }),
+ }),
+ ],
+ }),
+ })),
+ ),
+ }),
+ Widget.Separator(),
+ Widget.Button({
+ on_clicked: () => sh(options.quicksettings.networkSettings.value),
+ child: Widget.Box({
+ children: [
+ Widget.Icon(icons.ui.settings),
+ Widget.Label("Network"),
+ ],
+ }),
+ }),
+ ],
+})
diff --git a/linux/home/.config/ags/widget/quicksettings/widgets/PowerProfile.ts b/linux/home/.config/ags/widget/quicksettings/widgets/PowerProfile.ts
new file mode 100644
index 0000000..f566aaf
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/widgets/PowerProfile.ts
@@ -0,0 +1,99 @@
+import { ArrowToggleButton, Menu } from "../ToggleButton"
+import icons from "lib/icons"
+
+import asusctl from "service/asusctl"
+const asusprof = asusctl.bind("profile")
+
+const AsusProfileToggle = () => ArrowToggleButton({
+ name: "asusctl-profile",
+ icon: asusprof.as(p => icons.asusctl.profile[p]),
+ label: asusprof,
+ connection: [asusctl, () => asusctl.profile !== "Balanced"],
+ activate: () => asusctl.setProfile("Quiet"),
+ deactivate: () => asusctl.setProfile("Balanced"),
+ activateOnArrow: false,
+})
+
+const AsusProfileSelector = () => Menu({
+ name: "asusctl-profile",
+ icon: asusprof.as(p => icons.asusctl.profile[p]),
+ title: "Profile Selector",
+ content: [
+ Widget.Box({
+ vertical: true,
+ hexpand: true,
+ children: [
+ Widget.Box({
+ vertical: true,
+ children: asusctl.profiles.map(prof => Widget.Button({
+ on_clicked: () => asusctl.setProfile(prof),
+ child: Widget.Box({
+ children: [
+ Widget.Icon(icons.asusctl.profile[prof]),
+ Widget.Label(prof),
+ ],
+ }),
+ })),
+ }),
+ ],
+ }),
+ Widget.Separator(),
+ Widget.Button({
+ on_clicked: () => Utils.execAsync("rog-control-center"),
+ child: Widget.Box({
+ children: [
+ Widget.Icon(icons.ui.settings),
+ Widget.Label("Rog Control Center"),
+ ],
+ }),
+ }),
+ ],
+})
+
+
+const pp = await Service.import("powerprofiles")
+const profile = pp.bind("active_profile")
+const profiles = pp.profiles.map(p => p.Profile)
+
+const pretty = (str: string) => str
+ .split("-")
+ .map(str => `${str.at(0)?.toUpperCase()}${str.slice(1)}`)
+ .join(" ")
+
+const PowerProfileToggle = () => ArrowToggleButton({
+ name: "asusctl-profile",
+ icon: profile.as(p => icons.powerprofile[p]),
+ label: profile.as(pretty),
+ connection: [pp, () => pp.active_profile !== profiles[1]],
+ activate: () => pp.active_profile = profiles[0],
+ deactivate: () => pp.active_profile = profiles[1],
+ activateOnArrow: false,
+})
+
+const PowerProfileSelector = () => Menu({
+ name: "asusctl-profile",
+ icon: profile.as(p => icons.powerprofile[p]),
+ title: "Profile Selector",
+ content: [Widget.Box({
+ vertical: true,
+ hexpand: true,
+ child: Widget.Box({
+ vertical: true,
+ children: profiles.map(prof => Widget.Button({
+ on_clicked: () => pp.active_profile = prof,
+ child: Widget.Box({
+ children: [
+ Widget.Icon(icons.powerprofile[prof]),
+ Widget.Label(pretty(prof)),
+ ],
+ }),
+ })),
+ }),
+ })],
+})
+
+export const ProfileToggle = asusctl.available
+ ? AsusProfileToggle : PowerProfileToggle
+
+export const ProfileSelector = asusctl.available
+ ? AsusProfileSelector : PowerProfileSelector
diff --git a/linux/home/.config/ags/widget/quicksettings/widgets/Volume.ts b/linux/home/.config/ags/widget/quicksettings/widgets/Volume.ts
new file mode 100644
index 0000000..077439a
--- /dev/null
+++ b/linux/home/.config/ags/widget/quicksettings/widgets/Volume.ts
@@ -0,0 +1,161 @@
+import { type Stream } from "types/service/audio"
+import { Arrow, Menu } from "../ToggleButton"
+import { dependencies, icon, sh } from "lib/utils"
+import icons from "lib/icons.js"
+const audio = await Service.import("audio")
+
+type Type = "microphone" | "speaker"
+
+const VolumeIndicator = (type: Type = "speaker") => Widget.Button({
+ vpack: "center",
+ on_clicked: () => audio[type].is_muted = !audio[type].is_muted,
+ child: Widget.Icon({
+ icon: audio[type].bind("icon_name")
+ .as(i => icon(i || "", icons.audio.volume.medium)),
+ tooltipText: audio[type].bind("volume")
+ .as(vol => `Volume: ${Math.floor(vol * 100)}%`),
+ }),
+})
+
+const micIndicator = (type: Type = "microphone") => Widget.Button({
+ vpack: "center",
+ on_clicked: () => audio[type].is_muted = !audio[type].is_muted,
+ child: Widget.Icon({
+ icon: audio[type].bind("icon_name")
+ .as(i => icon(i || "", icons.audio.mic.medium)),
+ tooltipText: audio[type].bind("volume")
+ .as(vol => `Volume: ${Math.floor(vol * 100)}%`),
+ }),
+})
+
+const VolumeSlider = (type: Type = "speaker") => Widget.Slider({
+ hexpand: true,
+ draw_value: false,
+ on_change: ({ value, dragging }) => {
+ if (dragging) {
+ audio[type].volume = value
+ audio[type].is_muted = false
+ }
+ },
+ value: audio[type].bind("volume"),
+ class_name: audio[type].bind("is_muted").as(m => m ? "muted" : ""),
+})
+
+export const Volume = () => Widget.Box({
+ class_name: "volume",
+ children: [
+ VolumeIndicator("speaker"),
+ VolumeSlider("speaker"),
+ Widget.Box({
+ vpack: "center",
+ child: Arrow("sink-selector"),
+ }),
+ Widget.Box({
+ vpack: "center",
+ child: Arrow("app-mixer"),
+ visible: audio.bind("apps").as(a => a.length > 0),
+ }),
+ ],
+})
+
+export const Microhone = () => Widget.Box({
+ class_name: "slider horizontal",
+ visible: audio.bind("recorders").as(a => a.length > 0),
+ children: [
+ micIndicator("microphone"),
+ VolumeSlider("microphone"),
+ ],
+})
+
+const MixerItem = (stream: Stream) => Widget.Box(
+ {
+ hexpand: true,
+ class_name: "mixer-item horizontal",
+ },
+ Widget.Icon({
+ tooltip_text: stream.bind("name").as(n => n || ""),
+ icon: stream.bind("name").as(n => {
+ return Utils.lookUpIcon(n || "")
+ ? (n || "")
+ : icons.fallback.audio
+ }),
+ }),
+ Widget.Box(
+ { vertical: true },
+ Widget.Label({
+ xalign: 0,
+ truncate: "end",
+ max_width_chars: 28,
+ label: stream.bind("description").as(d => d || ""),
+ }),
+ Widget.Slider({
+ hexpand: true,
+ draw_value: false,
+ value: stream.bind("volume"),
+ on_change: ({ value }) => stream.volume = value,
+ }),
+ ),
+)
+
+const SinkItem = (stream: Stream) => Widget.Button({
+ hexpand: true,
+ on_clicked: () => audio.speaker = stream,
+ child: Widget.Box({
+ children: [
+ Widget.Icon({
+ icon: icon(stream.icon_name || "", icons.fallback.audio),
+ tooltip_text: stream.icon_name || "",
+ }),
+ Widget.Label((stream.description || "").split(" ").slice(0, 4).join(" ")),
+ Widget.Icon({
+ icon: icons.ui.tick,
+ hexpand: true,
+ hpack: "end",
+ visible: audio.speaker.bind("stream").as(s => s === stream.stream),
+ }),
+ ],
+ }),
+})
+
+const SettingsButton = () => Widget.Button({
+ on_clicked: () => {
+ if (dependencies("pavucontrol"))
+ sh("pavucontrol")
+ },
+ hexpand: true,
+ child: Widget.Box({
+ children: [
+ Widget.Icon(icons.ui.settings),
+ Widget.Label("Settings"),
+ ],
+ }),
+})
+
+export const AppMixer = () => Menu({
+ name: "app-mixer",
+ icon: icons.audio.mixer,
+ title: "App Mixer",
+ content: [
+ Widget.Box({
+ vertical: true,
+ class_name: "vertical mixer-item-box",
+ children: audio.bind("apps").as(a => a.map(MixerItem)),
+ }),
+ Widget.Separator(),
+ SettingsButton(),
+ ],
+})
+
+export const SinkSelector = () => Menu({
+ name: "sink-selector",
+ icon: icons.audio.type.headset,
+ title: "Sink Selector",
+ content: [
+ Widget.Box({
+ vertical: true,
+ children: audio.bind("speakers").as(a => a.map(SinkItem)),
+ }),
+ Widget.Separator(),
+ SettingsButton(),
+ ],
+})
diff --git a/linux/home/.config/ags/widget/settings/Group.ts b/linux/home/.config/ags/widget/settings/Group.ts
new file mode 100644
index 0000000..e9356e0
--- /dev/null
+++ b/linux/home/.config/ags/widget/settings/Group.ts
@@ -0,0 +1,34 @@
+import icons from "lib/icons"
+import Row from "./Row"
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export default (title: string, ...rows: ReturnType<typeof Row<any>>[]) => Widget.Box(
+ {
+ class_name: "group",
+ vertical: true,
+ },
+ Widget.Box([
+ Widget.Label({
+ hpack: "start",
+ vpack: "end",
+ class_name: "group-title",
+ label: title,
+ setup: w => Utils.idle(() => w.visible = !!title),
+ }),
+ title ? Widget.Button({
+ hexpand: true,
+ hpack: "end",
+ child: Widget.Icon(icons.ui.refresh),
+ class_name: "group-reset",
+ sensitive: Utils.merge(
+ rows.map(({ attribute: { opt } }) => opt.bind().as(v => v !== opt.initial)),
+ (...values) => values.some(b => b),
+ ),
+ on_clicked: () => rows.forEach(row => row.attribute.opt.reset()),
+ }) : Widget.Box(),
+ ]),
+ Widget.Box({
+ vertical: true,
+ children: rows,
+ }),
+)
diff --git a/linux/home/.config/ags/widget/settings/Page.ts b/linux/home/.config/ags/widget/settings/Page.ts
new file mode 100644
index 0000000..220e560
--- /dev/null
+++ b/linux/home/.config/ags/widget/settings/Page.ts
@@ -0,0 +1,19 @@
+import Group from "./Group"
+
+export default <T>(
+ name: string,
+ icon: string,
+ ...groups: ReturnType<typeof Group<T>>[]
+) => Widget.Box({
+ class_name: "page",
+ attribute: { name, icon },
+ child: Widget.Scrollable({
+ css: "min-height: 300px;",
+ child: Widget.Box({
+ class_name: "page-content",
+ vexpand: true,
+ vertical: true,
+ children: groups,
+ }),
+ }),
+})
diff --git a/linux/home/.config/ags/widget/settings/Row.ts b/linux/home/.config/ags/widget/settings/Row.ts
new file mode 100644
index 0000000..1e17096
--- /dev/null
+++ b/linux/home/.config/ags/widget/settings/Row.ts
@@ -0,0 +1,55 @@
+import { Opt } from "lib/option"
+import Setter from "./Setter"
+import icons from "lib/icons"
+
+export type RowProps<T> = {
+ opt: Opt<T>
+ title: string
+ note?: string
+ type?:
+ | "number"
+ | "color"
+ | "float"
+ | "object"
+ | "string"
+ | "enum"
+ | "boolean"
+ | "img"
+ | "font"
+ enums?: string[]
+ max?: number
+ min?: number
+}
+
+export default <T>(props: RowProps<T>) => Widget.Box(
+ {
+ attribute: { opt: props.opt },
+ class_name: "row",
+ tooltip_text: props.note ? `note: ${props.note}` : "",
+ },
+ Widget.Box(
+ { vertical: true, vpack: "center" },
+ Widget.Label({
+ xalign: 0,
+ class_name: "row-title",
+ label: props.title,
+ }),
+ Widget.Label({
+ xalign: 0,
+ class_name: "id",
+ label: props.opt.id,
+ }),
+ ),
+ Widget.Box({ hexpand: true }),
+ Widget.Box(
+ { vpack: "center" },
+ Setter(props),
+ ),
+ Widget.Button({
+ vpack: "center",
+ class_name: "reset",
+ child: Widget.Icon(icons.ui.refresh),
+ on_clicked: () => props.opt.reset(),
+ sensitive: props.opt.bind().as(v => v !== props.opt.initial),
+ }),
+)
diff --git a/linux/home/.config/ags/widget/settings/Setter.ts b/linux/home/.config/ags/widget/settings/Setter.ts
new file mode 100644
index 0000000..7e455c9
--- /dev/null
+++ b/linux/home/.config/ags/widget/settings/Setter.ts
@@ -0,0 +1,93 @@
+import { type RowProps } from "./Row"
+import { Opt } from "lib/option"
+import icons from "lib/icons"
+import Gdk from "gi://Gdk"
+
+function EnumSetter(opt: Opt<string>, values: string[]) {
+ const lbl = Widget.Label({ label: opt.bind().as(v => `${v}`) })
+ const step = (dir: 1 | -1) => {
+ const i = values.findIndex(i => i === lbl.label)
+ opt.setValue(dir > 0
+ ? i + dir > values.length - 1 ? values[0] : values[i + dir]
+ : i + dir < 0 ? values[values.length - 1] : values[i + dir],
+ )
+ }
+ const next = Widget.Button({
+ child: Widget.Icon(icons.ui.arrow.right),
+ on_clicked: () => step(+1),
+ })
+ const prev = Widget.Button({
+ child: Widget.Icon(icons.ui.arrow.left),
+ on_clicked: () => step(-1),
+ })
+ return Widget.Box({
+ class_name: "enum-setter",
+ children: [lbl, prev, next],
+ })
+}
+
+export default function Setter<T>({
+ opt,
+ type = typeof opt.value as RowProps<T>["type"],
+ enums,
+ max = 1000,
+ min = 0,
+}: RowProps<T>) {
+ switch (type) {
+ case "number": return Widget.SpinButton({
+ setup(self) {
+ self.set_range(min, max)
+ self.set_increments(1, 5)
+ self.on("value-changed", () => opt.value = self.value as T)
+ self.hook(opt, () => self.value = opt.value as number)
+ },
+ })
+
+ case "float":
+ case "object": return Widget.Entry({
+ on_accept: self => opt.value = JSON.parse(self.text || ""),
+ setup: self => self.hook(opt, () => self.text = JSON.stringify(opt.value)),
+ })
+
+ case "string": return Widget.Entry({
+ on_accept: self => opt.value = self.text as T,
+ setup: self => self.hook(opt, () => self.text = opt.value as string),
+ })
+
+ case "enum": return EnumSetter(opt as unknown as Opt<string>, enums!)
+ case "boolean": return Widget.Switch()
+ .on("notify::active", self => opt.value = self.active as T)
+ .hook(opt, self => self.active = opt.value as boolean)
+
+ case "img": return Widget.FileChooserButton({
+ on_file_set: ({ uri }) => { opt.value = uri!.replace("file://", "") as T },
+ })
+
+ case "font": return Widget.FontButton({
+ show_size: false,
+ use_size: false,
+ setup: self => self
+ .hook(opt, () => self.font = opt.value as string)
+ .on("font-set", ({ font }) => opt.value = font!
+ .split(" ").slice(0, -1).join(" ") as T),
+ })
+
+ case "color": return Widget.ColorButton()
+ .hook(opt, self => {
+ const rgba = new Gdk.RGBA()
+ rgba.parse(opt.value as string)
+ self.rgba = rgba
+ })
+ .on("color-set", ({ rgba: { red, green, blue } }) => {
+ const hex = (n: number) => {
+ const c = Math.floor(255 * n).toString(16)
+ return c.length === 1 ? `0${c}` : c
+ }
+ opt.value = `#${hex(red)}${hex(green)}${hex(blue)}` as T
+ })
+
+ default: return Widget.Label({
+ label: `no setter with type ${type}`,
+ })
+ }
+}
diff --git a/linux/home/.config/ags/widget/settings/SettingsDialog.ts b/linux/home/.config/ags/widget/settings/SettingsDialog.ts
new file mode 100644
index 0000000..be0c35e
--- /dev/null
+++ b/linux/home/.config/ags/widget/settings/SettingsDialog.ts
@@ -0,0 +1,63 @@
+import RegularWindow from "widget/RegularWindow"
+import layout from "./layout"
+import icons from "lib/icons"
+import options from "options"
+
+const current = Variable(layout[0].attribute.name)
+
+const Header = () => Widget.CenterBox({
+ class_name: "header",
+ start_widget: Widget.Button({
+ class_name: "reset",
+ on_clicked: options.reset,
+ hpack: "start",
+ vpack: "start",
+ child: Widget.Icon(icons.ui.refresh),
+ tooltip_text: "Reset",
+ }),
+ center_widget: Widget.Box({
+ class_name: "pager horizontal",
+ children: layout.map(({ attribute: { name, icon } }) => Widget.Button({
+ xalign: 0,
+ class_name: current.bind().as(v => `${v === name ? "active" : ""}`),
+ on_clicked: () => current.value = name,
+ child: Widget.Box([
+ Widget.Icon(icon),
+ Widget.Label(name),
+ ]),
+ })),
+ }),
+ end_widget: Widget.Button({
+ class_name: "close",
+ hpack: "end",
+ vpack: "start",
+ child: Widget.Icon(icons.ui.close),
+ on_clicked: () => App.closeWindow("settings-dialog"),
+ }),
+})
+
+const PagesStack = () => Widget.Stack({
+ transition: "slide_left_right",
+ children: layout.reduce((obj, page) => ({ ...obj, [page.attribute.name]: page }), {}),
+ shown: current.bind() as never,
+})
+
+export default () => RegularWindow({
+ name: "settings-dialog",
+ class_name: "settings-dialog",
+ title: "Settings",
+ setup(win) {
+ win.on("delete-event", () => {
+ win.hide()
+ return true
+ })
+ win.set_default_size(500, 600)
+ },
+ child: Widget.Box({
+ vertical: true,
+ children: [
+ Header(),
+ PagesStack(),
+ ],
+ }),
+})
diff --git a/linux/home/.config/ags/widget/settings/Wallpaper.ts b/linux/home/.config/ags/widget/settings/Wallpaper.ts
new file mode 100644
index 0000000..998f3b7
--- /dev/null
+++ b/linux/home/.config/ags/widget/settings/Wallpaper.ts
@@ -0,0 +1,31 @@
+import wallpaper from "service/wallpaper"
+
+export default () => Widget.Box(
+ { class_name: "row wallpaper" },
+ Widget.Box(
+ { vertical: true },
+ Widget.Label({
+ xalign: 0,
+ class_name: "row-title",
+ label: "Wallpaper",
+ vpack: "start",
+ }),
+ Widget.Button({
+ on_clicked: wallpaper.random,
+ label: "Random",
+ }),
+ Widget.FileChooserButton({
+ on_file_set: ({ uri }) => wallpaper.set(uri!.replace("file://", "")),
+ }),
+ ),
+ Widget.Box({ hexpand: true }),
+ Widget.Box({
+ class_name: "preview",
+ css: wallpaper.bind("wallpaper").as(wp => `
+ min-height: 120px;
+ min-width: 200px;
+ background-image: url('${wp}');
+ background-size: cover;
+ `),
+ }),
+)
diff --git a/linux/home/.config/ags/widget/settings/layout.ts b/linux/home/.config/ags/widget/settings/layout.ts
new file mode 100644
index 0000000..2b45810
--- /dev/null
+++ b/linux/home/.config/ags/widget/settings/layout.ts
@@ -0,0 +1,147 @@
+/* eslint-disable max-len */
+import Row from "./Row"
+import Group from "./Group"
+import Page from "./Page"
+import Wallpaper from "./Wallpaper"
+import options from "options"
+import icons from "lib/icons"
+
+const {
+ autotheme: at,
+ font,
+ theme,
+ bar: b,
+ launcher: l,
+ overview: ov,
+ powermenu: pm,
+ quicksettings: qs,
+ osd,
+ hyprland: h,
+} = options
+
+const {
+ dark,
+ light,
+ blur,
+ scheme,
+ padding,
+ spacing,
+ radius,
+ shadows,
+ widget,
+ border,
+} = theme
+
+export default [
+ Page("Theme", icons.ui.themes,
+ Group("",
+ Wallpaper() as ReturnType<typeof Row>,
+ Row({ opt: at, title: "Auto Generate Color Scheme" }),
+ Row({ opt: scheme, title: "Color Scheme", type: "enum", enums: ["dark", "light"] }),
+ ),
+ Group("Dark Colors",
+ Row({ opt: dark.bg, title: "Background", type: "color" }),
+ Row({ opt: dark.fg, title: "Foreground", type: "color" }),
+ Row({ opt: dark.primary.bg, title: "Primary", type: "color" }),
+ Row({ opt: dark.primary.fg, title: "On Primary", type: "color" }),
+ Row({ opt: dark.error.bg, title: "Error", type: "color" }),
+ Row({ opt: dark.error.fg, title: "On Error", type: "color" }),
+ Row({ opt: dark.widget, title: "Widget", type: "color" }),
+ Row({ opt: dark.border, title: "Border", type: "color" }),
+ ),
+ Group("Light Colors",
+ Row({ opt: light.bg, title: "Background", type: "color" }),
+ Row({ opt: light.fg, title: "Foreground", type: "color" }),
+ Row({ opt: light.primary.bg, title: "Primary", type: "color" }),
+ Row({ opt: light.primary.fg, title: "On Primary", type: "color" }),
+ Row({ opt: light.error.bg, title: "Error", type: "color" }),
+ Row({ opt: light.error.fg, title: "On Error", type: "color" }),
+ Row({ opt: light.widget, title: "Widget", type: "color" }),
+ Row({ opt: light.border, title: "Border", type: "color" }),
+ ),
+ Group("Theme",
+ Row({ opt: shadows, title: "Shadows" }),
+ Row({ opt: widget.opacity, title: "Widget Opacity", max: 100 }),
+ Row({ opt: border.opacity, title: "Border Opacity", max: 100 }),
+ Row({ opt: border.width, title: "Border Width" }),
+ Row({ opt: blur, title: "Blur", note: "0 to disable", max: 70 }),
+ ),
+ Group("UI",
+ Row({ opt: padding, title: "Padding" }),
+ Row({ opt: spacing, title: "Spacing" }),
+ Row({ opt: radius, title: "Roundness" }),
+ Row({ opt: font.size, title: "Font Size" }),
+ Row({ opt: font.name, title: "Font Name", type: "font" }),
+ ),
+ ),
+ Page("Bar", icons.ui.toolbars,
+ Group("General",
+ Row({ opt: b.flatButtons, title: "Flat Buttons" }),
+ Row({ opt: b.position, title: "Position", type: "enum", enums: ["top", "bottom"] }),
+ Row({ opt: b.corners, title: "Corners" }),
+ ),
+ Group("Launcher",
+ Row({ opt: b.launcher.icon.icon, title: "Icon" }),
+ Row({ opt: b.launcher.icon.colored, title: "Colored Icon" }),
+ Row({ opt: b.launcher.label.label, title: "Label" }),
+ Row({ opt: b.launcher.label.colored, title: "Colored Label" }),
+ ),
+ Group("Workspaces",
+ Row({ opt: b.workspaces.workspaces, title: "Number of Workspaces", note: "0 to make it dynamic" }),
+ ),
+ Group("Taskbar",
+ Row({ opt: b.taskbar.iconSize, title: "Icon Size" }),
+ Row({ opt: b.taskbar.monochrome, title: "Monochrome" }),
+ Row({ opt: b.taskbar.exclusive, title: "Exclusive to workspaces" }),
+ ),
+ Group("Date",
+ Row({ opt: b.date.format, title: "Date Format" }),
+ ),
+ Group("Media",
+ Row({ opt: b.media.monochrome, title: "Monochrome" }),
+ Row({ opt: b.media.preferred, title: "Preferred Player" }),
+ Row({ opt: b.media.direction, title: "Slide Direction", type: "enum", enums: ["left", "right"] }),
+ Row({ opt: b.media.format, title: "Format of the Label" }),
+ Row({ opt: b.media.length, title: "Max Length of Label" }),
+ ),
+ Group("Battery",
+ Row({ opt: b.battery.bar, title: "Style", type: "enum", enums: ["hidden", "regular", "whole"] }),
+ Row({ opt: b.battery.blocks, title: "Number of Blocks" }),
+ Row({ opt: b.battery.width, title: "Width of Bar" }),
+ Row({ opt: b.battery.charging, title: "Charging Color", type: "color" }),
+ ),
+ Group("Powermenu",
+ Row({ opt: b.powermenu.monochrome, title: "Monochrome" }),
+ ),
+ ),
+ Page("General", icons.ui.settings,
+ Group("Hyprland",
+ Row({ opt: h.gapsWhenOnly, title: "Gaps When Only" }),
+ ),
+ Group("Launcher",
+ Row({ opt: l.width, title: "Width" }),
+ Row({ opt: l.apps.iconSize, title: "Icon Size" }),
+ Row({ opt: l.apps.max, title: "Max Items" }),
+ ),
+ Group("Overview",
+ Row({ opt: ov.scale, title: "Scale", max: 100 }),
+ Row({ opt: ov.workspaces, title: "Workspaces", max: 11, note: "set this to 0 to make it dynamic" }),
+ Row({ opt: ov.monochromeIcon, title: "Monochrome Icons" }),
+ ),
+ Group("Powermenu",
+ Row({ opt: pm.layout, title: "Layout", type: "enum", enums: ["box", "line"] }),
+ Row({ opt: pm.labels, title: "Show Labels" }),
+ ),
+ Group("Quicksettings",
+ Row({ opt: qs.avatar.image, title: "Avatar", type: "img" }),
+ Row({ opt: qs.avatar.size, title: "Avatar Size" }),
+ Row({ opt: qs.media.monochromeIcon, title: "Media Monochrome Icons" }),
+ Row({ opt: qs.media.coverSize, title: "Media Cover Art Size" }),
+ ),
+ Group("On Screen Indicator",
+ Row({ opt: osd.progress.vertical, title: "Vertical" }),
+ Row({ opt: osd.progress.pack.h, title: "Horizontal Alignment", type: "enum", enums: ["start", "center", "end"] }),
+ Row({ opt: osd.progress.pack.v, title: "Vertical Alignment", type: "enum", enums: ["start", "center", "end"] }),
+ ),
+ ),
+] as const
diff --git a/linux/home/.config/ags/widget/settings/settingsdialog.scss b/linux/home/.config/ags/widget/settings/settingsdialog.scss
new file mode 100644
index 0000000..b8c9820
--- /dev/null
+++ b/linux/home/.config/ags/widget/settings/settingsdialog.scss
@@ -0,0 +1,144 @@
+window.settings-dialog {
+ background-color: $bg;
+ color: $fg;
+
+ .header {
+ .pager {
+ @include spacing(.5);
+ }
+
+ padding: $padding;
+
+ button {
+ @include button;
+ font-weight: bold;
+ padding: $padding*.5 $padding;
+
+ box {
+ @include spacing($spacing: .3em);
+ }
+ }
+
+ button.close {
+ padding: $padding * .5;
+ }
+
+ button.reset {
+ @include button($flat: true);
+ padding: $padding*.5;
+ }
+ }
+
+ .page {
+ @include scrollable($top: true);
+
+ .page-content {
+ padding: $padding*2;
+ padding-top: 0;
+ }
+ }
+
+ .group {
+ .group-title {
+ color: $primary-bg;
+ margin-bottom: $spacing*.5;
+ }
+
+ .group-reset {
+ @include button($flat: true);
+ margin: $spacing * .5;
+ padding: $padding * .5;
+
+ &:disabled {
+ color: transparent;
+ }
+ }
+
+ &:not(:first-child) {
+ margin-top: $spacing;
+ }
+ }
+
+ .row {
+ background-color: $widget-bg;
+ padding: $padding;
+ border: $border;
+ border-top: none;
+
+ &:first-child {
+ border-radius: $radius $radius 0 0;
+ border: $border;
+ }
+
+ &:last-child {
+ border-radius: 0 0 $radius $radius;
+ }
+
+ &:first-child:last-child {
+ border-radius: $radius;
+ border: $border;
+ }
+
+ button.reset {
+ margin-left: $spacing;
+ }
+
+ label.id,
+ label.note {
+ color: transparentize($fg, .4)
+ }
+
+ entry,
+ button {
+ @include button;
+ padding: $padding;
+ }
+
+ switch {
+ @include switch;
+ }
+
+ spinbutton {
+ @include unset;
+
+ entry {
+ border-radius: $radius 0 0 $radius;
+ }
+
+ button {
+ border-radius: 0;
+ }
+
+ button:last-child {
+ border-radius: 0 $radius $radius 0;
+ }
+ }
+
+ .enum-setter {
+ label {
+ background-color: $widget-bg;
+ border: $border;
+ padding: 0 $padding;
+ border-radius: $radius 0 0 $radius;
+ }
+
+ button {
+ border-radius: 0;
+ }
+
+ button:last-child {
+ border-radius: 0 $radius $radius 0;
+ }
+ }
+
+ &.wallpaper {
+ button {
+ margin-top: $spacing * .5;
+ }
+
+ .preview {
+ border-radius: $radius;
+ }
+ }
+ }
+}
diff --git a/linux/home/.config/betterlockscreen/betterlockscreenrc b/linux/home/.config/betterlockscreen/betterlockscreenrc
new file mode 100644
index 0000000..4cdfbe8
--- /dev/null
+++ b/linux/home/.config/betterlockscreen/betterlockscreenrc
@@ -0,0 +1,37 @@
+# ~/.config/betterlockscreenrc
+
+# default options
+display_on=0
+span_image=false
+lock_timeout=300
+fx_list=(dim blur dimblur pixel dimpixel color)
+dim_level=40
+blur_level=1
+pixel_scale=10,1000
+solid_color=333333
+wallpaper_cmd="feh --bg-fill"
+quiet=false
+# i3lockcolor_bin="i3lock-color" # Manually set command for i3lock-color
+
+# default theme
+loginbox=00000066
+loginshadow=00000000
+locktext="Type password to unlock..."
+font="sans-serif"
+ringcolor=ffffffff
+insidecolor=00000000
+separatorcolor=00000000
+ringvercolor=ffffffff
+insidevercolor=00000000
+ringwrongcolor=ffffffff
+insidewrongcolor=d23c3dff
+timecolor=ffffffff
+time_format="%H:%M:%S"
+greetercolor=ffffffff
+layoutcolor=ffffffff
+keyhlcolor=d23c3dff
+bshlcolor=d23c3dff
+verifcolor=ffffffff
+wrongcolor=d23c3dff
+modifcolor=d23c3dff
+bgcolor=000000ff
diff --git a/linux/home/.config/bspwm/bspwmrc b/linux/home/.config/bspwm/bspwmrc
new file mode 100755
index 0000000..d30d6db
--- /dev/null
+++ b/linux/home/.config/bspwm/bspwmrc
@@ -0,0 +1,275 @@
+#! /bin/sh
+
+################################################################################
+# ██████╗ ███████╗██████╗ ██╗ ██╗███╗ ███╗ #
+# ██╔══██╗██╔════╝██╔══██╗██║ ██║████╗ ████║ #
+# ██████╔╝███████╗██████╔╝██║ █╗ ██║██╔████╔██║ #
+# ██╔══██╗╚════██║██╔═══╝ ██║███╗██║██║╚██╔╝██║ #
+# ██████╔╝███████║██║ ╚███╔███╔╝██║ ╚═╝ ██║ #
+# ╚═════╝ ╚══════╝╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝ #
+################################## By: srdusr ##################################
+
+# ##############################################################################
+# # ENV VARS #
+# ##############################################################################
+
+## Environments
+export PATH="${PATH}:${HOME}/.config/bspwm/bin"
+
+# Get the name of the primary monitor
+mainmonitor=$(xrandr --query | awk '/ primary/{print $1}')
+
+## Monitors
+# If no primary monitor is identified, use the first connected monitor
+if [ "$mainmonitor" = "" ]; then
+ mainmonitor=$(xrandr --query | awk '/ connected/ {print $1; exit}')
+fi
+
+# Set up workspaces on the primary monitor
+bspc monitor "$mainmonitor" -d 󰲡 󰲣 󰲥 󰲧 󰲩 #1 2 3 4 5
+
+# Check the number of connected monitors
+connected_monitors=$(xrandr --query | grep -c " connected")
+
+if [ "$connected_monitors" -gt 1 ]; then
+ # Get the name of the secondary monitor (exclude the primary monitor)
+ secondmonitor=$(xrandr --query | awk '/ connected/ && $1 != "'"$mainmonitor"'" {print $1; exit}')
+
+ # Set up workspaces on the secondary monitor
+ bspc monitor "$secondmonitor" -d 󰲫 󰲭 󰲯 󰲱 󰿭 #6 7 8 9 10
+ # Check if the secondary monitor is connected and configure the layout
+ if [ "$secondmonitor" != "" ]; then
+ xrandr --output "$mainmonitor" --primary --auto --output "$secondmonitor" --auto --right-of "$mainmonitor"
+ fi
+fi
+
+#INTERNAL_MONITOR="LVDS-1"
+#EXTERNAL_MONITOR="HDMI-1"
+## on first load setup default workspaces
+#if [[ "$1" = 0 ]]; then
+# if [[ $(xrandr -q | grep "${EXTERNAL_MONITOR} connected") ]]; then
+# bspc monitor "$EXTERNAL_MONITOR" -d 1 2 3 4 5
+# bspc monitor "$INTERNAL_MONITOR" -d 6 7 8 9 10
+# bspc wm -O "$EXTERNAL_MONITOR" "$INTERNAL_MONITOR"
+# else
+# bspc monitor "$INTERNAL_MONITOR" -d 1 2 3 4 5 6 7 8 9 10
+# fi
+#fi
+
+# ##############################################################################
+# # FUNCTIONS #
+# ##############################################################################
+
+config() { bspc config "$@" & }
+rule() { bspc rule -a "$@" & }
+run_once() {
+ if [ ! "$(pgrep -f "$1")" ]; then
+ "$@" &
+ fi
+}
+
+# ##############################################################################
+# # WINDOW RULES #
+# ##############################################################################
+
+## Rules
+bspc rule -r *:* # remove all rules first
+rule '*' --one-shot state=below private=border_width:10
+#rule '*:Tiled' --one-shot state=tiled rectangle=50x50+0+50
+#rule '*' --one-shot state=floating rectangle=1028x374+0+50
+#rule \* rectangle=680x700+340+40
+rule '*:*:Picture-in-Picture' state=floating sticky=on layer=above
+rule '*:*:Picture in picture' state=floating sticky=on layer=above
+rule firefox:Toolkit focus=on state=floating sticky=on layer=above rectangle=400x280+955+475 #320x190+1030+480 #522x316-10+280
+rule "https://www.youtube.com - Enhancer for YouTube™ — Mozilla Firefox" state=floating sticky=on layer=above
+rule Wezterm state=floating
+rule Zathura state=tiled
+rule Pavucontrol state=floating rectangle=490x260+862+37
+rule Blueman-manager state=floating rectangle=536x420+818+37 #490x260-9+37
+rule scratchpad sticky=on state=floating # SCRATCHPAD
+rule heads-up-display sticky=on state=floating rectangle=360x160+990+35 # Heads Up Display (scratchpad)
+rule Onboard sticky=on state=floating rectangle=700x205+480-89 # Virtual keyboard
+rule Plank layer=above border=off
+rule Protonvpn state=floating
+rule qBittorrent desktop='^2'
+rule discord desktop='^4'
+rule firefox -o desktop=^1
+rule stalonetray state=floating manage=off
+#bspc rule -a Spotify:spotify desktop='^󰲥' state=tiled
+#bspc rule -a '*:spotify' desktop='^3' state=tiled
+
+# ##############################################################################
+# # AUTOSTART APPS #
+# ##############################################################################
+
+# Clear cache
+#rm "$HOME"/.cache/dunst.log
+#rm "$HOME"/.cache/fake_battery_capacity
+#rm "$HOME"/.cache/eww-calendar.lock
+#rm "$HOME"/.cache/eww-escreen.lock
+#rm "$HOME"/.cache/eww-control-center.lock
+#rm -r "$HOME"/.cache/dunst/
+
+# Autostart applications
+#"$HOME"/.config/bspwm/scripts/bspwm_setup_monitors &
+pgrep -x sxhkd > /dev/null || sxhkd &
+pgrep -x plank > /dev/null || plank &
+pgrep -x jgmenu > /dev/null || bspc rule -a jgmenu desktop='^H' state=floating hidden=on && jgmenu --hide-on-startup &
+picom --config "$HOME"/.config/picom/picom.conf &
+rm "$HOME"/.jgmenu-lockfile
+"$HOME"/.config/polybar/launch.sh &
+run_once unclutter & # Remove mouse when idle
+run_once "$HOME"/.scripts/lockscreen-wallpaper &
+run_once xss-lock -- betterlockscreen -l &
+nitrogen --force-setter=xinerama --restore &
+run_once redshift &
+run_once low-bat-notifier &
+pkill persistentQuickUtilities.sh; "$HOME"/.config/bspwm/scripts/persistentQuickUtilities.sh &
+#xfce4-panel --disable-wm-check &
+
+# Start polkit agent
+#[ "$(pidof xfce-polkit)" != "" ] || /usr/lib/xfce-polkit/xfce-polkit &
+run_once /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 &
+
+# Volume and brightness indicator (xob)
+source "$HOME"/.virtualenvs/bin/activate # Activate virtual environment
+run_once "$HOME"/.config/xob/launch.sh &
+deactivate # Deactivate virtual environment
+
+# Eww
+pkill eww
+eww daemon
+
+xset m 0 0 # Disable mouse drift
+
+pgrep -x plank.sh > /dev/null || plank.sh &
+
+#wmname LG3D # Fixes Java applications
+# Solve java apps issues (e.g. JetBrains IDEs like PyCharm, CLion, etc). #
+#export _JAVA_AWT_WM_NONREPARENTING=1
+
+#export QT_QPA_PLATFORMTHEME="qt5ct" # Use qt5ct to set Qt theme
+
+# Start MPD and mpDris2
+#exec mpd &
+#exec mpDris2 &
+
+# Systray
+#run_once signal-desktop --start-in-tray & #--use-tray-icon
+#run_once onboard --not-show-in=DESKTOPS &
+if ! pgrep -x "stalonetray" > /dev/null; then
+ stalonetray &
+fi
+
+declare -a restart=(clipit blueman-applet caffeine)
+for i in "${restart[@]}"; do
+ pgrep -x "$i" | xargs kill
+ sleep 0.5
+ eval "$i" &
+done
+
+while ! pgrep -x "clipit" > /dev/null || ! pgrep -x "blueman-applet" > /dev/null || ! pgrep -x "caffeine" > /dev/null; do
+ #sleep 0.5
+ systray &
+done
+xdo hide -N stalonetray
+touch "/tmp/syshide.lock"
+
+# ##############################################################################
+# # CONFIGURATION #
+# ##############################################################################
+
+## Config
+PANEL_HEIGHT=24
+config top_padding "$PANEL_HEIGHT"
+config honor_size_hints true
+config automatic_scheme alternate
+config initial_polarity second_child
+config pointer_modifier mod4
+config click_to_focus none
+config pointer_action1 move
+config pointer_action2 resize_side
+config pointer_action3 resize_corner
+config focus_follows_pointer true
+config remove_disabled_monitors true
+config remove_unplugged_monitors true
+config merge_overlapping_monitors true
+config border_width 4
+config border_radius 10
+config window_gap 10
+config split_ratio 0.52
+config borderless_monocle true
+config gapless_monocle true
+config swallow_first_click false
+config normal_border_color "#000000"
+config focused_border_color "#000000"
+config active_border_color "#000000"
+config presel_feedback_color "#BF616A"
+
+# ##############################################################################
+# # MISCELLANEOUS #
+# ##############################################################################
+
+## Fullscreen
+bspc subscribe node_state | while read -r _ _ _ _ state flag; do
+ if [[ "$state" != fullscreen ]]; then continue; fi
+ if [[ "$flag" == on ]]; then
+ xdo lower -N Plank
+ #"$(which eww)" -c "$HOME"/.config/eww close-all
+ else
+ xdo raise -N Plank
+ #"$(which eww)" -c "$HOME"/.config/eww open bar
+ fi
+done &
+
+## Title-bar
+rm -rf /tmp/title-bar_debug.log
+rm -rf /tmp/title-bar.lock
+
+processes=("title-bar" "update-title" "lemonbar")
+
+for process in "${processes[@]}"; do
+ if pidof -q "$process"; then
+ pkill -x "$process" > /dev/null; sleep 0.1
+ fi
+done
+
+# Check if title-bar is already running
+if ! pgrep -x "title-bar" >/dev/null; then
+ # Create a lock file
+ lockfile="/tmp/title-bar.lock"
+
+ # Check if the lock file exists
+ if [ ! -e "$lockfile" ]; then
+ # Create the lock file
+ touch "$lockfile"
+
+ # Function to handle BSPWM events
+ handle_bspwm_events() {
+ while read; do
+ if ! pgrep -x "title-bar" >/dev/null; then
+ bash "$HOME/.scripts/title-bar" &
+ fi
+ done
+ }
+
+ # Start bspc subscribe in the background and pass events to the handler function
+ bspc subscribe | handle_bspwm_events &
+
+ # Remove the lock file when the script exits
+ trap 'rm -f "$lockfile"' EXIT
+ else
+ echo "title-bar is already running."
+ fi
+fi
+
+start_dunst() {
+ # stop dunst if it has been started by any application that called notify-send
+ killall -q dunst
+ # Wait until the processes have been shut down
+ while pgrep -u "$UID" -x dunst >/dev/null; do sleep .05; done
+ while ! pgrep -u "$UID" -x dunst >/dev/null; do sleep .05; done
+}
+start_dunst &
+
+config external_rules_command ~/.config/bspwm/scripts/external_rules.sh &
diff --git a/linux/home/.config/bspwm/scripts/bspdragtofloat b/linux/home/.config/bspwm/scripts/bspdragtofloat
new file mode 100755
index 0000000..e2f88a2
--- /dev/null
+++ b/linux/home/.config/bspwm/scripts/bspdragtofloat
@@ -0,0 +1,46 @@
+#!/bin/env bash
+
+: "${BSPWM_DIR:="${XDG_CONFIG_HOME:-$HOME/.config}/bspwm"}"
+
+status_file="$BSPWM_DIR/tmp/drag_to_float"
+
+[[ "$1" = stop ]] && {
+ [[ -e "$status_file" ]] \
+ && rm -r -- "$status_file"
+ exit
+}
+
+[[ -e "$status_file" ]] \
+ && exit
+
+< <(bspc query -T -n pointed.window | jq -r '"\(.id) \(.client.state)"') read -r node node_state
+
+[[ -z "$node" ]] \
+ && exit
+
+case "$node_state" in
+ floating)
+ ;;
+ tiled|pseudo_tiled)
+ node_tiled_rect=($(bspc query -T -n "$node" | jq -r '.client.tiledRectangle[]'))
+ bspc node "$node" -t floating
+ xdo move -x "${node_tiled_rect[0]}" -y "${node_tiled_rect[1]}" "$node"
+ xdo resize -w "${node_tiled_rect[2]}" -h "${node_tiled_rect[3]}" "$node" ;;
+ *) # fullscreen
+ exit ;;
+esac
+
+eval "$(xdotool getmouselocation --shell)"
+x="$X" y="$Y"
+touch -- "$status_file"
+while [[ -e "$status_file" ]]; do
+ eval "$(xdotool getmouselocation --shell)"
+ (( X != x || Y != y )) && {
+ bspc node "$node" -v "$((X - x))" "$((Y - y))"
+ x="$X" y="$Y"
+ }
+ sleep .01
+done
+
+[[ -e "$status_file" ]] \
+ && rm -r -- "$status_file"
diff --git a/linux/home/.config/bspwm/scripts/bspswallow b/linux/home/.config/bspwm/scripts/bspswallow
new file mode 100755
index 0000000..dea343f
--- /dev/null
+++ b/linux/home/.config/bspwm/scripts/bspswallow
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+NODE_CURRENT=$(bspc query -N -n focused)
+$@ &
+PID_COMMAND=$!
+WATCH=$(bspc subscribe -c 1 node_add)
+NODE_NEW=${WATCH%% *}
+bspc node -s $NODE_CURRENT
+bspc node $NODE_CURRENT --flag hidden=on
+wait $PID_COMMAND
+bspc node $NODE_CURRENT --flag hidden=off
+bspc node $NODE_CURRENT --focus
diff --git a/linux/home/.config/bspwm/scripts/bspwindows b/linux/home/.config/bspwm/scripts/bspwindows
new file mode 100755
index 0000000..26deeab
--- /dev/null
+++ b/linux/home/.config/bspwm/scripts/bspwindows
@@ -0,0 +1,14 @@
+#!/bin/sh
+# bspwindows
+# get targets for drawing borders/whatever on in bspwm
+
+target="${1:-active}"
+
+case "$target" in
+ active)
+ bspc query -N -n .local.descendant_of.window.leaf.!fullscreen
+ ;;
+ inactive)
+ bspc query -N -n .local.!descendant_of.window.leaf.!fullscreen
+ ;;
+esac
diff --git a/linux/home/.config/bspwm/scripts/bspwm-monitor-setup b/linux/home/.config/bspwm/scripts/bspwm-monitor-setup
new file mode 100755
index 0000000..6e38bb7
--- /dev/null
+++ b/linux/home/.config/bspwm/scripts/bspwm-monitor-setup
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+
+INTERNAL_MONITOR="eDP"
+EXTERNAL_MONITOR="HDMI-A-0"
+
+monitor_add() {
+ # Move first 5 desktops to external monitor
+ for desktop in $(bspc query -D --names -m "$INTERNAL_MONITOR" | sed 5q); do
+ bspc desktop "$desktop" --to-monitor "$EXTERNAL_MONITOR"
+ done
+ # Remove default desktop created by bspwm
+ bspc desktop Desktop --remove
+ # reorder monitors
+ bspc wm -O "$EXTERNAL_MONITOR" "$INTERNAL_MONITOR"
+}
+
+monitor_remove() {
+ # Add default temp desktop because a minimum of one desktop is required per monitor
+ bspc monitor "$EXTERNAL_MONITOR" -a Desktop
+
+ # Move all desktops except the last default desktop to internal monitor
+ for desktop in $(bspc query -D -m "$EXTERNAL_MONITOR"); do
+ bspc desktop "$desktop" --to-monitor "$INTERNAL_MONITOR"
+ done
+
+ # delete default desktops
+ bspc desktop Desktop --remove
+ # reorder desktops
+ bspc monitor "$INTERNAL_MONITOR" -o 1 2 3 4 5 6 7 8 9 10
+}
+
+if [[ $(xrandr -q | grep "${EXTERNAL_MONITOR} connected") ]]; then
+ # set xrandr rules for docked setup
+ xrandr --output "$INTERNAL_MONITOR" --mode 1920x1080 --pos 0x0 --rotate normal --output "$EXTERNAL_MONITOR" --primary --mode 1920x1080 --pos 1920x780 --rotate normal
+ if [[ $(bspc query -D -m "${EXTERNAL_MONITOR}" | wc -l) -ne 5 ]]; then
+ monitor_add
+ fi
+ bspc wm -O "$EXTERNAL_MONITOR" "$INTERNAL_MONITOR"
+else
+ # set xrandr rules for mobile setup
+ xrandr --output "$INTERNAL_MONITOR" --primary --mode 1920x1080 --pos 0x0 --rotate normal --output "$EXTERNAL_MONITOR" --off
+ if [[ $(bspc query -D -m "${INTERNAL_MONITOR}" | wc -l) -ne 10 ]]; then
+ monitor_remove
+ fi
+fi
+
+# Set wallpaper
+~/.local/bin/setbg.sh &
+
+# Kill and relaunch polybar
+kill -9 $(pgrep -f 'polybar') >/dev/null 2>&1
+polybar-msg cmd quit >/dev/null 2>&1
+while pgrep -u $UID -x polybar >/dev/null; do sleep 1; done
+if [[ $(xrandr -q | grep "${EXTERNAL_MONITOR} connected") ]]; then
+ polybar --reload primary -c ~/.config/polybar/config.ini </dev/null >/var/tmp/polybar-primary.log 2>&1 200>&- &
+ polybar --reload secondary -c ~/.config/polybar/config.ini </dev/null >/var/tmp/polybar-secondary.log 2>&1 200>&- &
+else
+ polybar --reload primary -c ~/.config/polybar/config.ini </dev/null >/var/tmp/polybar-primary.log 2>&1 200>&- &
+fi
diff --git a/linux/home/.config/bspwm/scripts/bspwm-toggle-visibility.sh b/linux/home/.config/bspwm/scripts/bspwm-toggle-visibility.sh
new file mode 100755
index 0000000..45a4c53
--- /dev/null
+++ b/linux/home/.config/bspwm/scripts/bspwm-toggle-visibility.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Created By: srdusr
+# Created On: Mon 18 Sep 2023 18:37:21 CAT
+# Project: Bspwm script to toggle visibility of initial window and bring focus back to it
+
+# Get the ID of the currently focused desktop
+current_desktop_id=$(bspc query -D -d focused --names)
+
+# Get the ID of the first hidden window in the current desktop
+hidden_window_id=$(bspc query -N -d "$current_desktop_id" -n .hidden | head -n 1)
+
+# Check if there's a hidden window in the current desktop
+if [[ -n "$hidden_window_id" ]]; then
+ # There's a hidden window, so unhide it
+ bspc node "$hidden_window_id" -g hidden=off
+ # Bring focus back to the previously hidden window
+ bspc node -f "$hidden_window_id"
+else
+ # There's no hidden window in the current desktop, hide the first available window
+ first_window_id=$(bspc query -N -n focused.window)
+ bspc node "$first_window_id" -g hidden=on
+fi
diff --git a/linux/home/.config/bspwm/scripts/close-window b/linux/home/.config/bspwm/scripts/close-window
new file mode 100755
index 0000000..8f7f36b
--- /dev/null
+++ b/linux/home/.config/bspwm/scripts/close-window
@@ -0,0 +1,11 @@
+#!/bin/sh
+#
+# close or kill a bspwm window
+
+# show wallpaper if last tile
+bspc query -N -n .focused.tiled.window &> /dev/null \
+ && ! bspc query -N -n .!focused.local.tiled.window &> /dev/null \
+ && show-wallpaper -d
+
+# close focused window
+[ "$1" = "kill" ] && bspc node -k || bspc node -c
diff --git a/linux/home/.config/bspwm/scripts/drag-float b/linux/home/.config/bspwm/scripts/drag-float
new file mode 100755
index 0000000..788e978
--- /dev/null
+++ b/linux/home/.config/bspwm/scripts/drag-float
@@ -0,0 +1,42 @@
+#!/bin/env bash
+
+: "${BUTTON:=1}"
+
+node="$(bspc query -N -n pointed)"
+
+die() {
+ jobs -p | xargs -r -n1 -I{} kill {}
+ exit
+}
+
+trap 'die' USR1
+
+{ bspc subscribe node_focus | while read -r _ _ _ wid; do
+ (( wid != node )) && break; done; kill -USR1 "$$" ;} &
+{ while xinput list \
+ | sed -nE 's,.*id=([0-9]+).*slave\s+pointer.*,\1,p' \
+ | xargs -r -n1 -I{} xinput query-state {} 2> /dev/null \
+ | grep -qF "button[${BUTTON}]=down"; do sleep .3; done; kill -USR1 "$$" ;} &
+
+if bspc node "$node.tiled" -f; then
+ node_tiled_rect=($(bspc query -T -n "$node" | jq -r '.client.tiledRectangle[]'))
+ bspc node "$node" -t floating
+ xdo move -x "${node_tiled_rect[0]}" -y "${node_tiled_rect[1]}" "$node"
+ xdo resize -w "${node_tiled_rect[2]}" -h "${node_tiled_rect[3]}" "$node"
+elif bspc node "$node.floating" -f; then
+ :
+else
+ die
+fi
+
+eval "$(xdotool getmouselocation --shell)"
+x="$X" y="$Y"
+while :; do
+ eval "$(xdotool getmouselocation --shell)"
+ (( X != x || Y != y )) && {
+ bspc node "$node" -v "$((X - x))" "$((Y - y))"
+ x="$X" y="$Y"
+ }
+done
+
+wait
diff --git a/linux/home/.config/bspwm/scripts/external_rules.sh b/linux/home/.config/bspwm/scripts/external_rules.sh
new file mode 100755
index 0000000..7b07ae8
--- /dev/null
+++ b/linux/home/.config/bspwm/scripts/external_rules.sh
@@ -0,0 +1,71 @@
+#!/bin/env bash
+#
+# external_rules_command
+#
+# Absolute path to the command used to retrieve rule consequences.
+# The command will receive the following arguments: window ID, class
+# name, instance name, and intermediate consequences. The output of
+# that command must have the following format: key1=value1
+# key2=value2 ... (the valid key/value pairs are given in the
+# description of the rule command).
+#
+#
+# Rule
+# General Syntax
+# rule COMMANDS
+#
+# Commands
+# -a, --add (<class_name>|*)[:(<instance_name>|*)] [-o|--one-shot]
+# [monitor=MONITOR_SEL|desktop=DESKTOP_SEL|node=NODE_SEL]
+# [state=STATE] [layer=LAYER] [split_dir=DIR] [split_ratio=RATIO]
+# [(hidden|sticky|private|locked|marked|center|follow|manage|focus|border)=(on|off)]
+# [rectangle=WxH+X+Y]
+# Create a new rule.
+#
+# -r, --remove
+# ^<n>|head|tail|(<class_name>|*)[:(<instance_name>|*)]...
+# Remove the given rules.
+#
+# -l, --list
+# List the rules.
+
+# Programs to specific desktops
+wid=$1
+class=$2
+instance=$3
+consequences=$4
+
+main() {
+ case "$class" in
+ firefox)
+ if [ "$instance" = "Toolkit" ]; then
+ echo "state=floating sticky=on"
+ fi
+ ;;
+ Spotify)
+ echo desktop=^5 follow=on focus=on
+ ;;
+ "")
+ sleep 0.5
+
+ wm_class=("$(xprop -id "$wid" | grep "WM_CLASS" | grep -Po '"\K[^,"]+')")
+
+ class=${wm_class[-1]}
+
+ [[ ${#wm_class[@]} == "2" ]] && instance=${wm_class[0]}
+
+ [[ -n "$class" ]] && main
+ ;;
+ esac
+}
+
+main
+
+# Allow floating windows over fullscreen
+wid="$1"
+class="$2"
+instance="$3"
+eval "$4"
+
+[[ "$state" = floating ]] &&
+ echo 'layer=above'
diff --git a/linux/home/.config/bspwm/scripts/hide-window b/linux/home/.config/bspwm/scripts/hide-window
new file mode 100755
index 0000000..c350a0e
--- /dev/null
+++ b/linux/home/.config/bspwm/scripts/hide-window
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+CMD=${1:-help}
+shift
+
+help() {
+ echo "Available commands:"
+ echo " * unhide - select and unhide window"
+}
+
+unhide() {
+ action=${1:-list}
+ case $action in
+ "list")
+ selection=$(for id in "$(bspc query -N -n .hidden)"; do
+ title=$(xtitle "$id")
+ [[ -z "$title" ]] && title="<unnamed>"
+ echo "$id" "$title"
+ done | rofi -dmenu -i -p "Hidden windows" | cut -f1 -d' ')
+
+ [[ -z "$selection" ]] && exit 1
+
+ bspc node "$selection" -g hidden=off
+ ;;
+ esac
+ }
+
+ case $CMD in
+ "help")
+ help
+ ;;
+ "unhide")
+ unhide "$1"
+ ;;
+ *)
+ help
+ ;;
+ esac
diff --git a/linux/home/.config/dunst/assets/notification/fallback.png b/linux/home/.config/dunst/assets/notification/fallback.png
new file mode 100644
index 0000000..204aeda
--- /dev/null
+++ b/linux/home/.config/dunst/assets/notification/fallback.png
Binary files differ
diff --git a/linux/home/.config/dunst/assets/notification/music.png b/linux/home/.config/dunst/assets/notification/music.png
new file mode 100644
index 0000000..ae91be3
--- /dev/null
+++ b/linux/home/.config/dunst/assets/notification/music.png
Binary files differ
diff --git a/linux/home/.config/dunst/assets/notification/scrot.png b/linux/home/.config/dunst/assets/notification/scrot.png
new file mode 100644
index 0000000..1e12f38
--- /dev/null
+++ b/linux/home/.config/dunst/assets/notification/scrot.png
Binary files differ
diff --git a/linux/home/.config/dunst/assets/ui/volume-high.svg b/linux/home/.config/dunst/assets/ui/volume-high.svg
new file mode 100644
index 0000000..43152c9
--- /dev/null
+++ b/linux/home/.config/dunst/assets/ui/volume-high.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#eceff4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-volume-2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path></svg> \ No newline at end of file
diff --git a/linux/home/.config/dunst/assets/ui/volume-low.svg b/linux/home/.config/dunst/assets/ui/volume-low.svg
new file mode 100644
index 0000000..09b3650
--- /dev/null
+++ b/linux/home/.config/dunst/assets/ui/volume-low.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#eceff4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-volume"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon></svg> \ No newline at end of file
diff --git a/linux/home/.config/dunst/assets/ui/volume-medium.svg b/linux/home/.config/dunst/assets/ui/volume-medium.svg
new file mode 100644
index 0000000..6b3c1fa
--- /dev/null
+++ b/linux/home/.config/dunst/assets/ui/volume-medium.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#eceff4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-volume-1"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path></svg> \ No newline at end of file
diff --git a/linux/home/.config/dunst/assets/ui/volume-muted.svg b/linux/home/.config/dunst/assets/ui/volume-muted.svg
new file mode 100644
index 0000000..50434d4
--- /dev/null
+++ b/linux/home/.config/dunst/assets/ui/volume-muted.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#BF616A" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-volume-x"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><line x1="23" y1="9" x2="17" y2="15"></line><line x1="17" y1="9" x2="23" y2="15"></line></svg> \ No newline at end of file
diff --git a/linux/home/.config/dunst/dunstrc b/linux/home/.config/dunst/dunstrc
new file mode 100644
index 0000000..8989a2d
--- /dev/null
+++ b/linux/home/.config/dunst/dunstrc
@@ -0,0 +1,456 @@
+# See dunst(5) for all configuration options
+
+[global]
+ ### Display ###
+
+ # Which monitor should the notifications be displayed on.
+ monitor = 0
+
+ # Display notification on focused monitor. Possible modes are:
+ # mouse: follow mouse pointer
+ # keyboard: follow window with keyboard focus
+ # none: don't follow anything
+ #
+ # "keyboard" needs a window manager that exports the
+ # _NET_ACTIVE_WINDOW property.
+ # This should be the case for almost all modern window managers.
+ #
+ # If this option is set to mouse or keyboard, the monitor option
+ # will be ignored.
+ follow = none
+
+ ### Geometry ###
+
+ # dynamic width from 0 to 300
+ width = (200, 300)
+ # constant width of 300
+ # width = 300
+
+ # The maximum height of a single notification, excluding the frame.
+ height = 300
+
+ # Position the notification in the top right corner
+ origin = bottom-right
+
+ # Offset from the origin
+ offset = 6x30
+ # offset = 10x35 # i3 gaps = 20x20
+ # (H_Right_Gap/2) x (25+V_Bottom_Gap/2)
+
+ # Scale factor. It is auto-detected if value is 0.
+ scale = 0
+
+ # Maximum number of notification (0 means no limit)
+ notification_limit = 0
+
+ ### Progress bar ###
+
+ # Turn on the progess bar. It appears when a progress hint is passed with
+ # for example dunstify -h int:value:12
+ progress_bar = true
+
+ # Set the progress bar height. This includes the frame, so make sure
+ # it's at least twice as big as the frame width.
+ progress_bar_height = 10
+
+ # Set the frame width of the progress bar
+ progress_bar_frame_width = 1
+
+ # Set the minimum width for the progress bar
+ progress_bar_min_width = 150
+
+ # Set the maximum width for the progress bar
+ progress_bar_max_width = 300
+
+ # Show how many messages are currently hidden (because of
+ # notification_limit).
+ indicate_hidden = yes
+
+ # The transparency of the window. Range: [0; 100].
+ # This option will only work if a compositing window manager is
+ # present (e.g. xcompmgr, compiz, etc.). (X11 only)
+ transparency = 0
+
+ # Draw a line of "separator_height" pixel height between two
+ # notifications.
+ # Set to 0 to disable.
+ separator_height = 2
+
+ # Padding between text and separator.
+ padding = 11
+
+ # Horizontal padding.
+ horizontal_padding = 11
+
+ # Padding between text and icon.
+ text_icon_padding = 0
+
+ # Defines width in pixels of frame around the notification window.
+ # Set to 0 to disable.
+ frame_width = 2
+
+ # Define a color for the separator.
+ # possible values are:
+ # * auto: dunst tries to find a color fitting to the background;
+ # * foreground: use the same color as the foreground;
+ # * frame: use the same color as the frame;
+ # * anything else will be interpreted as a X color.
+ separator_color = "#1a1b26"
+
+ # Sort messages by urgency.
+ sort = yes
+
+ # Don't remove messages, if the user is idle (no mouse or keyboard input)
+ # for longer than idle_threshold seconds.
+ # Set to 0 to disable.
+ # A client can set the 'transient' hint to bypass this. See the rules
+ # section for how to disable this if necessary
+ idle_threshold = 120
+
+ ### Text ###
+
+ font = JetBrainsMono Nerd Font Medium 9
+
+ # The spacing between lines. If the height is smaller than the
+ # font height, it will get raised to the font height.
+ line_height = 5
+
+ # Possible values are:
+ # full: Allow a small subset of html markup in notifications:
+ # <b>bold</b>
+ # <i>italic</i>
+ # <s>strikethrough</s>
+ # <u>underline</u>
+ #
+ # For a complete reference see
+ # <https://docs.gtk.org/Pango/pango_markup.html>.
+ #
+ # strip: This setting is provided for compatibility with some broken
+ # clients that send markup even though it's not enabled on the
+ # server. Dunst will try to strip the markup but the parsing is
+ # simplistic so using this option outside of matching rules for
+ # specific applications *IS GREATLY DISCOURAGED*.
+ #
+ # no: Disable markup parsing, incoming notifications will be treated as
+ # plain text. Dunst will not advertise that it has the body-markup
+ # capability if this is set as a global setting.
+ #
+ # It's important to note that markup inside the format option will be parsed
+ # regardless of what this is set to.
+ markup = full
+
+ # The format of the message. Possible variables are:
+ # %a appname
+ # %s summary
+ # %b body
+ # %i iconname (including its path)
+ # %I iconname (without its path)
+ # %p progress value if set ([ 0%] to [100%]) or nothing
+ # %n progress value if set without any extra characters
+ # %% Literal %
+ # Markup is allowed
+ format = "<b>%s</b>\n%b"
+
+ # Alignment of message text.
+ # Possible values are "left", "center" and "right".
+ alignment = center
+
+ # Vertical alignment of message text and icon.
+ # Possible values are "top", "center" and "bottom".
+ vertical_alignment = center
+
+ # Show age of message if message is older than show_age_threshold
+ # seconds.
+ # Set to -1 to disable.
+ show_age_threshold = 60
+
+ # Specify where to make an ellipsis in long lines.
+ # Possible values are "start", "middle" and "end".
+ ellipsize = middle
+
+ # Ignore newlines '\n' in notifications.
+ ignore_newline = no
+
+ # Stack together notifications with the same content
+ stack_duplicates = true
+
+ # Hide the count of stacked notifications with the same content
+ hide_duplicate_count = true
+
+ # Display indicators for URLs (U) and actions (A).
+ show_indicators = yes
+
+ ### Icons ###
+
+ # Align icons left/right/off
+ icon_position = left
+
+ # Scale small icons up to this size, set to 0 to disable. Helpful
+ # for e.g. small files or high-dpi screens. In case of conflict,
+ # max_icon_size takes precedence over this.
+ min_icon_size = 0
+
+ # Scale larger icons down to this size, set to 0 to disable
+ max_icon_size = 32
+
+ # Paths to default icons.
+ #icon_path = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
+ #icon_path = /usr/share/icons/Qogir/16/status:/usr/share/icons/Qogir/16/devices/:/usr/share/icons/Qogir/16/apps/:/usr/share/pixmaps/
+ icon_path = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
+
+ ### History ###
+
+ # Should a notification popped up from history be sticky or timeout
+ # as if it would normally do.
+ sticky_history = yes
+
+ #history_file = ~/.cache/dunst/history
+
+ # Maximum amount of notifications kept in history
+ rhistory_length = 100
+
+ ### Misc/Advanced ###
+
+ # dmenu path.
+ dmenu = /usr/bin/dmenu -p dunst:
+
+ # Browser for opening urls in context menu.
+ browser = /usr/bin/brave
+
+ # Always run rule-defined scripts, even if the notification is suppressed
+ always_run_script = true
+
+ # Define the title of the windows spawned by dunst
+ title = Dunst
+
+ # Define the class of the windows spawned by dunst
+ class = Dunst
+
+ # Define the corner radius of the notification window
+ # in pixel size. If the radius is 0, you have no rounded
+ # corners.
+ # The radius will be automatically lowered if it exceeds half of the
+ # notification height to avoid clipping text and/or icons.
+ corner_radius = 10
+
+ # Ignore the dbus closeNotification message.
+ # Useful to enforce the timeout set by dunst configuration. Without this
+ # parameter, an application may close the notification sent before the
+ # user defined timeout.
+ ignore_dbusclose = false
+
+ ### Wayland ###
+ # These settings are Wayland-specific. They have no effect when using X11
+
+ # Uncomment this if you want to let notications appear under fullscreen
+ # applications (default: overlay)
+ # layer = top
+
+ # Set this to true to use X11 output on Wayland.
+ force_xwayland = false
+
+ ### Legacy
+
+ # Use the Xinerama extension instead of RandR for multi-monitor support.
+ # This setting is provided for compatibility with older nVidia drivers that
+ # do not support RandR and using it on systems that support RandR is highly
+ # discouraged.
+ #
+ # By enabling this setting dunst will not be able to detect when a monitor
+ # is connected or disconnected which might break follow mode if the screen
+ # layout changes.
+ force_xinerama = false
+
+ ### mouse
+
+ # Defines list of actions for each mouse event
+ # Possible values are:
+ # * none: Don't do anything.
+ # * do_action: Invoke the action determined by the action_name rule. If there is no
+ # such action, open the context menu.
+ # * open_url: If the notification has exactly one url, open it. If there are multiple
+ # ones, open the context menu.
+ # * close_current: Close current notification.
+ # * close_all: Close all notifications.
+ # * context: Open context menu for the notification.
+ # * context_all: Open context menu for all notifications.
+ # These values can be strung together for each mouse event, and
+ # will be executed in sequence.
+ mouse_left_click = close_current
+ mouse_middle_click = do_action, close_current
+ mouse_right_click = close_all
+
+# Experimental features that may or may not work correctly. Do not expect them
+# to have a consistent behaviour across releases.
+[experimental]
+ # Calculate the dpi to use on a per-monitor basis.
+ # If this setting is enabled the Xft.dpi value will be ignored and instead
+ # dunst will attempt to calculate an appropriate dpi value for each monitor
+ # using the resolution and physical size. This might be useful in setups
+ # where there are multiple screens with very different dpi values.
+ per_monitor_dpi = false
+
+# Every section that isn't one of the above is interpreted as a rules to
+# override settings for certain messages.
+#
+# Messages can be matched by
+# appname (discouraged, see desktop_entry)
+# body
+# category
+# desktop_entry
+# icon
+# match_transient
+# msg_urgency
+# stack_tag
+# summary
+#
+# and you can override the
+# background
+# foreground
+# format
+# frame_color
+# fullscreen
+# new_icon
+# set_stack_tag
+# set_transient
+# set_category
+# timeout
+# urgency
+# skip_display
+# history_ignore
+# action_name
+# word_wrap
+# ellipsize
+# alignment
+#
+# Shell-like globbing will get expanded.
+#
+# Instead of the appname filter, it's recommended to use the desktop_entry filter.
+# GLib based applications export their desktop-entry name. In comparison to the appname,
+# the desktop-entry won't get localized.
+#
+# SCRIPTING
+# You can specify a script that gets run when the rule matches by
+# setting the "script" option.
+# The script will be called as follows:
+# script appname summary body icon urgency
+# where urgency can be "LOW", "NORMAL" or "CRITICAL".
+#
+# NOTE: It might be helpful to run dunst -print in a terminal in order
+# to find fitting options for rules.
+
+# Disable the transient hint so that idle_threshold cannot be bypassed from the
+# client
+#[transient_disable]
+# match_transient = yes
+# set_transient = no
+#
+# Make the handling of transient notifications more strict by making them not
+# be placed in history.
+#[transient_history_ignore]
+# match_transient = yes
+# history_ignore = yes
+
+# fullscreen values
+# show: show the notifications, regardless if there is a fullscreen window opened
+# delay: displays the new notification, if there is no fullscreen window active
+# If the notification is already drawn, it won't get undrawn.
+# pushback: same as delay, but when switching into fullscreen, the notification will get
+# withdrawn from screen again and will get delayed like a new notification
+#[fullscreen_delay_everything]
+# fullscreen = delay
+[fullscreen_show_critical]
+ msg_urgency = critical
+ fullscreen = show
+[fullscreen_show_normal]
+ msg_urgency = normal
+ fullscreen = show
+
+
+#[espeak]
+# summary = "*"
+# script = dunst_espeak.sh
+
+#[script-test]
+# summary = "*script*"
+# script = dunst_test.sh
+
+#[ignore]
+# # This notification will not be displayed
+# summary = "foobar"
+# skip_display = true
+
+#[history-ignore]
+# # This notification will not be saved in history
+# summary = "foobar"
+# history_ignore = yes
+
+#[skip-display]
+# # This notification will not be displayed, but will be included in the history
+# summary = "foobar"
+# skip_display = yes
+
+#[signed_on]
+# appname = Pidgin
+# summary = "*signed on*"
+# urgency = low
+#
+#[signed_off]
+# appname = Pidgin
+# summary = *signed off*
+# urgency = low
+#
+#[says]
+# appname = Pidgin
+# summary = *says*
+# urgency = critical
+#
+#[twitter]
+# appname = Pidgin
+# summary = *twitter.com*
+# urgency = normal
+#
+
+[openEwwPopup]
+ script = ~/.config/dunst/scripts/openEwwPopup.sh
+
+[songArtLogger]
+ script = ~/.config/dunst/scripts/songArtLogger.sh
+
+[stack-volumes]
+ appname = "some_volume_notifiers"
+ set_stack_tag = "volume"
+
+[flameshot-urgency]
+ appname = flameshot
+ urgency = low
+
+[whatsapp-alignment]
+ appname = whatsapp-nativefier-d40211
+ alignment = left
+
+[discord-alignment]
+ appname = discord
+ alignment = left
+
+[signal-alignment]
+ appname = Signal
+ alignment = left
+
+[urgency_low]
+ background = "#16161e"
+ foreground = "#c0caf5"
+ frame_color = "#1f2335"
+ timeout = 3
+
+[urgency_normal]
+ background = "#16161e"
+ foreground = "#c0caf5"
+ frame_color = "#3d59a1"
+ timeout = 15
+
+[urgency_critical]
+ background = "#191D24"
+ foreground = "#c0caf5"
+ frame_color = "#db4b4b"
+ timeout = 60
diff --git a/linux/home/.config/dunst/scripts/openEwwPopup.sh b/linux/home/.config/dunst/scripts/openEwwPopup.sh
new file mode 100755
index 0000000..d22e981
--- /dev/null
+++ b/linux/home/.config/dunst/scripts/openEwwPopup.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+DND_LOCK_FILE="$HOME/.cache/dnd-lock.lock"
+EWW_BIN="$HOME/.local/bin/eww"
+
+finish() {
+ ${EWW_BIN} update noti=false; sleep 0.075
+ ${EWW_BIN} close notification-popup
+}
+
+# Run eww daemon if not running
+if [[ ! `pidof eww` ]]; then
+ ${EWW_BIN} daemon
+ sleep 1
+else
+ if [[ ! -f "$DND_LOCK_FILE" ]]; then
+ KILLED=false
+ for pid in $(pidof -x openEwwPopup.sh); do
+ if [ $pid != $$ ]; then
+ kill -9 $pid
+ KILLED=true
+ fi
+ done >/dev/nullx
+
+ if ! $KILLED; then
+ sleep 0.5
+ ${EWW_BIN} update noti=true
+ ${EWW_BIN} open notification-popup
+ canberra-gtk-play -i message
+ fi
+
+ sleep 5
+ finish
+ unset KILLED
+ fi
+fi
diff --git a/linux/home/.config/dunst/scripts/songArtLogger.sh b/linux/home/.config/dunst/scripts/songArtLogger.sh
new file mode 100755
index 0000000..f73beb4
--- /dev/null
+++ b/linux/home/.config/dunst/scripts/songArtLogger.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+TMP_DIR="$HOME/.cache/dunst"
+TMP_COVER_PATH="$TMP_DIR/$DUNST_SUMMARY.png"
+TMP_TEMP_PATH="$TMP_DIR/temp.png"
+
+if [ ! -d "$TMP_DIR" ]; then
+ mkdir -p "$TMP_DIR"
+fi
+
+ART_FROM_SPOTIFY="$(playerctl -p %any,spotify metadata mpris:artUrl | sed -e 's/open.spotify.com/i.scdn.co/g')"
+
+if [[ $(playerctl -p spotify,%any,firefox,chromium,brave,mpd metadata mpris:artUrl) ]]; then
+ curl -s "$ART_FROM_SPOTIFY" --output "$TMP_COVER_PATH"
+fi
+
+cp "$TMP_TEMP_PATH" "$TMP_COVER_PATH" \ No newline at end of file
diff --git a/linux/home/.config/eww/eww.scss b/linux/home/.config/eww/eww.scss
new file mode 100644
index 0000000..9405a6a
--- /dev/null
+++ b/linux/home/.config/eww/eww.scss
@@ -0,0 +1,11 @@
+@import "./src/scss/variables";
+@import "./src/scss/overrides";
+@import "./src/scss/modules";
+
+@import "./src/scss/bar/index.scss";
+@import "./src/scss/control-center/index.scss";
+@import "./src/scss/exitscreen/index.scss";
+@import "./src/scss/info-center/index.scss";
+@import "./src/scss/lockscreen/index.scss";
+@import "./src/scss/notification-center/index.scss";
+@import "./src/scss/notification-popup/index.scss";
diff --git a/linux/home/.config/eww/eww.yuck b/linux/home/.config/eww/eww.yuck
new file mode 100644
index 0000000..191b30c
--- /dev/null
+++ b/linux/home/.config/eww/eww.yuck
@@ -0,0 +1,23 @@
+(include "./src/yuck/_variables.yuck")
+(include "./src/yuck/_modules.yuck")
+
+(include "./src/yuck/bar/_widgets.yuck")
+(include "./src/yuck/bar/_windows.yuck")
+
+(include "./src/yuck/control-center/_widgets.yuck")
+(include "./src/yuck/control-center/_windows.yuck")
+
+(include "./src/yuck/exitscreen/_widgets.yuck")
+(include "./src/yuck/exitscreen/_windows.yuck")
+
+(include "./src/yuck/info-center/_widgets.yuck")
+(include "./src/yuck/info-center/_windows.yuck")
+
+(include "./src/yuck/lockscreen/_widgets.yuck")
+(include "./src/yuck/lockscreen/_windows.yuck")
+
+(include "./src/yuck/notification-center/_widgets.yuck")
+(include "./src/yuck/notification-center/_windows.yuck")
+
+(include "./src/yuck/notification-popup/_widgets.yuck")
+(include "./src/yuck/notification-popup/_windows.yuck")
diff --git a/linux/home/.config/hypr/autostart b/linux/home/.config/hypr/autostart
new file mode 100755
index 0000000..2805e42
--- /dev/null
+++ b/linux/home/.config/hypr/autostart
@@ -0,0 +1,34 @@
+#!/usr/bin/bash
+
+# Policy Authentication Agent
+/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 &
+
+eval "$(/usr/bin/gnome-keyring-daemon --start --components=gpg,pkcs11,secrets,ssh)"
+export "$(gnome-keyring-daemon --start --components=gpg,pkcs11,secrets,ssh)"
+
+# Setup Environment
+systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP &
+dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP --all &
+#dbus-update-activation-environment DISPLAY XAUTHORITY WAYLAND_DISPLAY &
+
+# variables
+scripts=~/.scripts
+
+# gsettings
+#gsettings set org.gnome.desktop.interface gtk-theme 'Tokyonight-Dark-BL-LB'
+#gsettings set org.gnome.desktop.interface font-name 'CaskaydiaCove Nerd Font 9'
+#gsettings set org.gnome.desktop.interface icon-theme 'Tokyonight-Moon'
+#gsettings set org.gnome.desktop.interface cursor-theme 'Sweet-cursors'
+
+# For nemo
+gsettings set org.cinnamon.desktop.default-applications.terminal exec wezterm
+
+# music daemon
+mpd &
+
+# other
+hyprctl setcursor Sweet-cursors 24
+wl-paste --watch cliphist store &
+notify-send -a aurora "hello $(whoami)" &
+sleep 2
+mpd-mpris &
diff --git a/linux/home/.config/hypr/hyprland.conf b/linux/home/.config/hypr/hyprland.conf
new file mode 100644
index 0000000..620dc55
--- /dev/null
+++ b/linux/home/.config/hypr/hyprland.conf
@@ -0,0 +1,175 @@
+# Sourcing external config files
+source=~/.config/hypr/user/monitors.conf
+source=~/.config/hypr/user/exec.conf
+source=~/.config/hypr/user/env.conf
+source=~/.config/hypr/user/binds.conf
+source=~/.config/hypr/user/window_rules.conf
+
+
+# Defaults
+$term = wezterm
+$browser = firefox
+#$gmail = firefox --new-instance -P app "https://mail.google.com/" --class appProfile
+$editor = nvim
+#$explorer = nemo
+#$music = g4music
+$notepad = code --profile notepad --unity-launch ~/Templates
+$launcher = wofi --show drun -n
+$launcher_alt = wofi --show run -n
+#$discord = discord
+#env = GTK_THEME,Breeze-Dark
+
+
+general {
+ gaps_in = 5
+ gaps_out = 5
+ border_size = 2
+ col.active_border = rgba(cba6f7ff) rgba(89b4faff) rgba(94e2d5ff) 10deg
+ col.inactive_border = 0xff313244
+ # whether to apply the sensitivity to raw input (e.g. used by games where you aim using your mouse)
+ apply_sens_to_raw = 0
+ layout = "master";
+ resize_on_border = yes
+ extend_border_grab_area = 20
+}
+
+
+input {
+ kb_layout = custom-us
+ #sensitivity = 0.75 # for mouse cursor
+ sensitivity = 0
+ follow_mouse = 0
+ scroll_method = 2fg
+
+ touchpad {
+ natural_scroll = false
+ disable_while_typing = true
+ tap-to-click = true
+ }
+}
+
+
+decoration {
+ rounding = 10
+ active_opacity = 0.95
+ inactive_opacity = 0.9
+ fullscreen_opacity = 0.95
+
+ dim_inactive = false
+ dim_strength = 0.05
+
+ blur {
+ enabled = true
+ #enabled = false
+ size = 3
+ passes = 1
+
+ vibrancy = 0.1696
+ }
+
+ drop_shadow = true
+ shadow_range = 4
+ shadow_render_power = 3
+ col.shadow = rgba(1a1a1aee)
+ #blur = true
+ #blur_size = 5
+ #blur_passes = 4
+ #blur_new_optimizations = true
+ #blur_xray = true
+ #blur_ignore_opacity = true
+
+ #drop_shadow = true
+ #shadow_ignore_window = true
+ #shadow_range = 20
+ #shadow_render_power = 3
+ #col.shadow = 0x55161925
+ col.shadow_inactive = 0x22161925
+ # Your blur "amount" is blur_size * blur_passes, but high blur_size (over around 5-ish) will produce artifacts.
+ # if you want heavy blur, you need to up the blur_passes.
+ # the more passes, the more you can up the blur_size without noticing artifacts.
+
+ # Blurring layerSurfaces
+ # blurls = gtk-layer-shell
+ # blurls = waybar
+ # blurls = lockscreen
+ blurls = rofi
+ blurls = wofi
+ blurls = firefox
+}
+
+
+animations {
+ enabled = true
+ # bezier = overshot, 0.05, 0.9, 0.1, 1.1
+ bezier = overshot, 0.13, 0.99, 0.29, 1.1
+ animation = windows, 1, 4, overshot, slide
+ animation = border, 1, 10, default
+ animation = fade, 1, 10, default
+ animation = workspaces, 1, 6, overshot, slidevert
+}
+
+
+dwindle {
+ pseudotile = true # enable pseudotiling on dwindle
+ force_split = 0
+ #col.group_border = 0xff89dceb
+ #col.group_border_active = 0xfff9e2af
+ preserve_split = true
+}
+
+
+master {
+ new_on_top = true
+ no_gaps_when_only = false
+}
+
+
+gestures {
+ workspace_swipe = true
+ workspace_swipe_invert = false
+ workspace_swipe_fingers = 3
+}
+
+
+misc {
+ disable_hyprland_logo = true
+
+ focus_on_activate = true
+
+ enable_swallow = true
+ #swallow_regex = ^(scratchpad)$
+}
+
+
+binds {
+ allow_workspace_cycles = true
+}
+
+
+custom {
+
+}
+
+
+layerrule = blur, bar0
+layerrule = noanim, bar0
+layerrule = ignorealpha 0.2, bar0
+layerrule = blur, dock0
+layerrule = noanim, dock0
+layerrule = ignorealpha 0.2, dock0
+layerrule = blur, indicator0
+layerrule = ignorealpha 0.2, indicator0
+layerrule = blur, toolbox0
+layerrule = noanim, toolbox0
+layerrule = ignorealpha 0.2, toolbox0
+layerrule = blur, applauncher
+layerrule = ignorealpha 0.2, applauncher
+layerrule = blur, datemenu
+layerrule = ignorealpha 0.2, datemenu
+layerrule = blur, quicksettings
+layerrule = ignorealpha 0.2, quicksettings
+layerrule = blur, wlroots
+layerrule = ignorealpha 0.2, wlroots
+layerrule = blur, notifications0
+layerrule = ignorealpha 0.2, notifications0
+
diff --git a/linux/home/.config/hypr/scripts/move-scratchpad.sh b/linux/home/.config/hypr/scripts/move-scratchpad.sh
new file mode 100755
index 0000000..a8d0731
--- /dev/null
+++ b/linux/home/.config/hypr/scripts/move-scratchpad.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# Number of pixels to move down
+pixels_to_move=10
+
+# Get the PID of each scratchpad window and move it down
+for pid in "$(hyprctl -j clients | jq -r '.[] | select(.class == "scratchpad") | .pid')"; do
+ hyprctl dispatch movewindowpixel 0 "$pixels_to_move",pid:"$pid"
+done
diff --git a/linux/home/.config/hypr/user/binds.conf b/linux/home/.config/hypr/user/binds.conf
new file mode 100644
index 0000000..a337a0e
--- /dev/null
+++ b/linux/home/.config/hypr/user/binds.conf
@@ -0,0 +1,167 @@
+# Window Manager keybindings
+
+# Scratchpad
+bind = SUPER, semicolon, exec, ~/.scripts/scratchpad && bash ~/.config/hypr/scripts/move-scratchpad.sh
+bind = SUPER, X, exec, ~/.scripts/scratchpad && bash ~/.config/hypr/scripts/move-scratchpad.sh
+#bind = SUPER, semicolon, exec, ~/.scripts/scratchpad
+#bind = SUPER, X, exec, ~/.scripts/scratchpad
+
+# HUD
+bind = SUPER, E, exec, ~/.scripts/heads-up-display
+
+# Package manager Terminal
+bind = SUPER, Q, exec, ~/.scripts/pac
+
+# Mouse binds
+bindm = SUPER, mouse:272, movewindow
+bindm = SUPER, mouse:273, resizewindow
+bind = SUPER, mouse_down, workspace, e-1
+bind = SUPER, mouse_up, workspace, e+1
+
+# Screenshot binds
+bind = , Print, exec,~/.scripts/screenshot_full
+bind = ALT, Print, exec,~/.scripts/screenshot
+
+# Application binds
+$term=wezterm
+bind = SUPER, T, exec, $term -e tmux new-session -A -s term
+bind = SUPER, Enter, exec, $term -e tmux new-session -A -s term
+bind = SUPER, W, exec, $browser
+#bind = SUPER, G, exec, $notepad
+
+# ags
+bind = SUPER_SHIFT, R, exec, ags -q; ags; notify-send "ags reloaded"
+bind=SUPER, Space, exec, ags -t launcher
+bind=,XF86PowerOff, exec, ags -t powermenu
+bind=SUPER, escape, exec, ags -t powermenu
+bind=SUPER, Tab, exec, ags -t overview
+#bind = SUPER, Space, exec, ags -r "toggleLauncher()"
+
+# Clipboard
+bind = SUPER, V, exec, pkill wofi || cliphist list | wofi --dmenu -p clippick -l top_right -x -15 -y 10 -n | cliphist decode | wl-copy
+
+
+# Hyprland keys
+bind=SUPER_SHIFT,Escape,exec,hyprctl reload; notify-send "Config Reloaded"
+bind = SUPER, D, killactive,
+#bind = SUPER_SHIFT, S, movetoworkspace,special
+#bind = SUPER, S, togglespecialworkspace,
+
+
+# Other dispatchers
+bind = ALT, Tab, cyclenext
+bind = SUPER, C, cyclenext
+bind = SUPER, F, fullscreen
+#bind = SUPER, T, exec, hyprctl dispatch centerwindow none
+bind = SUPER, M, fullscreen, 1
+bind = SUPER_SHIFT, M, fakefullscreen
+
+
+bind = SUPER, P, pseudo,
+bind = SUPER, S, togglefloating,
+bind = SUPER_SHIFT, G, togglegroup,
+bind = SUPER, tab, changegroupactive,
+bind = SUPER_SHIFT, I, togglesplit, # dwindle
+
+
+# Move window with SUPER + Shift + arrow keys
+# [↑]
+# [←] [↓] [→]
+bind = SUPER_SHIFT, left, movewindow, l
+bind = SUPER_SHIFT, right, movewindow, r
+bind = SUPER_SHIFT, up, movewindow, u
+bind = SUPER_SHIFT, down, movewindow, d
+bind = SUPER_SHIFT, H, movewindow, l
+bind = SUPER_SHIFT, L, movewindow, r
+bind = SUPER_SHIFT, K, movewindow, u
+bind = SUPER_SHIFT, J, movewindow, d
+
+# Move window focus with SUPER + arrow keys
+# [↑]
+# [←] [↓] [→]
+bind = SUPER, left, movefocus, l
+bind = SUPER, right, movefocus, r
+bind = SUPER, up, movefocus, u
+bind = SUPER, down, movefocus, d
+bind = SUPER, H, movefocus, l
+bind = SUPER, L, movefocus, r
+bind = SUPER, K, movefocus, u
+bind = SUPER, J, movefocus, d
+
+# Move To a workspaces
+bind = SUPER, 1, workspace, 1
+bind = SUPER, 2, workspace, 2
+bind = SUPER, 3, workspace, 3
+bind = SUPER, 4, workspace, 4
+bind = SUPER, 5, workspace, 5
+bind = SUPER, 6, workspace, 6
+bind = SUPER, 7, workspace, 7
+bind = SUPER, 8, workspace, 8
+bind = SUPER, 9, workspace, 9
+
+# Move windows between workspaces
+bind = SUPER_SHIFT, 1, movetoworkspace, 1
+bind = SUPER_SHIFT, 2, movetoworkspace, 2
+bind = SUPER_SHIFT, 3, movetoworkspace, 3
+bind = SUPER_SHIFT, 4, movetoworkspace, 4
+bind = SUPER_SHIFT, 5, movetoworkspace, 5
+bind = SUPER_SHIFT, 6, movetoworkspace, 6
+bind = SUPER_SHIFT, 7, movetoworkspace, 7
+bind = SUPER_SHIFT, 8, movetoworkspace, 8
+bind = SUPER_SHIFT, 9, movetoworkspace, 9
+
+
+# Use this to get thw XF86 bind (FN + Fx combination) for your keyboard
+# xev | grep -A2 --line-buffered '^KeyRelease' | sed -n '/keycode /s/^.*keycode \([0-9]*\).* (.*, \(.*\)).*$/\1 \2/p'
+
+# Volume Control
+#binde = , XF86AudioRaiseVolume, exec, pactl set-sink-volume @DEFAULT_SINK@ +1%
+#binde = , XF86AudioLowerVolume, exec, pactl set-sink-volume @DEFAULT_SINK@ -1%
+bind = , XF86AudioMute, exec, pactl set-sink-mute @DEFAULT_SINK@ toggle
+bind = , XF86AudioMicMute, exec, pactl set-source-mute @DEFAULT_SOURCE@ toggle
+bind = ALT, down, exec, pactl set-sink-volume @DEFAULT_SINK@ -5%
+bind = ALT, up, exec, pactl set-sink-volume @DEFAULT_SINK@ +5%
+
+# Media Control
+bind = , XF86AudioMedia, exec, playerctl play-pause
+bind = , XF86AudioPlay, exec, playerctl play-pause
+bind = , XF86AudioStop, exec, playerctl stop
+bind = , XF86AudioPrev, exec, playerctl previous
+bind = , XF86AudioNext, exec, playerctl next
+
+# Use arrow keys as multimedia keys
+bind = ALT_SHIFT, left, exec, playerctl previous
+bind = ALT_SHIFT, up, exec, playerctl play-pause
+bind = ALT_SHIFT, right, exec, playerctl next
+
+# Brightness Control
+binde = , XF86MonBrightnessUp, exec, light -A 1
+binde = , XF86MonBrightnessDown, exec, light -U 1
+
+bind = ALT, left, exec, brightnessctl set 10%- # Screen brightness down FN+F7
+bind = ALT, right, exec, brightnessctl set 10%+ # Screen brightness up FN+F8
+
+# Screensaver key
+bind = , XF86ScreenSaver, exec,~/.scripts/lock
+
+bind = SUPER_SHIFT, Y, exec, spotify
+bind = SUPER_SHIFT, D, exec, discord
+bind = SUPER , W, exec, firefox
+bind = SUPER_SHIFT, B, exec, rofi-rbw
+
+# Others
+#bind = , XF86Mail, exec,$gmail
+#bind = , XF86HomePage, exec, $explorer ~/
+#bind = , XF86Calculator, exec, qalculate-gtk
+#bind = , XF86Search, exec, wofi
+
+$mainMod = SUPER
+bind = $mainMod, tilde, exec, ~/.scripts/translate.sh
+bind = $mainMod CONTROL, k, exec, ~/.scripts/killmenu
+
+
+
+# trigger when the switch is turning on
+# bindl = , switch:on:Lid Switch, exec, hyprctl keyword monitor ", 1920x1080@60, auto, 1"
+# trigger when the switch is turning off
+bindl = , switch:off:Lid Switch, exec, playerctl --all-players stop; ~/.scripts/lock; systemctl suspend
diff --git a/linux/home/.config/hypr/user/env.conf b/linux/home/.config/hypr/user/env.conf
new file mode 100644
index 0000000..93f7694
--- /dev/null
+++ b/linux/home/.config/hypr/user/env.conf
@@ -0,0 +1,23 @@
+#Environment Variables for the Hyprland session
+
+# XDG
+env = XDG_CURRENT_DESKTOP,Hyprland
+env = XDG_SESSION_TYPE,wayland
+env = XDG_SESSION_DESKTOP,Hyprland
+
+# QT
+env = QT_AUTO_SCREEN_SCALE_FACTOR,1
+env = QT_QPA_PLATFORM=wayland;xcb # Not yet working for onlyoffice-editor
+env = QT_WAYLAND_DISABLE_WINDOWDECORATION,1
+env = QT_QPA_PLATFORMTHEME,qt6ct
+
+# GTK
+#env = GDK_BACKEND=wayland
+
+# Toolkit
+#env = SDL_VIDEODRIVER,wayland
+env = _JAVA_AWT_WM_NONEREPARENTING,1
+env = CLUTTER_BACKEND,wayland
+env = GDK_BACKEND,wayland,x11
+env = MOZ_ENABLE_WAYLAND,1
+
diff --git a/linux/home/.config/hypr/user/exec.conf b/linux/home/.config/hypr/user/exec.conf
new file mode 100644
index 0000000..22846d9
--- /dev/null
+++ b/linux/home/.config/hypr/user/exec.conf
@@ -0,0 +1,4 @@
+exec-once=$HOME/.config/hypr/autostart
+exec-once = swww kill; swww init --format xrgb
+exec = ags
+exec =$HOME/.scripts/move-qemu.sh
diff --git a/linux/home/.config/hypr/user/monitors.conf b/linux/home/.config/hypr/user/monitors.conf
new file mode 100644
index 0000000..7464786
--- /dev/null
+++ b/linux/home/.config/hypr/user/monitors.conf
@@ -0,0 +1,4 @@
+monitor=auto,preferred,auto, 1, bitdepth,10
+#monitor=, 1920x1080@60, auto, 1
+#monitor=, 1366x768@60, auto, 1
+#monitor=LVDS-1, 1366x768@60, auto, 1, bitdepth,10
diff --git a/linux/home/.config/hypr/user/window_rules.conf b/linux/home/.config/hypr/user/window_rules.conf
new file mode 100644
index 0000000..3964de2
--- /dev/null
+++ b/linux/home/.config/hypr/user/window_rules.conf
@@ -0,0 +1,70 @@
+# {{@@ header() @@}}
+# vim:fileencoding=utf-8:ft=conf:foldmethod=marker
+
+# Workspaces
+windowrulev2 = workspace 1 silent, class:firefox
+windowrulev2 = workspace 4 silent, class:discord
+windowrulev2 = workspace 5 silent, class:Spotify
+
+# Scratchpad
+$scratchpad = class:^(scratchpad)$
+windowrulev2 = opacity 1 0.9,class:^(scratchpad)$
+windowrulev2 = float,$scratchpad
+#windowrule = float,^(scratchpad)$
+#windowrule = move 15 40, ^(scratchpad)$
+#windowrule = move center,^(scratchpad)$
+#$scratchpadsize = size 98% 93%
+#windowrulev2 = tile,$scratchpad
+#windowrulev2 = pin,$scratchpad
+#windowrulev2=windowdance,$scratchpad
+#windowrulev2 = move 100%-20,$scratchpad
+#windowrulev2 = $scratchpadsize,$scratchpad
+
+# HUD
+windowrule = float, ^(heads-up-display)$
+windowrule = pin, ^(heads-up-display)$
+windowrule = size 325 160, ^(heads-up-display)$
+windowrule = move 1020 50, ^(heads-up-display)$
+
+# Package manager Terminal
+windowrule = float, ^(pac)$
+windowrule = pin, ^(pac)$
+windowrule = size 325 160, ^(pac)$
+windowrule = move 50 50, ^(pac)$
+
+# Picture-in-Picture
+windowrulev2 = idleinhibit fullscreen, title:^Picture-in-Picture$
+windowrulev2 = float, title:^Picture-in-Picture$
+windowrulev2 = pin, title:^Picture-in-Picture$
+windowrulev2 = move 920 480, title:^Picture-in-Picture$
+windowrulev2 = size 425 260, title:^Picture-in-Picture$
+
+# Firefox
+windowrulev2 = float, class:^(firefox)$, title:^(Firefox — Sharing Indicator)$
+windowrulev2 = opacity 1 1,class:^(firefox)$
+
+# Applications
+windowrule = float,^(rlr)$
+windowrule = float,^(pavucontrol)$
+windowrule = float,^(blueman-manager)$
+windowrule = float,^(nm-connection-editor)$
+windowrule = float,^(mediainfo-gui)$
+windowrulev2 = float, class:^(nemo)$, title:^(.*Properties)$
+windowrulev2 = float, class:^(Nemo-preview-start)$
+windowrulev2 = move 100%-433 53, class:^(wofi)$, title:^(clippick)$
+windowrulev2 = animation popin, class:^(wlogout)$, title:^(wlogout)$
+windowrulev2 = float, class:^(wlogout)$, title:^(wlogout)$
+windowrulev2 = animation slide, class:^(wofi)$
+#windowrulev2 = float, class:^(steam)$
+windowrule = float,^(com.github.neithern.g4music)$
+windowrule = size 670 635,^(com.github.neithern.g4music)$
+windowrule = move center,^(com.github.neithern.g4music)$
+windowrulev2 = opacity 0.0 override,class:^(xwaylandvideobridge)$
+windowrulev2 = noanim,class:^(xwaylandvideobridge)$
+windowrulev2 = noinitialfocus,class:^(xwaylandvideobridge)$
+windowrulev2 = maxsize 1 1,class:^(xwaylandvideobridge)$
+windowrulev2 = noblur,class:^(xwaylandvideobridge)$
+
+# Blur
+windowrule = noblur,^(firefox)$ # disables blur for firefox
+windowrule = noblur,^(scratchpad)$ # disables blur for firefox
diff --git a/linux/home/.config/inputrc b/linux/home/.config/inputrc
new file mode 100644
index 0000000..adfeec4
--- /dev/null
+++ b/linux/home/.config/inputrc
@@ -0,0 +1,86 @@
+$include /etc/inputrc
+
+"\f": clear-screen
+
+set bell-style none
+set meta-flag on
+set input-meta on
+set convert-meta off
+set output-meta on
+set show-all-if-ambiguous on # set show-all-if-unmodified on
+
+# Color files by types
+# Note that this may cause completion text blink in some terminals (e.g. xterm).
+set colored-stats On
+# Append char to indicate type
+set visible-stats On
+# Mark symlinked directories
+set mark-symlinked-directories On
+# Color the common prefix
+set colored-completion-prefix On
+# Color the common prefix in menu-complete
+set menu-complete-display-prefix On
+
+# set editing-mode vi
+set show-mode-in-prompt on
+set keyseq-timeout 0 # Reduce the delay between pressing escape and the cursor change
+set vi-cmd-mode-string "\1\e[2 q\2"
+set vi-ins-mode-string "\1\e[6 q\2"
+
+$if mode=vi
+ set keymap vi-command
+ # these are for vi-command mode
+ "\e[A": history-search-backward
+ "\e[B": history-search-forward
+ j: history-search-forward
+ k: history-search-backward
+ set keymap vi-insert
+ # these are for vi-insert mode
+ "\e[A": history-search-backward
+ "\e[B": history-search-forward
+ "jk" # escape
+$endif
+
+$if mode=emacs
+
+ "\C-P": history-search-backward
+ "\C-N": history-search-forward
+
+ # for linux console and RH/Debian xterm
+ "\e[1~": beginning-of-line
+ "\e[4~": end-of-line
+ "\e[5~": beginning-of-history
+ "\e[6~": end-of-history
+ "\e[7~": beginning-of-line
+ "\e[3~": delete-char
+ "\e[2~": quoted-insert
+ "\e[5C": forward-word
+ "\e[5D": backward-word
+ "\e\e[C": forward-word
+ "\e\e[D": backward-word
+ "\e[1;5C": forward-word
+ "\e[1;5D": backward-word
+
+ # for rxvt
+ "\e[8~": end-of-line
+
+ # for non RH/Debian xterm, can't hurt for RH/DEbian xterm
+ "\eOH": beginning-of-line
+ "\eOF": end-of-line
+
+ # for freebsd console
+ "\e[H": beginning-of-line
+ "\e[F": end-of-line
+
+$endif
+
+#set editing-mode emacs
+set editing-mode vi
+
+# # switch between vi or emacs
+# set keymap emacs
+# "\e[": vi-editing-mode
+# set keymap vi-insert
+# "\e[": emacs-editing-mode
+# set keymap vi-command
+# "\e[": emacs-editing-mode
diff --git a/linux/home/.config/jgmenu/jgmenurc b/linux/home/.config/jgmenu/jgmenurc
new file mode 100644
index 0000000..d35eee6
--- /dev/null
+++ b/linux/home/.config/jgmenu/jgmenurc
@@ -0,0 +1,32 @@
+stay_alive = 1
+tint2_look = 0
+position_mode = pointer
+terminal_exec = wezterm
+terminal_args = -x
+sub_spacing = 5
+menu_width = 200
+menu_padding_top = 5
+menu_padding_right = 2
+menu_padding_bottom = 5
+menu_padding_left = 2
+menu_radius = 7
+menu_border = 0
+menu_halign = left
+sub_hover_action = 1
+item_margin_y = 5
+item_height = 30
+item_padding_x = 8
+item_radius = 6
+item_border = 0
+sep_height = 5
+font = Roboto Medium 15px
+icon_theme = Papirus
+icon_size = 24
+color_menu_bg = #171B20 80
+color_menu_border = #c0caf5
+color_norm_bg = #171B20 0
+color_norm_fg = #b6beca 100
+color_sel_bg = #74bee9 50
+color_sel_fg = #b6beca 100
+color_sep_fg = b6beca 80
+csv_cmd = cat ~/.config/jgmenu/menu.csv
diff --git a/linux/home/.config/jgmenu/menu.csv b/linux/home/.config/jgmenu/menu.csv
new file mode 100644
index 0000000..dbece5a
--- /dev/null
+++ b/linux/home/.config/jgmenu/menu.csv
@@ -0,0 +1,32 @@
+Terminal,scratchpad,utilities-terminal
+Web-browser,firefox,web-browser
+Neovim,kitty -e nvim,nvim
+Ranger,wezterm -e ranger,stock_folder
+^sep()
+
+Extras,^checkout(extras),add
+^sep()
+
+Critical,^checkout(critical),gtk-dialog-warning
+^sep()
+
+Lock,betterlockscreen --lock,system-lock-screen
+Logout,pkill -KILL -u "$USER" &,system-log-out
+
+Exit,^checkout(exit),exit
+
+^tag(exit)
+Suspend,systemctl suspend,system-suspend
+Reboot,reboot,system-reboot
+Poweroff,poweroff,system-shutdown
+
+^tag(extras)
+Change wallpaper,nitrogen,nitrogen
+Randomize Wallpaper,random-wall
+Control-center,~/.config/eww/scripts/openControlCenter.sh
+Reload eww,pkill -f eww && eww daemon
+
+^tag(critical)
+Restart BSPWM,control_box -bspres
+Restart SXHKD,control_box -kbres,input-keyboard
+Restart Pipewire,control_box -soundres,audio-speakers
diff --git a/linux/home/.config/kitty/kitty.conf b/linux/home/.config/kitty/kitty.conf
new file mode 100644
index 0000000..cbf9f70
--- /dev/null
+++ b/linux/home/.config/kitty/kitty.conf
@@ -0,0 +1,827 @@
+
+# vim:fileencoding=utf-8:ft=conf:foldmethod=marker
+
+#: Fonts {{{
+
+#: kitty has very powerful font management. You can configure
+#: individual font faces and even specify special fonts for particular
+#: characters.
+
+font_family JetBrains Mono Medium
+bold_font auto
+italic_font auto
+bold_italic_font auto
+
+#: You can specify different fonts for the bold/italic/bold-italic
+#: variants. By default they are derived automatically, by the OSes
+#: font system. Setting them manually is useful for font families that
+#: have many weight variants like Book, Medium, Thick, etc. For
+#: example:
+
+#: font_family Operator Mono Book
+#: bold_font Operator Mono Medium
+#: italic_font Operator Mono Book Italic
+#: bold_italic_font Operator Mono Medium Italic
+
+font_size 9.0
+
+#: Font size (in pts)
+
+# adjust_line_height 0
+# adjust_column_width 0
+
+#: Change the size of each character cell kitty renders. You can use
+#: either numbers, which are interpreted as pixels or percentages
+#: (number followed by %), which are interpreted as percentages of the
+#: unmodified values. You can use negative pixels or percentages less
+#: than 100% to reduce sizes (but this might cause rendering
+#: artifacts).
+
+# symbol_map U+E0A0-U+E0A2,U+E0B0-U+E0B3 PowerlineSymbols
+
+#: Map the specified unicode codepoints to a particular font. Useful
+#: if you need special rendering for some symbols, such as for
+#: Powerline. Avoids the need for patched fonts. Each unicode code
+#: point is specified in the form U+<code point in hexadecimal>. You
+#: can specify multiple code points, separated by commas and ranges
+#: separated by hyphens. symbol_map itself can be specified multiple
+#: times. Syntax is::
+
+#: symbol_map codepoints Font Family Name
+
+# box_drawing_scale 0.001, 1, 1.5, 2
+
+#: Change the sizes of the lines used for the box drawing unicode
+#: characters These values are in pts. They will be scaled by the
+#: monitor DPI to arrive at a pixel value. There must be four values
+#: corresponding to thin, normal, thick, and very thick lines.
+
+#: }}}
+
+#: Cursor customization {{{
+
+# cursor magenta
+cursor white
+
+#: Default cursor color
+
+# cursor_shape block
+
+#: The cursor shape can be one of (block, beam, underline)
+
+cursor_blink_interval 0.5
+cursor_stop_blinking_after 15.0
+
+#: The interval (in seconds) at which to blink the cursor. Set to zero
+#: to disable blinking. Note that numbers smaller than repaint_delay
+#: will be limited to repaint_delay. Stop blinking cursor after the
+#: specified number of seconds of keyboard inactivity. Set to zero to
+#: never stop blinking.
+
+#: }}}
+
+#: Scrollback {{{
+
+scrollback_lines 20000
+
+#: Number of lines of history to keep in memory for scrolling back.
+#: Memory is allocated on demand.
+
+# scrollback_pager less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER
+
+#: Program with which to view scrollback in a new window. The
+#: scrollback buffer is passed as STDIN to this program. If you change
+#: it, make sure the program you use can handle ANSI escape sequences
+#: for colors and text formatting. INPUT_LINE_NUMBER in the command
+#: line above will be replaced by an integer representing which line
+#: should be at the top of the screen.
+
+wheel_scroll_multiplier 5
+
+#: Modify the amount scrolled by the mouse wheel or touchpad. Use
+#: negative numbers to change scroll direction.
+
+#: }}}
+
+#: Mouse {{{
+
+url_color #0087BD
+url_style curly
+
+#: The color and style for highlighting URLs on mouse-over. url_style
+#: can be one of: none, single, double, curly
+
+open_url_modifiers kitty_mod
+
+#: The modifier keys to press when clicking with the mouse on URLs to
+#: open the URL
+
+open_url_with default
+
+#: The program with which to open URLs that are clicked on. The
+#: special value default means to use the operating system's default
+#: URL handler.
+
+copy_on_select yes
+
+#: Copy to clipboard on select. With this enabled, simply selecting
+#: text with the mouse will cause the text to be copied to clipboard.
+#: Useful on platforms such as macOS/Wayland that do not have the
+#: concept of primary selectons. Note that this is a security risk,
+#: as all programs, including websites open in your browser can read
+#: the contents of the clipboard.
+
+# rectangle_select_modifiers ctrl+alt
+
+#: The modifiers to use rectangular selection (i.e. to select text in
+#: a rectangular block with the mouse)
+
+# select_by_word_characters :@-./_~?&=%+#
+
+#: Characters considered part of a word when double clicking. In
+#: addition to these characters any character that is marked as an
+#: alpha-numeric character in the unicode database will be matched.
+
+click_interval 0.5
+
+#: The interval between successive clicks to detect double/triple
+#: clicks (in seconds)
+
+# mouse_hide_wait 3.0
+
+#: Hide mouse cursor after the specified number of seconds of the
+#: mouse not being used. Set to zero to disable mouse cursor hiding.
+
+# focus_follows_mouse no
+
+#: Set the active window to the window under the mouse when moving the
+#: mouse around
+
+#: }}}
+
+#: Performance tuning {{{
+
+repaint_delay 10
+
+#: Delay (in milliseconds) between screen updates. Decreasing it,
+#: increases frames-per-second (FPS) at the cost of more CPU usage.
+#: The default value yields ~100 FPS which is more than sufficient for
+#: most uses. Note that to actually achieve 100 FPS you have to either
+#: set sync_to_monitor to no or use a monitor with a high refresh
+#: rate.
+
+# input_delay 3
+
+#: Delay (in milliseconds) before input from the program running in
+#: the terminal is processed. Note that decreasing it will increase
+#: responsiveness, but also increase CPU usage and might cause flicker
+#: in full screen programs that redraw the entire screen on each loop,
+#: because kitty is so fast that partial screen updates will be drawn.
+
+# sync_to_monitor yes
+
+#: Sync screen updates to the refresh rate of the monitor. This
+#: prevents tearing (https://en.wikipedia.org/wiki/Screen_tearing)
+#: when scrolling. However, it limits the rendering speed to the
+#: refresh rate of your monitor. With a very high speed mouse/high
+#: keyboard repeat rate, you may notice some slight input latency. If
+#: so, set this to no.
+
+#: }}}
+
+#: Terminal bell {{{
+
+enable_audio_bell no
+
+#: Enable/disable the audio bell. Useful in environments that require
+#: silence.
+
+# visual_bell_duration 0.5
+
+#: Visual bell duration. Flash the screen when a bell occurs for the
+#: specified number of seconds. Set to zero to disable.
+
+window_alert_on_bell yes
+
+#: Request window attention on bell. Makes the dock icon bounce on
+#: macOS or the taskbar flash on linux.
+
+bell_on_tab yes
+
+#: Show a bell symbol on the tab if a bell occurs in one of the
+#: windows in the tab and the window is not the currently focused
+#: window
+
+#: }}}
+
+#: Window layout {{{
+
+# remember_window_size yes
+# initial_window_width 640
+# initial_window_height 400
+
+#: If enabled, the window size will be remembered so that new
+#: instances of kitty will have the same size as the previous
+#: instance. If disabled, the window will initially have size
+#: configured by initial_window_width/height, in pixels. You can use a
+#: suffix of "c" on the width/height values to have them interpreted
+#: as number of cells instead of pixels.
+
+enabled_layouts *
+
+#: The enabled window layouts. A comma separated list of layout names.
+#: The special value * means all layouts. The first listed layout will
+#: be used as the startup layout. For a list of available layouts, see
+#: the layouts.
+
+# window_resize_step_cells 2
+# window_resize_step_lines 2
+
+#: The step size (in units of cell width/cell height) to use when
+#: resizing windows. The cells value is used for horizontal resizing
+#: and the lines value for vertical resizing.
+
+window_border_width 1
+
+#: The width (in pts) of window borders. Will be rounded to the
+#: nearest number of pixels based on screen resolution. Note that
+#: borders are displayed only when more than one window is visible.
+#: They are meant to separate multiple windows.
+
+window_margin_width 0
+
+#: The window margin (in pts) (blank area outside the border)
+
+# single_window_margin_width -1000.0
+
+#: The window margin (in pts) to use when only a single window is
+#: visible. Negative values will cause the value of
+#: window_margin_width to be used instead.
+
+window_padding_width 2
+
+#: The window padding (in pts) (blank area between the text and the
+#: window border)
+
+active_border_color #282c34
+
+#: The color for the border of the active window
+
+inactive_border_color #22262d
+
+#: The color for the border of inactive windows
+
+# bell_border_color #ff5a00
+
+#: The color for the border of inactive windows in which a bell has
+#: occurred
+
+inactive_text_alpha .6
+
+#: Fade the text in inactive windows by the specified amount (a number
+#: between zero and one, with zero being fully faded).
+
+#: }}}
+
+#: Tab bar {{{
+
+# tab_bar_edge bottom
+
+#: Which edge to show the tab bar on, top or bottom
+
+tab_bar_margin_width 4
+
+#: The margin to the left and right of the tab bar (in pts)
+
+tab_bar_style fade
+
+#: The tab bar style, can be one of: fade or separator. In the fade
+#: style, each tab's edges fade into the background color, in the
+#: separator style, tabs are separated by a configurable separator.
+
+# tab_fade 0.25 0.5 0.75 1
+tab_fade 1 1 1
+
+#: Control how each tab fades into the background when using fade for
+#: the tab_bar_style. Each number is an alpha (between zero and one)
+#: that controls how much the corresponding cell fades into the
+#: background, with zero being no fade and one being full fade. You
+#: can change the number of cells used by adding/removing entries to
+#: this list.
+
+# tab_separator " "
+
+#: The separator between tabs in the tab bar when using separator as
+#: the tab_bar_style.
+
+active_tab_foreground #282c34
+active_tab_background #abb2bf
+active_tab_font_style bold
+inactive_tab_foreground #5c6370
+inactive_tab_background #22262d
+inactive_tab_font_style normal
+
+#: Tab bar colors and styles
+
+#: }}}
+
+#: Color scheme {{{
+
+foreground #d8dee9
+#background #1d1f21
+background #000000
+
+#: The foreground and background colors
+
+background_opacity 0.7
+dynamic_background_opacity yes
+# Increase background opacity ctrl+shift+a>m
+
+# Decrease background opacity ctrl+shift+a>l
+
+# Full background opacity ctrl+shift+a>1
+
+# Reset background opacity ctrl+shift+a>d
+
+#: The opacity of the background. A number between 0 and 1, where 1 is
+#: opaque and 0 is fully transparent. This will only work if
+#: supported by the OS (for instance, when using a compositor under
+#: X11). Note that it only sets the default background color's
+#: opacity. This is so that things like the status bar in vim,
+#: powerline prompts, etc. still look good. But it means that if you
+#: use a color theme with a background color in your editor, it will
+#: not be rendered as transparent. Instead you should change the
+#: default background color in your kitty config and not use a
+#: background color in the editor color scheme. Or use the escape
+#: codes to set the terminals default colors in a shell script to
+#: launch your editor. Be aware that using a value less than 1.0 is a
+#: (possibly significant) performance hit. If you want to dynamically
+#: change transparency of windows set dynamic_background_opacity to
+#: yes (this is off by default as it has a performance cost)
+
+dim_opacity 1.0
+
+#: How much to dim text that has the DIM/FAINT attribute set. One
+#: means no dimming and zero means fully dimmed (i.e. invisible).
+
+selection_foreground #000000
+selection_background #FFFACD
+
+#: The foreground and background for text selected with the mouse
+
+
+#: The 16 terminal colors. There are 8 basic colors, each color has a
+#: dull and bright version. You can also set the remaining colors from
+#: the 256 color table as color16 to color255.
+
+#: black
+color0 #313539
+color8 #676f78
+
+#: red
+color1 #b02626
+color9 #b55454
+
+#: green
+color2 #40a62f
+color10 #78a670
+
+#: yellow
+color3 #f2e635
+color11 #faf380
+
+#: blue
+color4 #314ad0
+color12 #707fd0
+
+#: magenta
+color5 #b30ad0
+color13 #c583d0
+
+#: cyan
+color6 #32d0fc
+color14 #8adaf1
+
+#: white
+color7 #acadb1
+color15 #e0e3e7
+
+
+#: }}}
+
+#: Advanced {{{
+
+# shell zsh
+
+#: The shell program to execute. The default value of . means to use
+#: whatever shell is set as the default shell for the current user.
+#: Note that on macOS if you change this, you might need to add
+#: --login to ensure that the shell starts in interactive mode and
+#: reads its startup rc files.
+
+editor .
+
+#: The console editor to use when editing the kitty config file or
+#: similar tasks. A value of . means to use the environment variable
+#: EDITOR. Note that this environment variable has to be set not just
+#: in your shell startup scripts but system-wide, otherwise kitty will
+#: not see it.
+
+# Confirm when closing where 0 disables it; -1 enables it
+confirm_os_window_close 0
+
+# close_on_child_death nvim
+
+#: Close the window when the child process (shell) exits. If no (the
+#: default), the terminal will remain open when the child exits as
+#: long as there are still processes outputting to the terminal (for
+#: example disowned or backgrounded processes). If yes, the window
+#: will close as soon as the child process exits. Note that setting it
+#: to yes means that any background processes still using the terminal
+#: can fail silently because their stdout/stderr/stdin no longer work.
+
+# allow_remote_control no
+
+#: Allow other programs to control kitty. If you turn this on other
+#: programs can control all aspects of kitty, including sending text
+#: to kitty windows, opening new windows, closing windows, reading the
+#: content of windows, etc. Note that this even works over ssh
+#: connections.
+
+# startup_session none
+
+#: Path to a session file to use for all kitty instances. Can be
+#: overridden by using the kitty --session command line option for
+#: individual instances. See sessions in the kitty documentation for
+#: details. Note that relative paths are interpreted with respect to
+#: the kitty config directory. Environment variables in the path are
+#: expanded.
+
+# clipboard_control write-clipboard write-primary
+clipboard_control write-primary write-clipboard no-append
+
+#: Allow programs running in kitty to read and write from the
+#: clipboard. You can control exactly which actions are allowed. The
+#: set of possible actions is: write-clipboard read-clipboard write-
+#: primary read-primary The default is to allow writing to the
+#: clipboard and primary selection. Note that enabling the read
+#: functionality is a security risk as it means that any program, even
+#: one running on a remote server via SSH can read your clipboard.
+
+#term xterm-kitty
+
+#: The value of the TERM environment variable to set. Changing this
+#: can break many terminal programs, only change it if you know what
+#: you are doing, not because you read some advice on Stack Overflow
+#: to change it.
+
+#: }}}
+
+#: OS specific tweaks {{{
+
+# macos_titlebar_color #22262d
+
+#: Change the color of the kitty window's titlebar on macOS. A value
+#: of system means to use the default system color, a value of
+#: background means to use the background color of the currently
+#: active window and finally you can use an arbitrary color, such as
+#: #12af59 or red. WARNING: This option works by using a hack, as
+#: there is no proper Cocoa API for it. It sets the background color
+#: of the entire window and makes the titlebar transparent. As such it
+#: is incompatible with background_opacity. If you want to use both,
+#: you are probably better off just hiding the titlebar with
+#: macos_hide_titlebar.
+
+# macos_hide_titlebar no
+
+#: Hide the kitty window's title bar on macOS.
+
+hide_window_decorations yes
+
+#: Hide the window decorations (title bar and window borders) on X11
+#: and Wayland. Whether this works and exactly what effect it has
+#: depends on the window manager, as it is the job of the window
+#: manager/compositor to draw window decorations.
+
+# macos_option_as_alt yes
+
+#: Use the option key as an alt key. With this set to no, kitty will
+#: use the macOS native Option+Key = unicode character behavior. This
+#: will break any Alt+key keyboard shortcuts in your terminal
+#: programs, but you can use the macOS unicode input technique.
+
+# macos_hide_from_tasks no
+
+#: Hide the kitty window from running tasks (Option+Tab) on macOS.
+
+# macos_quit_when_last_window_closed no
+
+#: Have kitty quit when all the top-level windows are closed. By
+#: default, kitty will stay running, even with no open windows, as is
+#: the expected behavior on macOS.
+
+linux_display_server x11
+
+#: Choose between Wayland and X11 backends. By default, an appropriate
+#: backend based on the system state is chosen automatically. Set it
+#: to x11 or wayland to force the choice.
+
+#: }}}
+
+#: Keyboard shortcuts {{{
+
+#: For a list of key names, see: GLFW keys
+#: <http://www.glfw.org/docs/latest/group__keys.html>. The name to use
+#: is the part after the GLFW_KEY_ prefix. For a list of modifier
+#: names, see: GLFW mods
+#: <http://www.glfw.org/docs/latest/group__mods.html>
+
+#: On Linux you can also use XKB key names to bind keys that are not
+#: supported by GLFW. See XKB keys
+#: <https://github.com/xkbcommon/libxkbcommon/blob/master/xkbcommon/xkbcommon-
+#: keysyms.h> for a list of key names. The name to use is the part
+#: after the XKB_KEY_ prefix. Note that you should only use an XKB key
+#: name for keys that are not present in the list of GLFW keys.
+
+#: You can use the special action no_op to unmap a keyboard shortcut
+#: that is assigned in the default configuration.
+
+#: You can combine multiple actions to be triggered by a single
+#: shortcut, using the syntax below::
+
+#: map key combine <separator> action1 <separator> action2 <separator> action3 ...
+
+#: For example::
+
+#: map kitty_mod+e combine : new_window : next_layout
+
+#: this will create a new window and switch to the next available
+#: layout
+
+#: You can use multi-key shortcuts using the syntax shown below::
+
+#: map key1>key2>key3 action
+
+#: For example::
+
+#: map ctrl+f>2 set_font_size 20
+
+# kitty_mod ctrl+shift
+
+#: The value of kitty_mod is used as the modifier for all default
+#: shortcuts, you can change it in your kitty.conf to change the
+#: modifiers for all the default shortcuts.
+
+# clear_all_shortcuts no
+
+#: You can have kitty remove all shortcut definition seen up to this
+#: point. Useful, for instance, to remove the default shortcuts.
+
+#: Clipboard {{{
+
+# map cmd+c copy_to_clipboard
+#map kitty_mod+c copy_to_clipboard
+# map cmd+v paste_from_clipboard
+#map kitty_mod+v paste_from_clipboard
+#map kitty_mod+s paste_from_selection
+map shift+insert paste_from_selection
+# map kitty_mod+o pass_selection_to_program
+map ctrl+c copy_to_clipboard
+map ctrl+v paste_from_clipboard
+
+#: You can also pass the contents of the current selection to any
+#: program using pass_selection_to_program. By default, the system's
+#: open program is used, but you can specify your own, for example::
+
+#: map kitty_mod+o pass_selection_to_program firefox
+
+#: You can pass the current selection to a terminal program running in
+#: a new kitty window, by using the @selection placeholder::
+
+#: map kitty_mod+y new_window less @selection
+
+#: }}}
+
+#: Scrolling {{{
+
+#map kitty_mod+up scroll_line_up
+#map ctrl+k scroll_line_up
+#map kitty_mod+k scroll_line_up
+#map kitty_mod+down scroll_line_down
+#map ctrl+j scroll_line_down
+#map kitty_mod+j scroll_line_down
+map kitty_mod+page_up scroll_page_up
+map kitty_mod+page_down scroll_page_down
+map kitty_mod+b scroll_page_up
+map kitty_mod+f scroll_page_down
+# map kitty_mod+home scroll_home
+# map kitty_mod+end scroll_end
+# map kitty_mod+h show_scrollback
+
+#: You can send the contents of the current screen + history buffer as
+#: stdin to an arbitrary program using the placeholders @text (which
+#: is the plain text) and @ansi (which includes text styling escape
+#: codes). For only the current screen, use @screen or @ansi_screen.
+#: For example, the following command opens the scrollback buffer in
+#: less in a new window::
+
+#: map kitty_mod+y new_window @ansi less +G -R
+
+#: }}}
+
+#: Window management {{{
+
+# map kitty_mod+enter new_window
+map kitty_mod+enter no_op
+map kitty_mod+enter new_window_with_cwd
+
+#: You can open a new window running an arbitrary program, for
+#: example::
+
+#: map kitty_mod+y new_window mutt
+
+#: You can open a new window with the current working directory set to
+#: the working directory of the current window using::
+
+#: map ctrl+alt+enter new_window_with_cwd
+
+# map cmd+n new_os_window
+# map kitty_mod+n new_os_window
+# map kitty_mod+w close_window
+# map kitty_mod+] next_window
+# map kitty_mod+[ previous_window
+map kitty_mod+j previous_window
+map kitty_mod+k next_window
+map kitty_mod+up move_window_forward
+map kitty_mod+down move_window_backward
+# map kitty_mod+f move_window_forward
+# map kitty_mod+b move_window_backward
+# map kitty_mod+` move_window_to_top
+# map kitty_mod+r start_resizing_window
+# map kitty_mod+1 first_window
+# map kitty_mod+2 second_window
+# map kitty_mod+3 third_window
+# map kitty_mod+4 fourth_window
+# map kitty_mod+5 fifth_window
+# map kitty_mod+6 sixth_window
+# map kitty_mod+7 seventh_window
+# map kitty_mod+8 eighth_window
+# map kitty_mod+9 ninth_window
+# map kitty_mod+0 tenth_window
+#: }}}
+
+#: Tab management {{{
+
+# map kitty_mod+right next_tab
+# map kitty_mod+left previous_tab
+map kitty_mod+] no_op
+map kitty_mod+] next_tab
+map kitty_mod+[ no_op
+map kitty_mod+[ previous_tab
+# map kitty_mod+t new_tab
+# map kitty_mod+q close_tab
+# map kitty_mod+. move_tab_forward
+# map kitty_mod+, move_tab_backward
+map kitty_mod+right no_op
+map kitty_mod+right move_tab_forward
+map kitty_mod+left no_op
+map kitty_mod+left move_tab_backward
+# map kitty_mod+alt+t set_tab_title
+map kitty_mod+t no_op
+map kitty_mod+t new_tab_with_cwd
+
+#: You can also create shortcuts to go to specific tabs, with 1 being
+#: the first tab::
+
+#: map ctrl+alt+1 goto_tab 1
+#: map ctrl+alt+2 goto_tab 2
+
+#: Just as with new_window above, you can also pass the name of
+#: arbitrary commands to run when using new_tab and use
+#: new_tab_with_cwd.
+#: }}}
+
+#: Layout management {{{
+
+# map kitty_mod+l next_layout
+
+#: You can also create shortcuts to switch to specific layouts::
+
+map kitty_mod+0 no_op
+map kitty_mod+0 goto_layout stack
+map kitty_mod+9 no_op
+map kitty_mod+9 goto_layout tall
+map kitty_mod+8 no_op
+map kitty_mod+8 goto_layout fat
+#: map ctrl+alt+t goto_layout tall
+#: map ctrl+alt+s goto_layout stack
+#: }}}
+
+#: Font sizes {{{
+
+#: You can change the font size for all top-level kitty windows at a
+#: time or only the current one.
+
+map kitty_mod+equal change_font_size all +2.0
+map kitty_mod+minus change_font_size all -2.0
+map kitty_mod+backspace change_font_size all 0
+
+#: To setup shortcuts for specific font sizes::
+
+#: map kitty_mod+f6 change_font_size all 10.0
+
+#: To setup shortcuts to change only the current window's font size::
+
+#: map kitty_mod+f6 change_font_size current 10.0
+#: }}}
+
+#: Select and act on visible text {{{
+
+#: Use the hints kitten to select text and either pass it to an
+#: external program or insert it into the terminal or copy it to the
+#: clipboard.
+
+# map kitty_mod+e kitten hints
+
+#: Open a currently visible URL using the keyboard. The program used
+#: to open the URL is specified in open_url_with.
+
+# map kitty_mod+p>f kitten hints --type path --program -
+
+#: Select a path/filename and insert it into the terminal. Useful, for
+#: instance to run git commands on a filename output from a previous
+#: git command.
+
+# map kitty_mod+p>shift+f kitten hints --type path
+
+#: Select a path/filename and open it with the default open program.
+
+# map kitty_mod+p>l kitten hints --type line --program -
+
+#: Select a line of text and insert it into the terminal. Use for the
+#: output of things like: ls -1
+
+# map kitty_mod+p>w kitten hints --type word --program -
+
+#: Select words and insert into terminal.
+
+# map kitty_mod+p>h kitten hints --type hash --program -
+
+#: Select something that looks like a hash and insert it into the
+#: terminal. Useful with git, which uses sha1 hashes to identify
+#: commits
+
+
+#: The hints kitten has many more modes of operation that you can map
+#: to different shortcuts. For a full description see kittens/hints.
+#: }}}
+
+#: Miscellaneous {{{
+
+
+# map kitty_mod+f11 toggle_fullscreen
+# map kitty_mod+u input_unicode_character
+# map kitty_mod+f2 edit_config_file
+# map kitty_mod+escape kitty_shell window
+
+#: Open the kitty shell in a new window/tab/overlay/os_window to
+#: control kitty using commands.
+
+# map kitty_mod+a>m set_background_opacity +0.1
+# map kitty_mod+a>l set_background_opacity -0.1
+# map kitty_mod+a>1 set_background_opacity 1
+# map kitty_mod+a>d set_background_opacity default
+#
+# map kitty_mod+a>m set_background_opacity +0.1
+# map kitty_mod+a>l set_background_opacity -0.1
+map kitty_mod+, set_background_opacity 1
+map kitty_mod+. set_background_opacity default
+
+#: You can tell kitty to send arbitrary (UTF-8) encoded text to the
+#: client program when pressing specified shortcut keys. For example::
+
+#: map ctrl+alt+a send_text all Special text
+
+#: This will send "Special text" when you press the ctrl+alt+a key
+#: combination. The text to be sent is a python string literal so you
+#: can use escapes like \x1b to send control codes or \u21fb to send
+#: unicode characters (or you can just input the unicode characters
+#: directly as UTF-8 text). The first argument to send_text is the
+#: keyboard modes in which to activate the shortcut. The possible
+#: values are normal or application or kitty or a comma separated
+#: combination of them. The special keyword all means all modes. The
+#: modes normal and application refer to the DECCKM cursor key mode
+#: for terminals, and kitty refers to the special kitty extended
+#: keyboard protocol.
+
+#: Another example, that outputs a word and then moves the cursor to
+#: the start of the line (same as pressing the Home key)::
+
+#: map ctrl+alt+a send_text normal Word\x1b[H
+#: map ctrl+alt+a send_text application Word\x1bOH
+
+#: }}}
+
+map ctrl+space send_text all \x10
+
+# }}}
diff --git a/linux/home/.config/mimeapps.list b/linux/home/.config/mimeapps.list
new file mode 100644
index 0000000..85d088c
--- /dev/null
+++ b/linux/home/.config/mimeapps.list
@@ -0,0 +1,14 @@
+[Default Applications]
+application/pdf=zathura.desktop;
+application/tex=zathura.desktop;
+image/png=phototonic.desktop
+image/jpeg=phototonic.desktop;
+video/mp4=mpv.desktop;
+video/mkv=mpv.desktop;
+inode/directory=pcmanfm.desktop
+
+[Added Associations]
+image/png=phototonic.desktop;
+
+[Removed Associations]
+
diff --git a/linux/home/.config/picom/picom.conf b/linux/home/.config/picom/picom.conf
new file mode 100644
index 0000000..d189670
--- /dev/null
+++ b/linux/home/.config/picom/picom.conf
@@ -0,0 +1,494 @@
+#################################
+# Animations #
+#################################
+# requires https://github.com/jonaburg/picom
+# (These are also the default values)
+transition-length = 300
+transition-pow-x = 0.1
+transition-pow-y = 0.1
+transition-pow-w = 0.1
+transition-pow-h = 0.1
+size-transition = true
+
+
+#################################
+# Corners #
+#################################
+# requires: https://github.com/sdhand/compton or https://github.com/jonaburg/picom
+corner-radius = 10.0;
+rounded-corners-exclude = [
+#"window_type = 'normal'",
+# "class_g = 'Bspwm' && class_i = 'presel_feedback'",
+# "class_g = 'URxvt'",
+# "class_g = 'alacritty'",
+# "class_g = 'Org.gnome.Nautilus'",
+# "class_g = 'Nemo'",
+# "class_g = 'firefox'",
+# "class_g = 'Rofi'",
+# "class_g = 'Spotify'",
+# "class_g = 'discord'",
+# "class_g = 'Code'",
+# "class_g = 'TelegramDesktop'",
+# "class_g = 'YouTube Music'",
+ "class_g = 'Polybar'"
+# "class_g = 'qutebrowser'",
+# "class_g = 'Zathura'",
+# "class_g = 'Pavucontrol'"
+
+];
+round-borders = 1;
+#round-borders-exclude = [
+# "class_g = 'TelegramDesktop'",
+#];
+
+
+
+#################################
+# Shadows #
+#################################
+
+
+# Enabled client-side shadows on windows. Note desktop windows
+# (windows with '_NET_WM_WINDOW_TYPE_DESKTOP') never get shadow,
+# unless explicitly requested using the wintypes option.
+#
+shadow = false;
+#shadow = true;
+
+# The blur radius for shadows, in pixels. (defaults to 12)
+# shadow-radius = 12
+shadow-radius = 20;
+
+# The opacity of shadows. (0.0 - 1.0, defaults to 0.75)
+# shadow-opacity = .75
+
+# The left offset for shadows, in pixels. (defaults to -15)
+# shadow-offset-x = -15
+shadow-offset-x = -5;
+
+# The top offset for shadows, in pixels. (defaults to -15)
+# shadow-offset-y = -15
+shadow-offset-y = -5;
+
+# Avoid drawing shadows on dock/panel windows. This option is deprecated,
+# you should use the *wintypes* option in your config file instead.
+#
+# no-dock-shadow = false
+
+# Don't draw shadows on drag-and-drop windows. This option is deprecated,
+# you should use the *wintypes* option in your config file instead.
+#
+# no-dnd-shadow = false
+
+# Red color value of shadow (0.0 - 1.0, defaults to 0).
+# shadow-red = 0
+
+# Green color value of shadow (0.0 - 1.0, defaults to 0).
+# shadow-green = 0
+
+# Blue color value of shadow (0.0 - 1.0, defaults to 0).
+# shadow-blue = 0
+
+# Do not paint shadows on shaped windows. Note shaped windows
+# here means windows setting its shape through X Shape extension.
+# Those using ARGB background is beyond our control.
+# Deprecated, use
+# shadow-exclude = 'bounding_shaped'
+# or
+# shadow-exclude = 'bounding_shaped && !rounded_corners'
+# instead.
+#
+# shadow-ignore-shaped = ''
+
+# Specify a list of conditions of windows that should have no shadow.
+#
+# examples:
+# shadow-exclude = "n:e:Notification";
+#
+# shadow-exclude = []
+shadow-exclude = [
+ "name = 'Notification'",
+ "class_g = 'Conky'",
+ #"class_g ?= 'Notify-osd'",
+ "class_g = 'Cairo-clock'",
+ "class_g = 'Polybar'",
+ "class_g = 'Nwg-menu'",
+ # "class_g = 'Rofi'"
+ "_GTK_FRAME_EXTENTS@:c"
+ ];
+
+# Specify a X geometry that describes the region in which shadow should not
+# be painted in, such as a dock window region. Use
+# shadow-exclude-reg = "x10+0+0"
+# for example, if the 10 pixels on the bottom of the screen should not have shadows painted on.
+#
+# shadow-exclude-reg = ""
+
+# Crop shadow of a window fully on a particular Xinerama screen to the screen.
+# xinerama-shadow-crop = false
+
+
+#################################
+# Fading #
+#################################
+
+
+# Fade windows in/out when opening/closing and when opacity changes,
+# unless no-fading-openclose is used.
+# fading = false
+fading = true
+
+# Opacity change between steps while fading in. (0.01 - 1.0, defaults to 0.028)
+# fade-in-step = 0.028
+fade-in-step = 0.08;
+
+# Opacity change between steps while fading out. (0.01 - 1.0, defaults to 0.03)
+# fade-out-step = 0.03
+fade-out-step = 0.08;
+
+# The time between steps in fade step, in milliseconds. (> 0, defaults to 10)
+# fade-delta = 10
+
+# Specify a list of conditions of windows that should not be faded.
+# fade-exclude = []
+
+# Do not fade on window open/close.
+# no-fading-openclose = false
+
+# Do not fade destroyed ARGB windows with WM frame. Workaround of bugs in Openbox, Fluxbox, etc.
+# no-fading-destroyed-argb = false
+
+
+#################################
+# Transparency / Opacity #
+#################################
+
+# Opacity of inactive windows. (0.1 - 1.0, defaults to 1.0)
+# inactive-opacity = 1
+inactive-opacity = 1;
+
+# Opacity of window titlebars and borders. (0.1 - 1.0, disabled by default)
+# frame-opacity = 1.0
+frame-opacity = 1;
+
+# Default opacity for dropdown menus and popup menus. (0.0 - 1.0, defaults to 1.0)
+# menu-opacity = 1.0
+
+# Let inactive opacity set by -i override the '_NET_WM_OPACITY' values of windows.
+# inactive-opacity-override = true
+inactive-opacity-override = false;
+
+# Default opacity for active windows. (0.0 - 1.0, defaults to 1.0)
+active-opacity = 1.0;
+
+# Dim inactive windows. (0.0 - 1.0, defaults to 0.0)
+inactive-dim = 0.2
+
+# Specify a list of conditions of windows that should always be considered focused.
+# focus-exclude = []
+focus-exclude = ["class_g = 'Plank'", "class_g = 'Nwg-menu'", "class_g = 'st'", "class_g = 'kitty'", "class_g = 'wezterm'", "class_g = 'Alacritty'", "class_g = 'firefox'"];
+
+# Use fixed inactive dim value, instead of adjusting according to window opacity.
+# inactive-dim-fixed = 1.0
+
+# Specify a list of opacity rules, in the format `PERCENT:PATTERN`,
+# like `50:name *= "Firefox"`. picom-trans is recommended over this.
+# Note we don't make any guarantee about possible conflicts with other
+# programs that set '_NET_WM_WINDOW_OPACITY' on frame or client windows.
+# example:
+# opacity-rule = [ "80:class_g = 'URxvt'" ];
+opacity-rule = ["80:class_g = 'betterlockscreen'", "80:class_g = 'URxvt'", "100:class_g = 'firefox'", "100:class_g = 'Zathura'", "80:class_g = 'Spotify'", "80:class_g *?= 'Rofi'", "100:class_g = 'kitty' && focused", "100:class_g = 'kitty' && !focused", "100:class_g = 'Alacritty' && focused", "100:class_g = 'Alacritty' && !focused", "100:class_g = 'wezterm' && focused", "100:class_g = 'wezterm' && !focused"];
+#
+#opacity-rule = ["85:class_g ?= 'Alacritty' && focused"];
+
+#blur-background-exclude = ["class_g = 'scratchpad'"];
+
+#################################
+# Background-Blurring #
+#################################
+
+
+# Parameters for background blurring, see the *BLUR* section for more information.
+blur-method = "dual_kawase";
+#blur-method = "gaussian";
+blur-strength = 6;
+# blur-size = 12
+#
+# blur-deviation = false
+
+# Blur background of semi-transparent / ARGB windows.
+# Bad in performance, with driver-dependent behavior.
+# The name of the switch may change without prior notifications.
+#
+blur-background = false;
+
+# Blur background of windows when the window frame is not opaque.
+# Implies:
+# blur-background
+# Bad in performance, with driver-dependent behavior. The name may change.
+#
+# blur-background-frame = false
+
+
+# Use fixed blur strength rather than adjusting according to window opacity.
+#blur-background-fixed = false
+
+
+# Specify the blur convolution kernel, with the following format:
+# example:
+# blur-kern = "5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1";
+#
+# blur-kern = ''
+#blur-kern = "3x3box";
+blur-kern = "7x7box";
+
+# Exclude conditions for background blur.
+# blur-background-exclude = []
+blur-background-exclude = [
+ "window_type = 'dock'",
+ "window_type = 'desktop'",
+ "_GTK_FRAME_EXTENTS@:c",
+ "class_g = 'kitty'",
+ "class_g = 'Alacritty'",
+ "class_g = 'wezterm'",
+# "class_g = 'Polybar'",
+ "class_g = 'URxvt'",
+ "class_g = 'scratchpad'",
+ "class_g = 'heads-up-display'",
+ "class_g = 'Firefox'",
+ "class_g = 'firefox'",
+ "class_g = 'discord'",
+ "class_g = 'Rofi'",
+ "class_g = 'Zathura'",
+ "class_g = 'Notification'",
+];
+
+#################################
+# General Settings #
+#################################
+
+# Daemonize process. Fork to background after initialization. Causes issues with certain (badly-written) drivers.
+# daemon = false
+
+# Specify the backend to use: `xrender`, `glx`, or `xr_glx_hybrid`.
+# `xrender` is the default one.
+#
+#experimental-backends = true;
+#backend = 'glx'
+backend = "glx";
+#backend = "xrender";
+
+# Enable/disable VSync.
+# vsync = false
+vsync = true
+
+# Enable remote control via D-Bus. See the *D-BUS API* section below for more details.
+# dbus = false
+
+# Try to detect WM windows (a non-override-redirect window with no
+# child that has 'WM_STATE') and mark them as active.
+#
+# mark-wmwin-focused = false
+mark-wmwin-focused = true;
+
+# Mark override-redirect windows that doesn't have a child window with 'WM_STATE' focused.
+mark-ovredir-focused = false;
+#mark-ovredir-focused = true;
+
+# Try to detect windows with rounded corners and don't consider them
+# shaped windows. The accuracy is not very high, unfortunately.
+#
+# detect-rounded-corners = false
+detect-rounded-corners = true;
+
+# Detect '_NET_WM_OPACITY' on client windows, useful for window managers
+# not passing '_NET_WM_OPACITY' of client windows to frame windows.
+#
+# detect-client-opacity = false
+detect-client-opacity = true;
+
+# Specify refresh rate of the screen. If not specified or 0, picom will
+# try detecting this with X RandR extension.
+#
+# refresh-rate = 60
+refresh-rate = 0
+
+# Limit picom to repaint at most once every 1 / 'refresh_rate' second to
+# boost performance. This should not be used with
+# vsync drm/opengl/opengl-oml
+# as they essentially does sw-opti's job already,
+# unless you wish to specify a lower refresh rate than the actual value.
+#
+# sw-opti =
+
+# Use EWMH '_NET_ACTIVE_WINDOW' to determine currently focused window,
+# rather than listening to 'FocusIn'/'FocusOut' event. Might have more accuracy,
+# provided that the WM supports it.
+#
+# use-ewmh-active-win = false
+
+# Unredirect all windows if a full-screen opaque window is detected,
+# to maximize performance for full-screen windows. Known to cause flickering
+# when redirecting/unredirecting windows.
+#
+# unredir-if-possible = false
+
+# Delay before unredirecting the window, in milliseconds. Defaults to 0.
+# unredir-if-possible-delay = 0
+
+# Conditions of windows that shouldn't be considered full-screen for unredirecting screen.
+# unredir-if-possible-exclude = []
+
+# Use 'WM_TRANSIENT_FOR' to group windows, and consider windows
+# in the same group focused at the same time.
+#
+# detect-transient = false
+detect-transient = true
+
+# Use 'WM_CLIENT_LEADER' to group windows, and consider windows in the same
+# group focused at the same time. 'WM_TRANSIENT_FOR' has higher priority if
+# detect-transient is enabled, too.
+#
+# detect-client-leader = false
+detect-client-leader = true
+
+# Resize damaged region by a specific number of pixels.
+# A positive value enlarges it while a negative one shrinks it.
+# If the value is positive, those additional pixels will not be actually painted
+# to screen, only used in blur calculation, and such. (Due to technical limitations,
+# with use-damage, those pixels will still be incorrectly painted to screen.)
+# Primarily used to fix the line corruption issues of blur,
+# in which case you should use the blur radius value here
+# (e.g. with a 3x3 kernel, you should use `--resize-damage 1`,
+# with a 5x5 one you use `--resize-damage 2`, and so on).
+# May or may not work with *--glx-no-stencil*. Shrinking doesn't function correctly.
+#
+# resize-damage = 1
+
+# Specify a list of conditions of windows that should be painted with inverted color.
+# Resource-hogging, and is not well tested.
+#
+# invert-color-include = []
+
+# GLX backend: Avoid using stencil buffer, useful if you don't have a stencil buffer.
+# Might cause incorrect opacity when rendering transparent content (but never
+# practically happened) and may not work with blur-background.
+# My tests show a 15% performance boost. Recommended.
+#
+# glx-no-stencil = false
+
+# GLX backend: Avoid rebinding pixmap on window damage.
+# Probably could improve performance on rapid window content changes,
+# but is known to break things on some drivers (LLVMpipe, xf86-video-intel, etc.).
+# Recommended if it works.
+#
+# glx-no-rebind-pixmap = false
+
+# Disable the use of damage information.
+# This cause the whole screen to be redrawn everytime, instead of the part of the screen
+# has actually changed. Potentially degrades the performance, but might fix some artifacts.
+# The opposing option is use-damage
+#
+# no-use-damage = false
+use-damage = true
+
+# Use X Sync fence to sync clients' draw calls, to make sure all draw
+# calls are finished before picom starts drawing. Needed on nvidia-drivers
+# with GLX backend for some users.
+#
+# xrender-sync-fence = false
+
+# GLX backend: Use specified GLSL fragment shader for rendering window contents.
+# See `compton-default-fshader-win.glsl` and `compton-fake-transparency-fshader-win.glsl`
+# in the source tree for examples.
+#
+# glx-fshader-win = ''
+
+# Force all windows to be painted with blending. Useful if you
+# have a glx-fshader-win that could turn opaque pixels transparent.
+#
+# force-win-blend = false
+
+# Do not use EWMH to detect fullscreen windows.
+# Reverts to checking if a window is fullscreen based only on its size and coordinates.
+#
+# no-ewmh-fullscreen = false
+
+# Dimming bright windows so their brightness doesn't exceed this set value.
+# Brightness of a window is estimated by averaging all pixels in the window,
+# so this could comes with a performance hit.
+# Setting this to 1.0 disables this behaviour. Requires --use-damage to be disabled. (default: 1.0)
+#
+# max-brightness = 1.0
+
+# Make transparent windows clip other windows like non-transparent windows do,
+# instead of blending on top of them.
+#
+# transparent-clipping = false
+
+# Set the log level. Possible values are:
+# "trace", "debug", "info", "warn", "error"
+# in increasing level of importance. Case doesn't matter.
+# If using the "TRACE" log level, it's better to log into a file
+# using *--log-file*, since it can generate a huge stream of logs.
+#
+# log-level = "debug"
+log-level = "warn";
+
+# Set the log file.
+# If *--log-file* is never specified, logs will be written to stderr.
+# Otherwise, logs will to written to the given file, though some of the early
+# logs might still be written to the stderr.
+# When setting this option from the config file, it is recommended to use an absolute path.
+#
+# log-file = '/path/to/your/log/file'
+
+# Show all X errors (for debugging)
+# show-all-xerrors = false
+
+# Write process ID to a file.
+# write-pid-path = '/path/to/your/log/file'
+
+# Window type settings
+#
+# 'WINDOW_TYPE' is one of the 15 window types defined in EWMH standard:
+# "unknown", "desktop", "dock", "toolbar", "menu", "utility",
+# "splash", "dialog", "normal", "dropdown_menu", "popup_menu",
+# "tooltip", "notification", "combo", and "dnd".
+#
+# Following per window-type options are available: ::
+#
+# fade, shadow:::
+# Controls window-type-specific shadow and fade settings.
+#
+# opacity:::
+# Controls default opacity of the window type.
+#
+# focus:::
+# Controls whether the window of this type is to be always considered focused.
+# (By default, all window types except "normal" and "dialog" has this on.)
+#
+# full-shadow:::
+# Controls whether shadow is drawn under the parts of the window that you
+# normally won't be able to see. Useful when the window has parts of it
+# transparent, and you want shadows in those areas.
+#
+# redir-ignore:::
+# Controls whether this type of windows should cause screen to become
+# redirected again after been unredirected. If you have unredir-if-possible
+# set, and doesn't want certain window to cause unnecessary screen redirection,
+# you can set this to `true`.
+#
+wintypes:
+{
+ #normal = { full-shadow = true; };
+ #menu = { full-shadow = true; };
+ tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; full-shadow = false; };
+ dock = { shadow = false; };
+ dnd = { shadow = false; };
+ popup_menu = { full-shadow = false opacity = 0.8; };
+ #utility = { full-shadow = true; };
+ #toolbar = { full-shadow = true; };
+ notification = { opacity = 0.75; }
+};
+
diff --git a/linux/home/.config/plank/dock/launchers/Alacritty.dockitem b/linux/home/.config/plank/dock/launchers/Alacritty.dockitem
new file mode 100644
index 0000000..b3091c5
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/Alacritty.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/Alacritty.desktop
diff --git a/linux/home/.config/plank/dock/launchers/com.obsproject.Studio.dockitem b/linux/home/.config/plank/dock/launchers/com.obsproject.Studio.dockitem
new file mode 100644
index 0000000..7626308
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/com.obsproject.Studio.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/com.obsproject.Studio.desktop
diff --git a/linux/home/.config/plank/dock/launchers/discord.dockitem b/linux/home/.config/plank/dock/launchers/discord.dockitem
new file mode 100644
index 0000000..c6736ba
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/discord.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/discord.desktop
diff --git a/linux/home/.config/plank/dock/launchers/firefox.dockitem b/linux/home/.config/plank/dock/launchers/firefox.dockitem
new file mode 100644
index 0000000..0dbfff7
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/firefox.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/firefox.desktop
diff --git a/linux/home/.config/plank/dock/launchers/gimp.dockitem b/linux/home/.config/plank/dock/launchers/gimp.dockitem
new file mode 100644
index 0000000..c9f9438
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/gimp.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/gimp.desktop
diff --git a/linux/home/.config/plank/dock/launchers/google-chrome.dockitem b/linux/home/.config/plank/dock/launchers/google-chrome.dockitem
new file mode 100644
index 0000000..cd04294
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/google-chrome.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/google-chrome.desktop
diff --git a/linux/home/.config/plank/dock/launchers/obsidian.dockitem b/linux/home/.config/plank/dock/launchers/obsidian.dockitem
new file mode 100644
index 0000000..0170dbd
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/obsidian.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/obsidian.desktop
diff --git a/linux/home/.config/plank/dock/launchers/org.gnome.Nautilus.dockitem b/linux/home/.config/plank/dock/launchers/org.gnome.Nautilus.dockitem
new file mode 100644
index 0000000..ae5f8d9
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/org.gnome.Nautilus.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/org.gnome.Nautilus.desktop
diff --git a/linux/home/.config/plank/dock/launchers/spotify.dockitem b/linux/home/.config/plank/dock/launchers/spotify.dockitem
new file mode 100644
index 0000000..8ae95b4
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/spotify.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/spotify.desktop
diff --git a/linux/home/.config/plank/dock/launchers/steam.dockitem b/linux/home/.config/plank/dock/launchers/steam.dockitem
new file mode 100644
index 0000000..143d3b4
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/steam.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/steam.desktop
diff --git a/linux/home/.config/plank/dock/launchers/trash.dockitem b/linux/home/.config/plank/dock/launchers/trash.dockitem
new file mode 100644
index 0000000..567a63e
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/trash.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=docklet://trash
diff --git a/linux/home/.config/plank/dock/launchers/virtualbox.dockitem b/linux/home/.config/plank/dock/launchers/virtualbox.dockitem
new file mode 100644
index 0000000..fa95934
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/virtualbox.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/virtualbox.desktop
diff --git a/linux/home/.config/plank/dock/launchers/vlc.dockitem b/linux/home/.config/plank/dock/launchers/vlc.dockitem
new file mode 100644
index 0000000..c20771a
--- /dev/null
+++ b/linux/home/.config/plank/dock/launchers/vlc.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/vlc.desktop
diff --git a/linux/home/.config/plank/dock1/launchers/code-oss.dockitem b/linux/home/.config/plank/dock1/launchers/code-oss.dockitem
new file mode 100644
index 0000000..3d8af2b
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/code-oss.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/code-oss.desktop
diff --git a/linux/home/.config/plank/dock1/launchers/com.obsproject.Studio.dockitem b/linux/home/.config/plank/dock1/launchers/com.obsproject.Studio.dockitem
new file mode 100644
index 0000000..39554a1
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/com.obsproject.Studio.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///home/srdusr/.local/share/applications/com.obsproject.Studio.desktop
diff --git a/linux/home/.config/plank/dock1/launchers/discord.dockitem b/linux/home/.config/plank/dock1/launchers/discord.dockitem
new file mode 100644
index 0000000..c6736ba
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/discord.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/discord.desktop
diff --git a/linux/home/.config/plank/dock1/launchers/firefox.dockitem b/linux/home/.config/plank/dock1/launchers/firefox.dockitem
new file mode 100644
index 0000000..0dbfff7
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/firefox.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/firefox.desktop
diff --git a/linux/home/.config/plank/dock1/launchers/obsidian.dockitem b/linux/home/.config/plank/dock1/launchers/obsidian.dockitem
new file mode 100644
index 0000000..0170dbd
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/obsidian.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/obsidian.desktop
diff --git a/linux/home/.config/plank/dock1/launchers/org.qbittorrent.qBittorrent.dockitem b/linux/home/.config/plank/dock1/launchers/org.qbittorrent.qBittorrent.dockitem
new file mode 100644
index 0000000..7475952
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/org.qbittorrent.qBittorrent.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/org.qbittorrent.qBittorrent.desktop
diff --git a/linux/home/.config/plank/dock1/launchers/pcmanfm.dockitem b/linux/home/.config/plank/dock1/launchers/pcmanfm.dockitem
new file mode 100644
index 0000000..bc3af9d
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/pcmanfm.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/pcmanfm.desktop
diff --git a/linux/home/.config/plank/dock1/launchers/phototonic.dockitem b/linux/home/.config/plank/dock1/launchers/phototonic.dockitem
new file mode 100644
index 0000000..e391893
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/phototonic.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///home/srdusr/.local/share/applications/phototonic.desktop
diff --git a/linux/home/.config/plank/dock1/launchers/steam.dockitem b/linux/home/.config/plank/dock1/launchers/steam.dockitem
new file mode 100644
index 0000000..143d3b4
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/steam.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/steam.desktop
diff --git a/linux/home/.config/plank/dock1/launchers/trash.dockitem b/linux/home/.config/plank/dock1/launchers/trash.dockitem
new file mode 100644
index 0000000..567a63e
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/trash.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=docklet://trash
diff --git a/linux/home/.config/plank/dock1/launchers/vlc.dockitem b/linux/home/.config/plank/dock1/launchers/vlc.dockitem
new file mode 100644
index 0000000..c20771a
--- /dev/null
+++ b/linux/home/.config/plank/dock1/launchers/vlc.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/vlc.desktop
diff --git a/linux/home/.config/polybar/config.ini b/linux/home/.config/polybar/config.ini
new file mode 100644
index 0000000..afb14a8
--- /dev/null
+++ b/linux/home/.config/polybar/config.ini
@@ -0,0 +1,671 @@
+;==========================================================
+;
+;
+; ██████╗ ██████╗ ██╗ ██╗ ██╗██████╗ █████╗ ██████╗
+; ██╔══██╗██╔═══██╗██║ ╚██╗ ██╔╝██╔══██╗██╔══██╗██╔══██╗
+; ██████╔╝██║ ██║██║ ╚████╔╝ ██████╔╝███████║██████╔╝
+; ██╔═══╝ ██║ ██║██║ ╚██╔╝ ██╔══██╗██╔══██║██╔══██╗
+; ██║ ╚██████╔╝███████╗██║ ██████╔╝██║ ██║██║ ██║
+; ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝
+;
+;
+; To learn more about how to configure Polybar
+; go to https://github.com/polybar/polybar
+;
+; The README contains a lot of information
+;
+;==========================================================
+
+[settings]
+screenchange-reload = true
+pseudo-transparency = true
+;throttle-output = 5
+;throttle-output-for = 10
+;compositing-background = source
+;compositing-foreground = over
+;compositing-overline = over
+;compositing-underline = over
+;compositing-border = over
+
+[colors]
+foreground = #fafafa
+foreground-alt = #aaCECCC9
+;background = #aa000000
+background = #dd000000
+background-alt = #aaCECCC9
+disabled = #707880
+alert = #ff8989
+blue = #61afef
+dark_blue = #42A5F5
+light_blue = #ADD8E6
+nord = #81a1c1
+purple = #c882e7
+orange = #E57C46
+gray = #676E95
+red = #EC7875
+pink = #EC407A
+yellow = #FDD835
+amber = #FBC02D
+indigo = #6C77BB
+green = #61C766
+lime = #B9C244
+
+[fonts]
+font0 = "SF Pro Mono:style=Display Bold:size=10;2"
+font1 = "Material Design Icons:style=Bold:size=11.5;2"
+font2 = "Material Design Icons:style=Bold:size=13.5;2"
+font3 = "SF Pro:style=Medium:size=10.5;2"
+font4 = "SF Pro:style=Regular:size=13;2"
+font5 = "UbuntuMono Nerd font:size=11;2"
+font6 = "Feather:style=Regular:size=15;4"
+font7 = "Font Awesome 6 Pro Solid:style=Solid:size=14;4"
+font8 = "JetBrainsMono Nerd Font:size=9;2"
+font9 = "Fira Nerd font:size=11;2"
+font10 = "RobotoMono Nerd Font:weight=bold:size=9;2"
+font11 = "RobotoMono Nerd Font:size=10;3"
+font12 = "RobotoMono Nerd Font:size=11;3"
+
+[common]
+line-size = 1pt
+enable-ipc = true
+wm-restack = bspwm
+dpi = 96
+font-0 = ${fonts.font0}
+font-1 = ${fonts.font1}
+font-2 = ${fonts.font2}
+font-3 = ${fonts.font3}
+font-4 = ${fonts.font4}
+font-5 = ${fonts.font5}
+font-6 = ${fonts.font6}
+font-7 = ${fonts.font7}
+font-8 = ${fonts.font8}
+font-9 = ${fonts.font9}
+font-10 = ${fonts.font10}
+font-11 = ${fonts.font11}
+font-12 = ${fonts.font12}
+background = ${colors.background}
+foreground = ${colors.foreground}
+
+[bar/main-0]
+monitor = ${env:MONITOR:}
+width = 99%
+offset-x = 0.5%
+offset-y = 0.5%
+height = 20pt
+radius = 10.0
+fixed-center = true
+inherit = common
+;modules-left = space space menu space space space bspwm space space
+modules-left = space space menu space space space bspwm space space big_space space space cpu space sep space memory space sep space temperature space sep space battery
+modules-center = space space date space space
+;modules-center = space space cpu space sep space memory space sep space temperature space sep space battery big_space date big_space wireless-network space sep space netspeed space sep space vpn space space
+;modules-right = space space mic space sep space volume space sep space backlight space sep space inbox space sep space systray space space sep control space space
+modules-right = space space wireless-network space sep space netspeed space sep space vpn space space big_space space space mic space sep space volume space sep space backlight space sep space inbox space sep space systray space space sep control space space
+
+[bar/main-1]
+monitor = ${env:MONITOR:}
+;width = 260px
+width = 180px
+offset-x = 0%:+10px
+offset-y = 0.5%
+height = 20pt
+radius = 10.0
+fixed-center = true
+inherit = common
+modules-left = space space menu space space space bspwm space space
+
+[bar/main-2]
+monitor = ${env:MONITOR:}
+;width = 230px
+;offset-x = 33.3333%:-130px
+width = 295px
+offset-x = 33.3333%:-195px
+offset-y = 0.5%
+height = 20pt
+radius = 10.0
+fixed-center = true
+inherit = common
+modules-center = space space cpu space sep space memory space sep space temperature space sep space battery space space
+
+[bar/main-3]
+monitor = ${env:MONITOR:}
+;width = 220px
+;width = 150px
+width = 170px
+;offset-x = 50%:-110px
+;offset-x = 50%:-110px
+offset-x = 50%:-85px
+offset-y = 0.5%
+height = 20pt
+radius = 10.0
+fixed-center = true
+inherit = common
+font-0 = "RobotoMono Nerd Font:weight=bold:size=9;2"
+font-1 = "RobotoMono Nerd Font:size=10;3"
+font-2 = "RobotoMono Nerd Font:size=11;3"
+;modules-center = space space day space sep space date space sep space time space space
+modules-center = space space date space space
+
+[bar/main-4]
+monitor = ${env:MONITOR:}
+;width = 230px
+;offset-x = 66.6667%:-100px
+width = 295px
+offset-x = 66.6667%:-100px
+offset-y = 0.5%
+height = 20pt
+radius = 10.0
+fixed-center = true
+;padding-right = 4
+inherit = common
+;modules-center = space space space sep space vpn space space
+modules-center = space space wireless-network space sep space netspeed space sep space vpn space space
+;modules-center = space space wireless-network netspeed space sep space space space
+
+[bar/main-5]
+monitor = ${env:MONITOR:}
+;width = 260px
+width = 180px
+offset-x = 100%:-190px
+offset-y = 0.5%
+height = 20pt
+radius = 10.0
+fixed-center = true
+padding-left = 2
+;padding-right = 2
+inherit = common
+modules-right = space space mic space sep space volume space sep space backlight space sep space inbox space sep space systray space space sep control space space
+
+
+;; Modules
+
+[module/bspwm]
+type = internal/bspwm
+format = <label-state>
+format-padding = 2
+format-foreground = ${colors.foreground}
+index-sort = true
+enable-click = true
+reverse-scroll = false
+label-focused = ●
+label-focused-padding = 1
+label-occupied = "%name%"
+label-occupied-foreground = ${colors.foreground}
+label-occupied-padding = 1
+;label-empty = ○
+label-empty="%name%"
+label-empty-foreground = ${colors.disabled}
+label-empty-padding = 1
+format-font = 5
+
+[module/xwindow]
+type = internal/xwindow
+format = <label>
+format-background = ${colors.background}
+format-foreground = ${colors.foreground}
+format-padding = 2
+label = %title%
+label-maxlen = 40
+label-empty = ~/
+label-empty-foreground = ${colors.disabled}
+
+[module/volume]
+type = internal/pulseaudio
+;format-volume = <label-volume> <bar-volume>
+;label-volume = 
+;label-volume-foreground = ${colors.foreground}
+;label-muted =  muted
+;bar-volume-width = 10
+;bar-volume-foreground-0 = #55aa55
+;bar-volume-foreground-1 = #55aa55
+;bar-volume-foreground-2 = #55aa55
+;bar-volume-foreground-3 = #55aa55
+;bar-volume-foreground-4 = #55aa55
+;bar-volume-foreground-5 = #f5a70a
+;bar-volume-foreground-6 = #ff5555
+;bar-volume-gradient = false
+;bar-volume-indicator = │
+;bar-volume-indicator-font = 2
+;bar-volume-indicator-foreground = #ff
+;bar-volume-fill = ─
+;bar-volume-fill-font = 2
+;bar-volume-empty = ─
+;bar-volume-empty-font = 2
+;bar-volume-empty-foreground =
+
+;format-volume = <ramp-volume> <label-volume
+format-volume = <ramp-volume>
+format-volume-font = 9
+format-volume-padding = 0
+label-volume = %percentage:2%%
+label-volume-padding = 1
+;label-muted-foreground = ${colors.foreground}
+;format-volume-foreground = ${colors.purple}
+format-muted-foreground = ${colors.red}
+format-muted-underline = ${colors.red}
+ramp-volume-foreground = ${colors.foreground}
+;label-muted = "muted"
+label-muted = "  "
+label-muted-foreground = ${colors.disabled}
+ramp-volume-0 = "  "
+ramp-volume-1 = "  "
+ramp-volume-2 = "  "
+ramp-volume-3 = "  "
+ramp-volume-4 = " "
+ramp-volume-5 = " "
+ramp-volume-6 = " "
+click-right = "pavucontrol"
+scroll-interval = 10
+
+[module/inbox]
+type = custom/text
+content-foreground = ${colors.foreground}
+;content-padding = 1
+;content-font = 3
+content = "󰮒"
+;content = " "
+;󰧬󰮒󰻨
+click-left = ~/.config/eww/scripts/openNotificationCenter.sh
+;click-left = notification-center
+
+[module/day]
+type = internal/date
+interval = 1
+date = %a
+label = %date%
+label-foreground = ${colors.foreground}
+
+;[module/day]
+;type = internal/date
+;interval = 1
+;date = %A
+;label = %date:8%
+;label-foreground = ${colors.foreground}
+;
+;[module/date]
+;type = internal/date
+;interval = 1
+;;date = %d-%m-%Y
+;date = %d %b %Y
+;label = %date%
+;label-foreground = ${colors.foreground}
+;format = %{A1:$HOME/.config/eww/scripts/popup calendar &:}<label>%{A}
+
+[module/date]
+type = internal/date
+interval = 1
+label = %date% %time%
+;label-padding = 2.5
+label-background =
+date = %a %d %b %Y
+time = %H:%M:%S
+format-font = 11
+
+
+[module/calendar]
+type = custom/text
+content = 󰸗
+;content-font = 1
+content-padding = 1
+content-foreground = ${colors.primary}
+enable-click = true
+click-left = ~/.config/eww/scripts/popup calendar &
+
+[module/time]
+type = internal/date
+interval = 1
+date = %H:%M:%S
+label = %date%
+label-foreground = ${colors.foreground}
+
+[module/memory]
+type=internal/memory
+interval=5
+format=<label>
+format-font=8
+format-prefix="󰨅"
+;󰘚󰥜󰥠󰨅
+format-foreground=${colors.foreground}
+format-prefix-foreground=${colors.foreground}
+label-font=2
+label-foreground=${colors.foreground}
+label="%{A1:alacritty -e htop &:} %gb_used%%{A}"
+
+[module/cpu]
+type=internal/cpu
+interval=5
+format-prefix-font=4
+format-prefix="󰍛 "
+format-padding=0
+;format-prefix-foreground=${colors.green}
+format-prefix-foreground=${colors.foreground}
+format-foreground=${colors.foreground}
+label="%percentage%%"
+label-foreground=${colors.foreground}
+label-font=2
+
+;[module/temperature]
+;type=internal/temperature
+;; Seconds to sleep between updates
+;; Default: 1
+;interval=10
+;; Thermal zone to use
+;; To list all the zone types, run
+;; $ for i in /sys/class/thermal/thermal_zone*; do echo "$i: $(<$i/type)"; done
+;; Default: 0
+;thermal-zone=0
+;; Full path of temperature sysfs path
+;; Use `sensors` to find preferred temperature source, then run
+;; $ for i in /sys/class/hwmon/hwmon*/temp*_input; do echo "$(<$(dirname $i)/name): $(cat ${i%_*}_label 2>/dev/null || echo $(basename ${i%_*})) $(readlink -f $i)"; done
+;; to find path to desired file
+;; Default reverts to thermal zone setting
+;;hwmon-path=/sys/devices/platform/dell_smm_hwmon/hwmon/hwmon2/temp1_input
+;hwmon-path=/sys/devices/platform/dell_smm_hwmon/hwmon/hwmon1/temp1_input
+;; Base temperature for where to start the ramp (in degrees celsius)
+;; Default: 0
+;base-temperature=20
+;warn-temperature=60
+;; Threshold temperature to display warning label (in degrees celsius)
+;; Default: 80
+;format-prefix="  "
+;format-warn-prefix="  "
+;format-warn-foreground=${colors.red}
+;format-foreground=${colors.foreground}
+;format-font=4
+;format-warn-font=4
+;label-warn-font=2
+;label-foreground=${colors.foreground}
+;format = "<label>"
+;label-font=2
+
+[module/temperature]
+type = custom/script
+interval = 5
+format = <label>
+format-prefix = "  "
+format-prefix-foreground = ${colors.foreground}
+exec = ~/.config/polybar/scripts/temperature.sh
+
+
+[module/battery]
+type = custom/script
+exec = $HOME/.scripts/battery.sh
+format-font = 1
+format-prefix = ""
+interval = 10
+;click-right = xfce4-power-manager-settings
+
+[module/backlight]
+type = internal/backlight
+; Use the following command to list available cards:
+; $ ls -1 /sys/class/backlight/
+; Default: first usable card in /sys/class/backlight (new in version 3.7.0)
+card = intel_backlight
+; Use the `/sys/class/backlight/.../actual-brightness` file
+; rather than the regular `brightness` file.
+; New in version 3.6.0
+; Changed in version: 3.7.0: Defaults to true also on amdgpu backlights
+; Default: true
+;use-actual-brightness = true
+; Interval in seconds after which after which the current brightness is read
+; (even if no update is detected).
+; Use this as a fallback if brightness updates are not registering in polybar
+; (which happens if the use-actual-brightness is false).
+; There is no guarantee on the precisio of this timing.
+; Set to 0 to turn off
+; New in version 3.7.0
+; Default: 0 (5 if use-actual-brightness is false)
+;poll-interval = 0
+; Enable changing the backlight with the scroll wheel
+; NOTE: This may require additional configuration on some systems. Polybar will
+; write to `/sys/class/backlight/${self.card}/brightness` which requires polybar
+; to have write access to that file.
+; DO NOT RUN POLYBAR AS ROOT.
+; The recommended way is to add the user to the
+; `video` group and give that group write-privileges for the `brightness` file.
+; See the ArchWiki for more information:
+; https://wiki.archlinux.org/index.php/Backlight#ACPI
+; Default: false
+enable-scroll = true
+; Interval for changing the brightness (in percentage points).
+; New in version 3.7.0
+; Default: 5
+scroll-interval = 10
+; Available tags:
+; <label> (default)
+; <ramp>
+; <bar>
+format = <ramp>
+format-foreground = {colors.foreground}
+; Available tokens:
+; %percentage% (default)
+label = %percentage:2%%
+label-font=7
+;; Only applies if <ramp> is used
+ramp-0 = 󰃞
+ramp-1 = 󰃝
+ramp-2 = 󰃟
+ramp-3 = 󰃠
+;; Only applies if <bar> is used
+;bar-width = 10
+;bar-indicator = |
+;bar-fill = ─
+;bar-empty = ─
+
+[module/bluetooth]
+type = custom/text
+content = ""
+format = <label>
+content-foreground = ${colors.foreground}
+; click-middle = bspc rule -a '*' -o state=floating rectangle=400x120+775+48 && kitty -e sudo polybarblue.sh
+ click-left = blueman-manager
+
+[module/control]
+type = custom/script
+exec = echo 􀜊
+format = <label>
+format-padding = 1
+label-padding =
+content-background =
+format-foreground = ${colors.foreground}
+click-left = ~/.config/eww/scripts/openControlCenter.sh
+;click-left = control-center
+;click-left = $HOME/.scripts/toggle-control &
+
+[module/wireless-network]
+type = internal/network
+interface = wlan0
+interval = 3.0
+unknown-as-up = true
+format-connected-background = ${colors.background}
+format-connected-foreground = ${colors.foreground}
+format-connected-padding = 1
+format-connected = %{A1:$HOME/.scripts/rofi-network-manager.sh:}<ramp-signal> <label-connected>%{A}
+label-connected = "%essid:03:5%/%local_ip%"
+#label-connected = "ESSID/127.0.0.1"
+format-disconnected-background = ${colors.background}
+format-disconnected-foreground = ${colors.foreground}
+format-disconnected-padding = 1
+format-disconnected = %{A1:$HOME/.scripts//rofi-network-manager.sh:}<label-disconnected>%{A}
+;label-disconnected ="Network Disconnected 󱍢 ......"
+;label-disconnected =" 󰤮 Network Disconnected ......... 󱍢 .......... "
+label-disconnected ="󰤮 Net Disconnected"
+ramp-signal-0 = "󰤯"
+ramp-signal-1 = "󰤟"
+ramp-signal-2 = "󰤢"
+ramp-signal-3 = "󰤥"
+ramp-signal-4 = "󰤨"
+ramp-signal-foreground = ${colors.white}
+enable-click = true
+click-left = $HOME/.scripts/rofi-network-manager.sh &
+
+[module/wifi]
+type = custom/script
+tail = true
+interval = 1
+format = <label>
+format-prefix = " "
+wifi = wifi
+wifi-alt = iwgetid -r
+exec = iwgetid -r
+click-left = kitty nmtui
+click-right = nm-connection-editor
+label-disconnected = %{A1:nm-connection-editor:}%essid%%{A}
+
+[module/wlan-signal]
+type = custom/script
+label = %output%
+exec = awk 'NR==3 {print $4 "00 dBm"}' /proc/net/wireless
+format-prefix = "ﴽ "
+format-prefix-foreground = ${colors.yellow}
+format-background = ${colors.background}
+format-foreground = ${colors.foreground}
+interval = 1
+
+[module/netspeed]
+type = internal/network
+;interface = ${system.sys_network_interface}
+interface-type = wireless
+interval = 3.0
+accumulate-stats = true
+;unknown-as-up = true
+format-connected = <label-connected>
+format-disconnected = <label-disconnected>
+;label-disconnected = ""
+label-disconnected = " 0 KB/s "
+format-disconnected-prefix = "󰯎"
+format-connected-prefix = "󰯎"
+speed-unit = ""
+label-connected = "%netspeed:5%B/s "
+
+[module/upspeed]
+type = internal/network
+interface-type = wireless
+interval = 1
+format-connected = <label-connected>
+format-disconnected = <label-disconnected>
+label-disconnected = ""
+format-disconnected-prefix = ""
+format-connected-prefix = " "
+label-connected = " %upspeed:8%"
+
+[module/downspeed]
+type = internal/network
+interface-type = wireless
+interval = 1
+format-connected = <label-connected>
+format-disconnected = <label-disconnected>
+label-disconnected = ""
+format-disconnected-prefix = ""
+format-connected-prefix = ""
+label-connected = " %downspeed:8%"
+
+[module/vpn]
+type = custom/script
+#exec = protonvpn status
+exec = ~/.config/polybar/scripts/vpn.sh
+;tail = true
+interval = 1
+label-font = 6
+format-prefix = " "
+format = <label>
+click-left = sudo protonvpn c -f
+click-right = sudo protonvpn disconnect
+;
+
+[module/gpu-nvidia]
+type = custom/script
+exec = $HOME/.config/polybar/scripts/gpu-nvidia.sh
+interval = 2
+format-font = 2
+format-foreground = #69F0AE
+
+[module/gpu-intel]
+type = custom/script
+#exec = $HOME/.config/polybar/scripts/gpu-intel.sh
+interval = 2
+
+[module/spotify]
+type = custom/script
+tail = true
+interval = 1
+format-prefix = " "
+format = <label>
+exec = ~/.config/polybar/scripts/get_spotify_status.sh
+
+[module/menu]
+type = custom/text
+content = 󱎂
+;󰣇󰈷󰨝󰵆􀇸􀘸􀟒􀣺􀦲
+content-font = 3
+content-padding = 1
+content-foreground = ${colors.foreground}
+enable-click = true
+click-left = ~/.config/jgmenu/scripts/startmenu.sh
+click-right = $HOME/.scripts/menu_full.sh
+
+[module/power]
+type = custom/text
+content = 󰐥
+;content = 襤
+content-foreground = ${colors.red}
+content-padding = 1
+label-margin = 3
+click-left = ~/.scripts/sysmenu.sh
+
+[module/systray]
+type=custom/ipc
+hook-0=echo " "
+hook-1=echo " "
+click-left=systray
+initial=2
+format-font=2
+format-foreground=${colors.blue}
+;format-foreground=${colors.foreground}
+
+[module/weather]
+type = custom/script
+exec = "sh ~/.config/polybar/weather.sh"
+interval = 700
+
+[module/tray]
+type = internal/tray
+format-margin = 8px
+tray-spacing = 8px
+
+[module/updates]
+type = custom/script
+tail = true
+interval = 1
+format-prefix = " "
+format = <label>
+exec = checkupdates | wc -l
+click-left = kitty yay -Syu --noconfirm
+
+[module/mic]
+type = custom/script
+interval = 0.5
+exec = $HOME/.config/polybar/scripts/microphone.sh
+format = <label>
+format-font = 9
+click-left = pamixer --source 1 -t
+scroll-up = pamixer --source 1 -i 5
+scroll-down = pamixer --source 1 -d 5
+
+
+;; decor
+
+[module/sep]
+type = custom/text
+content = "|"
+content-foreground = ${colors.disabled}
+
+[module/space]
+type = custom/text
+content = " "
+
+[module/big_space]
+type = custom/text
+content = " "
+
+; vim:ft=dosini
diff --git a/linux/home/.config/polybar/launch.sh b/linux/home/.config/polybar/launch.sh
new file mode 100755
index 0000000..5468c8e
--- /dev/null
+++ b/linux/home/.config/polybar/launch.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env sh
+
+# Terminate already running bar instances
+killall -q polybar
+
+# Wait until the processes have been shut down
+while pgrep -u "$UID" -x polybar >/dev/null; do sleep 1; done
+
+
+# Launch bar
+#polybar main-0 &
+polybar main-1 &
+polybar main-2 &
+polybar main-3 &
+polybar main-4 &
+polybar main-5 &
+
+# Define bars per monitors
+#declare -A ARRANGEMENTS=(["$mainmonitor"]="main-0" ["$secondmonitor"]="main-0")
+declare -A ARRANGEMENTS=(["$mainmonitor"]="main-1,main-2,main-3,main-4,main-5" ["$secondmonitor"]="main-1,main-2,main-3,main-4,main-5")
+
+# Each key
+for MONITOR in "${!ARRANGEMENTS[@]}"; do
+ # split at `,` into array
+ while IFS=',' read -ra BARLIST; do
+ # for each bar (seperated by `,`) at current key
+ for BAR in "${BARLIST[@]}"; do
+ MONITOR="$MONITOR" polybar --reload "$BAR" &
+ done
+ done <<< "${ARRANGEMENTS[$MONITOR]}"
+done
diff --git a/linux/home/.config/polybar/scripts/bluetooth.sh b/linux/home/.config/polybar/scripts/bluetooth.sh
new file mode 100755
index 0000000..061604b
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/bluetooth.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+if [ $(bluetoothctl show | grep "Powered: yes" | wc -c) -eq 0 ]
+then
+ echo ""
+else
+ if [ $(echo info | bluetoothctl | grep 'Device' | wc -c) -eq 0 ]
+ then
+ echo ""
+ fi
+ echo ""
+fi
+
diff --git a/linux/home/.config/polybar/scripts/check-network.sh b/linux/home/.config/polybar/scripts/check-network.sh
new file mode 100755
index 0000000..dabe74c
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/check-network.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+count=0
+disconnected="睊"
+wireless_connected="直"
+ethernet_connected="泌"
+
+ID="$(ip link | awk '/state UP/ {print $2}')"
+
+while true; do
+ if (ping -c 1 archlinux.org || ping -c 1 google.com || ping -c 1 bitbucket.org || ping -c 1 github.com || ping -c 1 sourceforge.net) &>/dev/null; then
+ if [[ $ID == e* ]]; then
+ echo "$ethernet_connected" ; sleep 25
+ else
+ echo "$wireless_connected" ; sleep 25
+ fi
+ else
+ echo "$disconnected" ; sleep 0.5
+ fi
+done
+
diff --git a/linux/home/.config/polybar/scripts/check_updates.sh b/linux/home/.config/polybar/scripts/check_updates.sh
new file mode 100755
index 0000000..52e51a9
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/check_updates.sh
@@ -0,0 +1,118 @@
+
+#!/usr/bin/bash
+#
+# checkupdates: Safely print a list of pending updates.
+#
+# Copyright (c) 2013 Kyle Keen <keenerd@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+declare -r myname='checkupdates'
+declare -r myver='1.0.0'
+
+plain() {
+ (( QUIET )) && return
+ local mesg=$1; shift
+ printf "${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&1
+}
+
+msg() {
+ (( QUIET )) && return
+ local mesg=$1; shift
+ printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&1
+}
+
+msg2() {
+ (( QUIET )) && return
+ local mesg=$1; shift
+ printf "${BLUE} ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&1
+}
+
+ask() {
+ local mesg=$1; shift
+ printf "${BLUE}::${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}" "$@" >&1
+}
+
+warning() {
+ local mesg=$1; shift
+ printf "${YELLOW}==> $(gettext "WARNING:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2
+}
+
+error() {
+ local mesg=$1; shift
+ printf "${RED}==> $(gettext "ERROR:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2
+}
+
+# check if messages are to be printed using color
+unset ALL_OFF BOLD BLUE GREEN RED YELLOW
+if [[ -t 2 && ! $USE_COLOR = "n" ]]; then
+ # prefer terminal safe colored and bold text when tput is supported
+ if tput setaf 0 &>/dev/null; then
+ ALL_OFF="$(tput sgr0)"
+ BOLD="$(tput bold)"
+ BLUE="${BOLD}$(tput setaf 4)"
+ GREEN="${BOLD}$(tput setaf 2)"
+ RED="${BOLD}$(tput setaf 1)"
+ YELLOW="${BOLD}$(tput setaf 3)"
+ else
+ ALL_OFF="\e[1;0m"
+ BOLD="\e[1;1m"
+ BLUE="${BOLD}\e[1;34m"
+ GREEN="${BOLD}\e[1;32m"
+ RED="${BOLD}\e[1;31m"
+ YELLOW="${BOLD}\e[1;33m"
+ fi
+fi
+readonly ALL_OFF BOLD BLUE GREEN RED YELLOW
+
+
+if (( $# > 0 )); then
+ echo "${myname} v${myver}"
+ echo
+ echo "Safely print a list of pending updates"
+ echo
+ echo "Usage: ${myname}"
+ echo
+ echo 'Note: Export the "CHECKUPDATES_DB" variable to change the path of the temporary database.'
+ exit 0
+fi
+
+if ! type -P fakeroot >/dev/null; then
+ error 'Cannot find the fakeroot binary.'
+ exit 1
+fi
+
+if [[ -z $CHECKUPDATES_DB ]]; then
+ CHECKUPDATES_DB="${TMPDIR:-/tmp}/checkup-db-${USER}/"
+fi
+
+trap 'rm -f $CHECKUPDATES_DB/db.lck' INT TERM EXIT
+
+DBPath="$(pacman-conf DBPath)"
+if [[ -z "$DBPath" ]] || [[ ! -d "$DBPath" ]]; then
+ DBPath="/var/lib/pacman/"
+fi
+
+mkdir -p "$CHECKUPDATES_DB"
+ln -s "${DBPath}/local" "$CHECKUPDATES_DB" &> /dev/null
+if ! fakeroot -- pacman -Sy --dbpath "$CHECKUPDATES_DB" --logfile /dev/null &> /dev/null; then
+ error 'Cannot fetch updates'
+ exit 1
+fi
+pacman -Qu --dbpath "$CHECKUPDATES_DB" 2> /dev/null | grep -v '\[.*\]'
+
+exit 0
+
+# vim: set noet:
diff --git a/linux/home/.config/polybar/scripts/cmus.sh b/linux/home/.config/polybar/scripts/cmus.sh
new file mode 100755
index 0000000..2f42c63
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/cmus.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+prepend_zero () {
+ seq -f "%02g" $1 $1
+}
+
+artist=$(echo -n $(cmus-remote -C status | grep "tag artist" | cut -c 12-))
+
+if [[ $artist = *[!\ ]* ]]; then
+ song=$(echo -n $(cmus-remote -C status | grep title | cut -c 11-))
+ position=$(cmus-remote -C status | grep position | cut -c 10-)
+ minutes1=$(prepend_zero $(($position / 60)))
+ seconds1=$(prepend_zero $(($position % 60)))
+ duration=$(cmus-remote -C status | grep duration | cut -c 10-)
+ minutes2=$(prepend_zero $(($duration / 60)))
+ seconds2=$(prepend_zero $(($duration % 60)))
+ echo -n "$artist - $song "
+else
+ echo
+fi
+
+#prepend_zero () {
+# seq -f "%02g" $1 $1
+#}
+#
+#artist=$(echo -n $(cmus-remote -C status | grep "tag artist" | cut -c 12-))
+#
+#if [[ $artist = *[!\ ]* ]]; then
+# song=$(echo -n $(cmus-remote -C status | grep title | cut -c 11-))
+# position=$(cmus-remote -C status | grep position | cut -c 10-)
+# minutes1=$(prepend_zero $(($position / 60)))
+# seconds1=$(prepend_zero $(($position % 60)))
+# duration=$(cmus-remote -C status | grep duration | cut -c 10-)
+# minutes2=$(prepend_zero $(($duration / 60)))
+# seconds2=$(prepend_zero $(($duration % 60)))
+# echo -n "$artist - $song [$minutes1:$seconds1/$minutes2:$seconds2]"
+#else
+# echo
+#fi
diff --git a/linux/home/.config/polybar/scripts/get_spotify_status.sh b/linux/home/.config/polybar/scripts/get_spotify_status.sh
new file mode 100755
index 0000000..f04400d
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/get_spotify_status.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+# The name of polybar bar which houses the main spotify module and the control modules.
+PARENT_BAR="now-playing"
+PARENT_BAR_PID=$(pgrep -a "polybar" | grep "$PARENT_BAR" | cut -d" " -f1)
+
+# Set the source audio player here.
+# Players supporting the MPRIS spec are supported.
+# Examples: spotify, vlc, chrome, mpv and others.
+# Use `playerctld` to always detect the latest player.
+# See more here: https://github.com/altdesktop/playerctl/#selecting-players-to-control
+#PLAYER="spotify"
+PLAYER="playerctld"
+
+# Format of the information displayed
+# Eg. {{ artist }} - {{ album }} - {{ title }}
+# See more attributes here: https://github.com/altdesktop/playerctl/#printing-properties-and-metadata
+FORMAT="{{ title }} - {{ artist }}"
+
+# Sends $2 as message to all polybar PIDs that are part of $1
+update_hooks() {
+ while IFS= read -r id
+ do
+ polybar-msg -p "$id" hook spotify-play-pause $2 1>/dev/null 2>&1
+ done < <(echo "$1")
+}
+
+PLAYERCTL_STATUS=$(playerctl --player=$PLAYER status 2>/dev/null)
+EXIT_CODE=$?
+
+if [ $EXIT_CODE -eq 0 ]; then
+ STATUS=$PLAYERCTL_STATUS
+else
+ STATUS="No player is running"
+fi
+
+if [ "$1" == "--status" ]; then
+ echo "$STATUS"
+else
+ if [ "$STATUS" = "Stopped" ]; then
+ echo "No music is playing"
+ elif [ "$STATUS" = "Paused" ]; then
+ update_hooks "$PARENT_BAR_PID" 2
+ playerctl --player=$PLAYER metadata --format "$FORMAT"
+ elif [ "$STATUS" = "No player is running" ]; then
+ echo ""
+ else
+ update_hooks "$PARENT_BAR_PID" 1
+ playerctl --player=$PLAYER metadata --format "$FORMAT"
+ fi
+fi
+
diff --git a/linux/home/.config/polybar/scripts/menu.sh b/linux/home/.config/polybar/scripts/menu.sh
new file mode 100755
index 0000000..cd95ee0
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/menu.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# Specify the path to the Rofi configuration file
+config_file="$HOME/.config/rofi/styles/appmenu.rasi"
+
+rofi -no-lazy-grab -show drun -display-drun "Applications " -drun-display-format "{name}" -sep -config "$config_file"
diff --git a/linux/home/.config/polybar/scripts/menu.shsave b/linux/home/.config/polybar/scripts/menu.shsave
new file mode 100755
index 0000000..ea5bf8e
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/menu.shsave
@@ -0,0 +1,63 @@
+#!/bin/bash
+
+# Custom Rofi Script
+
+BORDER="#1F1F1F"
+SEPARATOR="#1F1F1F"
+FOREGROUND="#A9ABB0"
+BACKGROUND="#1F1F1F"
+BACKGROUND_ALT="#252525"
+HIGHLIGHT_BACKGROUND="#FF6F00"
+HIGHLIGHT_FOREGROUND="#FFFFFF"
+
+BLACK="#000000"
+WHITE="#ffffff"
+RED="#e53935"
+GREEN="#43a047"
+YELLOW="#fdd835"
+BLUE="#1e88e5"
+MAGENTA="#00897b"
+CYAN="#00acc1"
+PINK="#d81b60"
+PURPLE="#8e24aa"
+INDIGO="#3949ab"
+TEAL="#00897b"
+LIME="#c0ca33"
+AMBER="#ffb300"
+ORANGE="#fb8c00"
+BROWN="#6d4c41"
+GREY="#757575"
+BLUE_GREY="#546e7a"
+DEEP_PURPLE="#5e35b1"
+DEEP_ORANGE="#f4511e"
+LIGHT_BLUE="#039be5"
+LIGHT_GREEN="#7cb342"
+
+# Launch Rofi
+rofi -no-lazy-grab -show drun -display-drun "Applications " -drun-display-format "{name}" -hide-scrollbar false \
+-hide-sidebar-mode true \
+-bw 0 \
+-lines 15 \
+-line-padding 10 \
+-padding 0 \
+-width 20 \
+-xoffset 7 -yoffset 28 \
+-location 1 \
+-columns 1 \
+-show-icons -icon-theme "Papirus" \
+-color-enabled true \
+-color-window "$BACKGROUND,$BORDER,$SEPARATOR" \
+-color-normal "$BACKGROUND_ALT,$FOREGROUND,$BACKGROUND_ALT,$HIGHLIGHT_BACKGROUND,$HIGHLIGHT_FOREGROUND" \
+-color-active "$BACKGROUND,$MAGENTA,$BACKGROUND_ALT,$HIGHLIGHT_BACKGROUND,$HIGHLIGHT_FOREGROUND" \
+-color-urgent "$BACKGROUND,$YELLOW,$BACKGROUND_ALT,$HIGHLIGHT_BACKGROUND,$HIGHLIGHT_FOREGROUND"
+#rofi -no-lazy-grab -show drun -display-drun "Applications " -drun-display-format "{name}" -hide-scrollbar true -sidebar-mode false -bw 0 -lines 6 -line-padding 10 -padding 20 -width 30 -xoffset 7 -yoffset 25 -location 1 -columns 2 -show-icons -icon-theme "Papirus"
+
+# More Options
+# -fullscreen \
+
+# Theming help
+# color window = background, border, separator
+# color normal = background, foreground, background-alt, highlight-background, highlight-foreground
+# color active = background, foreground, background-alt, highlight-background, highlight-foreground
+# color urgent = background, foreground, background-alt, highlight-background, highlight-foreground
+
diff --git a/linux/home/.config/polybar/scripts/menu_full.sh b/linux/home/.config/polybar/scripts/menu_full.sh
new file mode 100755
index 0000000..9f898a4
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/menu_full.sh
@@ -0,0 +1,65 @@
+
+#!/bin/bash
+
+# Custom Rofi Script
+
+BORDER="#1F1F1F"
+SEPARATOR="#1F1F1F"
+FOREGROUND="#A9ABB0"
+BACKGROUND="#1F1F1F"
+BACKGROUND_ALT="#252525"
+HIGHLIGHT_BACKGROUND="#FF6F00"
+HIGHLIGHT_FOREGROUND="#FFFFFF"
+
+BLACK="#000000"
+WHITE="#ffffff"
+RED="#e53935"
+GREEN="#43a047"
+YELLOW="#fdd835"
+BLUE="#1e88e5"
+MAGENTA="#00897b"
+CYAN="#00acc1"
+PINK="#d81b60"
+PURPLE="#8e24aa"
+INDIGO="#3949ab"
+TEAL="#00897b"
+LIME="#c0ca33"
+AMBER="#ffb300"
+ORANGE="#fb8c00"
+BROWN="#6d4c41"
+GREY="#757575"
+BLUE_GREY="#546e7a"
+DEEP_PURPLE="#5e35b1"
+DEEP_ORANGE="#f4511e"
+LIGHT_BLUE="#039be5"
+LIGHT_GREEN="#7cb342"
+
+# Launch Rofi
+rofi -no-lazy-grab -show drun \
+-display-drun "Applications " -drun-display-format "{name}" \
+-hide-scrollbar true \
+-bw 0 \
+-lines 10 \
+-line-padding 15 \
+-padding 60 \
+-width 30 \
+-xoffset 10 -yoffset 40 \
+-location 1 \
+-fullscreen \
+-columns 4 \
+-show-icons -icon-theme "Papirus" \
+-font "Fantasque Sans Mono 10" \
+-color-enabled true \
+-color-window "$BACKGROUND,$BORDER,$SEPARATOR" \
+-color-normal "$BACKGROUND_ALT,$FOREGROUND,$BACKGROUND_ALT,$HIGHLIGHT_BACKGROUND,$HIGHLIGHT_FOREGROUND" \
+-color-active "$BACKGROUND,$MAGENTA,$BACKGROUND_ALT,$HIGHLIGHT_BACKGROUND,$HIGHLIGHT_FOREGROUND" \
+-color-urgent "$BACKGROUND,$YELLOW,$BACKGROUND_ALT,$HIGHLIGHT_BACKGROUND,$HIGHLIGHT_FOREGROUND"
+
+# More Options
+# -fullscreen \
+
+# Theming help
+# color window = background, border, separator
+# color normal = background, foreground, background-alt, highlight-background, highlight-foreground
+# color active = background, foreground, background-alt, highlight-background, highlight-foreground
+# color urgent = background, foreground, background-alt, highlight-background, highlight-foreground
diff --git a/linux/home/.config/polybar/scripts/now-playing.sh b/linux/home/.config/polybar/scripts/now-playing.sh
new file mode 100755
index 0000000..8fa6000
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/now-playing.sh
@@ -0,0 +1,217 @@
+#!/usr/bin/env python3
+import dbus
+import signal
+import time
+from unicodedata import east_asian_width
+
+# Config options
+
+# (int) : Length of media info string. If length of string exceedes this value, the text will scroll. Default value is 20
+message_display_len = 20
+
+# (int) : Font index of polybar. this value should be 1 more than the font value specified in polybar config.
+font_index = 1
+
+# (float) : Update speed of the text in seconds.
+update_delay = 0.3
+
+# (list) : list of chars containing previous, play, pause, next glyphs for media controls in respective order
+control_chars = ['','','','']
+
+# (dict) : dict of char icons to display as prefix.
+# If player name is available as key, then use the corressponding icon,
+# else default key value.
+# example:
+display_player_prefix = {
+ "spotify": ' ',
+ "firefox": ' ',
+ "default": ' '
+}
+
+# (list) : list of metadata fields based on mpris sepecification.
+# For more details/ field names, refer [mpris sepecification](https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata/)
+metadata_fields = ["xesam:title", "xesam:artist"]
+
+# (char) : separator for metadata fields
+metadata_separator = "-"
+
+# (bool) : Hide text when no player is available? True disables the output for no players.
+hide_output = False
+
+# Defult initialization
+current_player = None
+player_names = None
+players = None
+message = None
+display_text = ""
+display_prefix = " "
+display_suffix = ""
+last_player_name = None
+
+session_bus = dbus.SessionBus()
+
+def get_name(player_name ):
+ if player_name not in player_names:
+ return
+ name = ".".join(player_name.split(".")[3:])
+ return name
+
+def get_name_by_index(index):
+ if index >= len(player_names):
+ return
+ return get_name(player_names[index])
+
+def get_status(player):
+ status = ""
+ try:
+ status = player.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus', dbus_interface='org.freedesktop.DBus.Properties')
+ except Exception as e:
+ pass
+ return status
+
+def get_metadata(player):
+ metadata = {}
+ try:
+ metadata = player.Get('org.mpris.MediaPlayer2.Player', 'Metadata', dbus_interface='org.freedesktop.DBus.Properties')
+ except Exception as e:
+ pass
+ return metadata
+
+def update_prefix_suffix(player_name="", status=""):
+ global display_prefix, display_suffix, status_paused
+
+ player_option = ""
+ if player_name != "":
+ player_option = "-p " + player_name
+
+ prev_button = "%%{A:playerctl %s previous :}%c%%{A}" %(player_option,control_chars[0])
+ play_button = "%%{A:playerctl %s play :}%c%%{A}" %(player_option,control_chars[1])
+ pause_button = "%%{A:playerctl %s pause :}%c%%{A}" %(player_option,control_chars[2])
+ next_button = "%%{A:playerctl %s next :}%c%%{A}" %(player_option,control_chars[3])
+
+ suffix = "| " + prev_button
+ if status == "Playing":
+ suffix += " "+pause_button
+ status_paused = False
+ else:
+ suffix += " "+play_button
+ status_paused = True
+ suffix += " "+next_button
+ # print(suffix)
+ display_suffix = suffix
+
+ for key in display_player_prefix.keys():
+ if key in player_name:
+ display_prefix = display_player_prefix[key]
+ break
+ else:
+ display_prefix = display_player_prefix["default"]
+
+def update_players():
+ global player_names, players, session_bus, current_player, last_player_name
+ player_names = [service for service in session_bus.list_names() if service.startswith('org.mpris.MediaPlayer2.')]
+ players = [session_bus.get_object(service, '/org/mpris/MediaPlayer2') for service in player_names]
+ if last_player_name != get_name(current_player):
+ for index, player in enumerate(player_names):
+ if get_name(player) == last_player_name:
+ current_player = index
+
+def handle_event(*args):
+ global current_player, players, last_player_name
+ update_players()
+ if len(players) == 0:
+ return
+ current_player += 1
+ current_player %= len(players)
+ last_player_name = getname_by_index(current_player)
+# print("SIGUSR1: updated values - current_player = %d players len = %d"%(current_player,len(players)))
+
+def update_message():
+ global players, current_player,player_names, message, display_text, message_display_len, display_suffix, last_player_name
+ if len(players) == 0:
+ tmp_message = "No player available"
+ update_prefix_suffix()
+ else:
+ name = get_name_by_index(current_player)
+ status = get_status(players[current_player])
+ metadata_obj = get_metadata(players[current_player])
+ metadata_string_list = []
+ for field in metadata_fields:
+ result = metadata_obj.get(field)
+ if type(result) == dbus.Array:
+ result = result[0]
+ if not result:
+ result = "No "+field.split(":")[1]
+ metadata_string_list.append(str(result))
+ metadata_string = (" "+metadata_separator+" ").join(metadata_string_list)
+ if visual_len(metadata_string) > message_display_len:
+ metadata_string = " " + metadata_string + " |"
+ update_prefix_suffix(name,status)
+ tmp_message = ""
+ if metadata_string:
+ tmp_message += str(metadata_string)
+ last_player_name = name
+ if message != tmp_message:
+ message = tmp_message
+ display_text = message
+
+def scroll():
+ global display_text, message_display_len, status_paused
+ if not status_paused:
+ if len(display_text) > message_display_len:
+ display_text = display_text[1:] + display_text[0]
+ elif len(display_text) < message_display_len:
+ display_text += " "*(message_display_len - len(display_text))
+
+def visual_len(text):
+ visual_length = 0
+ for ch in text:
+ width = east_asian_width(ch)
+ if width == 'W' or width == 'F':
+ visual_length += 1
+ visual_length += 1
+ return visual_length
+
+def make_visual_len(text, visual_desired_length):
+ visual_length = 0
+ altered_text = ''
+ for char in text:
+ if visual_length < visual_desired_length:
+ width = east_asian_width(char)
+ if width == 'W' or width == 'F':
+ visual_length += 2
+ else:
+ visual_length += 1
+ altered_text += char
+ else:
+ break
+ if visual_length == visual_desired_length + 1:
+ altered_text = altered_text[:-1] + ' '
+ elif visual_length < visual_desired_length:
+ altered_text += ' ' * (visual_desired_length - visual_length)
+ return altered_text
+
+def print_text():
+ global display_text, message_display_len, players, player_names, display_prefix, display_suffix
+ if hide_output and len(players)==0:
+ print("", flush = True)
+ return
+ scroll()
+ print(display_prefix + " " +
+ "%%{T%d}" % (font_index) +
+ make_visual_len(display_text, message_display_len) +
+ "%{T-}" + display_suffix, flush=True)
+
+def main():
+ global current_player, players
+ update_players()
+ current_player = 0
+ while True:
+ time.sleep(update_delay)
+ update_players()
+ update_message()
+ print_text()
+
+if __name__ == '__main__':
+ signal.signal(signal.SIGUSR1, handle_event)
+ main()
diff --git a/linux/home/.config/polybar/scripts/polybar_wrapper b/linux/home/.config/polybar/scripts/polybar_wrapper
new file mode 100755
index 0000000..901bb28
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/polybar_wrapper
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+DIR=$(dirname $(realpath $0))
+
+WINDOW_ID_CONKY=/tmp/conky_window_id
+WINDOW_ID_TOP=/tmp/polybar_top_window_id
+WINDOW_ID_EXPANDED=/tmp/polybar_expanded_window_id
+
+conky_launch() {
+ # Hacky X11 magic to make Conky appear above polybar
+ killall conky
+ # xdotool search can't find Conky's window but fortunately Conky outputs it
+ conky -c ~/.config/conky/config 2> /tmp/conky_out
+ # Extract the hex window id from Conky's output
+ HEX=$(awk '/drawing to created window/ {print $NF}' /tmp/conky_out | tr -d '()' | awk -Fx '{print $2}')
+ WIN_ID=$(( 16#$HEX )) # convert to decimal
+ xdotool windowunmap $WIN_ID
+ echo $WIN_ID > $WINDOW_ID_CONKY
+}
+
+polybar_launch() {
+ killall polybar
+
+ polybar top &
+ xdotool search --sync --pid $! > $WINDOW_ID_TOP
+
+ polybar expanded &
+ xdotool search --sync --pid $! > $WINDOW_ID_EXPANDED
+
+ bar_collapse
+}
+
+launch() {
+ # Temporarily disable conky until I update the config
+ # conky_launch
+ # sleep 0.2
+ polybar_launch
+}
+
+bar_expand() {
+ xdotool windowmap $(cat $WINDOW_ID_EXPANDED)
+ xdotool windowunmap $(cat $WINDOW_ID_TOP)
+}
+
+bar_collapse() {
+ xdotool windowunmap $(cat $WINDOW_ID_EXPANDED)
+ xdotool windowmap $(cat $WINDOW_ID_TOP)
+}
+
+rofi_open() {
+ options_close
+ bar_expand &
+ rofi -modi run -show run
+ bar_collapse
+}
+
+drun_open() {
+ bar_expand &
+ rofi -theme drun -modi drun -show drun -drun-categories Custom
+ bar_collapse
+}
+
+search_open() {
+ options_close
+ bar_expand &
+ rofi -theme window -modi window -show window
+ bar_collapse
+}
+
+options_open() {
+ bar_expand
+ $DIR/rofi_option_menu
+ bar_collapse
+ # echo "open" > /tmp/polybar_side_panel_state
+ # ID_CONKY=$(cat $WINDOW_ID_CONKY)
+ # xdotool windowmap $ID_CONKY
+ # xdotool windowraise $ID_CONKY
+ # ~/.config/i3/scripts/music_player show_applet
+}
+
+case "$1" in
+ rofi)
+ rofi_open;;
+ search)
+ search_open;;
+ drun)
+ drun_open;;
+ options)
+ options_open;;
+ launch)
+ launch;;
+esac
diff --git a/linux/home/.config/polybar/scripts/popup-calendar.sh b/linux/home/.config/polybar/scripts/popup-calendar.sh
new file mode 100755
index 0000000..4e5303c
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/popup-calendar.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+BAR_HEIGHT=22 # polybar height
+BORDER_SIZE=1 # border size from your wm settings
+YAD_WIDTH=222 # 222 is minimum possible value
+YAD_HEIGHT=193 # 193 is minimum possible value
+DATE="$(date +"%a %d %b, %H:%M")"
+
+case "$1" in
+--popup)
+ if [ "$(xdotool getwindowfocus getwindowname)" = "yad-calendar" ]; then
+ exit 0
+ fi
+
+ eval "$(xdotool getmouselocation --shell)"
+ eval "$(xdotool getdisplaygeometry --shell)"
+
+ # X
+ if [ "$((X + YAD_WIDTH / 2 + BORDER_SIZE))" -gt "$WIDTH" ]; then #Right side
+ : $((pos_x = WIDTH - YAD_WIDTH - BORDER_SIZE))
+ elif [ "$((X - YAD_WIDTH / 2 - BORDER_SIZE))" -lt 0 ]; then #Left side
+ : $((pos_x = BORDER_SIZE))
+ else #Center
+ : $((pos_x = X - YAD_WIDTH / 2))
+ fi
+
+ # Y
+ if [ "$Y" -gt "$((HEIGHT / 2))" ]; then #Bottom
+ : $((pos_y = HEIGHT - YAD_HEIGHT - BAR_HEIGHT - BORDER_SIZE))
+ else #Top
+ : $((pos_y = BAR_HEIGHT + BORDER_SIZE))
+ fi
+
+ yad --calendar --undecorated --fixed --close-on-unfocus --no-buttons \
+ --width="$YAD_WIDTH" --height="$YAD_HEIGHT" --posx="$pos_x" --posy="$pos_y" \
+ --title="yad-calendar" --borders=0 >/dev/null &
+ ;;
+*)
+ echo "$DATE"
+ ;;
+esac
diff --git a/linux/home/.config/polybar/scripts/rofi-power.sh b/linux/home/.config/polybar/scripts/rofi-power.sh
new file mode 100755
index 0000000..87ac92c
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/rofi-power.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env sh
+#
+# A rofi powered menu to execute power related action.
+# Uses: amixer mpc poweroff reboot rofi rofi-prompt
+
+power_off=''
+reboot=''
+lock=''
+suspend='鈴'
+log_out=''
+
+chosen=$(printf '%s;%s;%s;%s;%s\n' "$power_off" "$reboot" "$lock" "$suspend" \
+ "$log_out" \
+ | rofi -theme '~/.config/rofi/themes/power.rasi' \
+ -dmenu \
+ -sep ';' \
+ -selected-row 2)
+
+case "$chosen" in
+ "$power_off")
+ rofi-prompt --query 'Shutdown?' && poweroff
+ ;;
+
+ "$reboot")
+ rofi-prompt --query 'Reboot?' && reboot
+ ;;
+
+ "$lock")
+ # TODO Add your lockscreen command.
+ ;;
+
+ "$suspend")
+ # Pause music and mute volume before suspending.
+ mpc --quiet pause
+ amixer set Master mute
+ # TODO Add your suspend command.
+ ;;
+
+ "$log_out")
+ # TODO Add your log out command.
+ ;;
+
+ *) exit 1 ;;
+esac
+
diff --git a/linux/home/.config/polybar/scripts/scroll_spotify_status.sh b/linux/home/.config/polybar/scripts/scroll_spotify_status.sh
new file mode 100755
index 0000000..74e0bfd
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/scroll_spotify_status.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# see man zscroll for documentation of the following parameters
+zscroll -l 20 \
+ --delay 0.1 \
+ --scroll-padding " " \
+ --match-command "$HOME/.config/polybar/scripts/get_spotify_status.sh --status" \
+ --match-text "Playing" "--scroll 1" \
+ --match-text "Paused" "--scroll 0" \
+ --update-check true "$HOME/.config/polybar/scripts/get_spotify_status.sh" &
+
+wait
diff --git a/linux/home/.config/polybar/scripts/sysmenu.sh b/linux/home/.config/polybar/scripts/sysmenu.sh
new file mode 100755
index 0000000..a3a7a2a
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/sysmenu.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+# Display a power menu to: shutdown, reboot,
+# lock, logout, and suspend. This script can be
+# executed by clicking on the polybar powermenu module
+# or with a keyboard shortcut
+
+# Options to be displayed
+shutdown=" Shutdown"
+reboot=" Reboot"
+lock=" Lock"
+logout=" Logout"
+suspend=" Suspend"
+
+uptime=$(uptime -p | sed -e 's/up //g')
+
+# Options passed into variable
+options="$shutdown\n$reboot\n$lock\n$logout\n$suspend"
+
+# Specify the path to the Rofi configuration file
+config_file="$HOME/.config/rofi/styles/powermenu.rasi"
+
+# Show Rofi with the specified configuration file
+chosen="$(echo -e "$options" | rofi -no-lazy-grab -sep -config "$config_file" -dmenu -p 'System ' "$uptime")"
+
+case $chosen in
+ $shutdown)
+ shutdown now
+ ;;
+ $reboot)
+ systemctl reboot
+ ;;
+ $lock)
+ betterlockscreen --lock dimblur
+ ;;
+ $logout)
+ bspc quit
+ ;;
+ $suspend)
+ systemctl suspend
+ ;;
+esac
diff --git a/linux/home/.config/polybar/scripts/sysmenu.shsave b/linux/home/.config/polybar/scripts/sysmenu.shsave
new file mode 100755
index 0000000..00ce125
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/sysmenu.shsave
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# display a power menu to: shutdown, reboot,
+# lock, logout, and suspend. This script can be
+# executed by clicking on the polybar powermenu module
+# or with a keyboard shortcut
+
+
+# options to be displayed
+shutdown=" Shutdown"
+reboot=" Reboot"
+lock=" Lock"
+logout=" Logout"
+suspend=" Suspend"
+
+uptime=$(uptime -p | sed -e 's/up //g')
+
+# options passed into variable
+options="$shutdown\n$reboot\n$lock\n$logout\n$suspend"
+
+chosen="$(echo -e "$options" | rofi -no-lazy-grab -sep -lines 5 -hide-scrollbar true -border 0 -padding 0 -height 2px -width 15 -xoffset -10 -yoffset 28 -location 3 -columns 1 -dmenu -p 'System ' "$uptime")"
+
+case $chosen in
+$shutdown)
+ systemctl poweroff
+ ;;
+$reboot)
+ systemctl reboot
+ ;;
+$lock)
+ betterlockscreen --lock dimblur
+ ;;
+$logout)
+ bspc quit
+ ;;
+$suspend)
+ systemctl suspend
+ ;;
+esac
+
diff --git a/linux/home/.config/polybar/scripts/system-usb-mount.sh b/linux/home/.config/polybar/scripts/system-usb-mount.sh
new file mode 100755
index 0000000..63e9187
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/system-usb-mount.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+devices=$(lsblk -Jplno NAME,TYPE,RM,SIZE,MOUNTPOINT,VENDOR)
+
+case "$1" in
+ --mount)
+ for mount in $(echo "$devices" | jq -r '.blockdevices[] | select(.type == "part") | select(.rm == true) | select(.mountpoint == null) | .name'); do
+ udisksctl mount --no-user-interaction -b "$mount"
+
+ mountpoint=$(udisksctl mount --no-user-interaction -b $mount)
+ mountpoint=$(echo $mountpoint | cut -d " " -f 4- | tr -d ".")
+ kitty -e "bash -lc 'pcmanfm $mountpoint'" &
+ done
+ ;;
+ --unmount)
+ for unmount in $(echo "$devices" | jq -r '.blockdevices[] | select(.type == "part") | select(.rm == true) | select(.mountpoint != null) | .name'); do
+ udisksctl unmount --no-user-interaction -b "$unmount"
+ udisksctl power-off --no-user-interaction -b "$unmount"
+ done
+ ;;
+ *)
+ output=""
+ counter=0
+
+ for unmounted in $(echo "$devices" | jq -r '.blockdevices[] | select(.type == "part") | select(.rm == true) | select(.mountpoint == null) | .name'); do
+ unmounted=$(echo "$unmounted" | tr -d "[:digit:]")
+ unmounted=$(echo "$devices" | jq -r '.blockdevices[] | select(.name == "'"$unmounted"'") | .vendor')
+ unmounted=$(echo "$unmounted" | tr -d ' ')
+
+ if [ $counter -eq 0 ]; then
+ space=""
+ else
+ space=" "
+ fi
+ counter=$((counter + 1))
+
+ output="$output$space#1 $unmounted"
+ done
+
+ for mounted in $(echo "$devices" | jq -r '.blockdevices[] | select(.type == "part") | select(.rm == true) | select(.mountpoint != null) | .size'); do
+ if [ $counter -eq 0 ]; then
+ space=""
+ else
+ space=" "
+ fi
+ counter=$((counter + 1))
+
+ output="$output$space#2 $mounted"
+ done
+
+ echo "$output"
+ ;;
+esac
diff --git a/linux/home/.config/polybar/scripts/temperature.sh b/linux/home/.config/polybar/scripts/temperature.sh
new file mode 100755
index 0000000..7ef6abb
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/temperature.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Pulls CPU temps, averages them, and outputs them
+
+count=0
+sum=0.0
+
+# Iterate over each temperature reading
+for temp in "$(sensors | grep "^Core" | grep -e '+.*C' | cut -f 2 -d '+' | cut -f 1 -d ' ' | sed 's/°C//')"; do
+ sum=$(echo "$sum + $temp" | bc)
+ ((count++))
+done
+
+# Calculate the average
+avg=$(echo "scale=0; $sum / $count" | bc)
+
+# Output the average temperature without decimal points
+echo " ${avg%.*}°C"
diff --git a/linux/home/.config/polybar/scripts/toggle_bluetooth.sh b/linux/home/.config/polybar/scripts/toggle_bluetooth.sh
new file mode 100755
index 0000000..899d5ec
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/toggle_bluetooth.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+if [ $(bluetoothctl show | grep "Powered: yes" | wc -c) -eq 0 ]
+then
+ bluetoothctl power on
+else
+ bluetoothctl power off
+fi
+
diff --git a/linux/home/.config/polybar/scripts/vpn.sh b/linux/home/.config/polybar/scripts/vpn.sh
new file mode 100755
index 0000000..ab1eb9d
--- /dev/null
+++ b/linux/home/.config/polybar/scripts/vpn.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# Set a default message
+default_message=" vpn"
+
+# Check if Protonvpn service is running
+if pgrep -x "openvpn" >/dev/null; then
+ # If Protonvpn service is running, get the country
+ country=$(protonvpn s | grep Country)
+ # Extract the connection ID
+ connection=$(pgrep -a openvpn$ | head -n 1 | awk '{print $NF }' | cut -d '.' -f 1)
+ # Output vpn status with the country if connected
+ echo " vpn" #"$country"
+else
+ # If Protonvpn service is not running, output default message
+ echo "$default_message"
+fi
+
+#
+#proton_status=$(protonvpn s)
+#current_status=$(protonvpn s | wc -l)
+#current_server=$(protonvpn s | awk '/Server:/ {print "VPN "$2}')
+#
+#if [ "$current_status" -gt 2 ]; then
+# echo "$current_server"
+#else
+# echo "%{F#bf616a}NO VPN"
+#fi
diff --git a/linux/home/.config/pypoetry/config.toml b/linux/home/.config/pypoetry/config.toml
new file mode 100644
index 0000000..53b35d3
--- /dev/null
+++ b/linux/home/.config/pypoetry/config.toml
@@ -0,0 +1,3 @@
+[virtualenvs]
+create = true
+in-project = true
diff --git a/linux/home/.config/rofi/Notif.rasi b/linux/home/.config/rofi/Notif.rasi
new file mode 100644
index 0000000..47bc1af
--- /dev/null
+++ b/linux/home/.config/rofi/Notif.rasi
@@ -0,0 +1,153 @@
+configuration {
+ display-drun: " Apps";
+ display-window: "缾 Windows";
+ show-icons:true;
+ font: "Fira Code 10";
+}
+
+* {
+ background-color: #fffff7;
+ bg: #fffff7;
+ text-color: #927f70;
+ selbg: #927f70;
+ actbg: #eee8da;
+ urgbg: #eee8da;
+ winbg: #eee8da;
+
+ selected-normal-foreground: @text-color;
+ normal-foreground: @text-color;
+ selected-normal-background: @actbg;
+ normal-background: @background-color;
+
+ selected-urgent-foreground: @urgbg;
+ urgent-foreground: @text-color;
+ selected-urgent-background: @actbg;
+ urgent-background: @background-color;
+ urgent-foreground: @urgbg;
+
+ selected-active-foreground: @selbg;
+ active-foreground: @selbg;
+ selected-active-background: @actbg;
+ active-background: @background-color;
+
+ line-margin: 2;
+ line-padding: 2;
+ separator-style: "none";
+ hide-scrollbar: "true";
+ margin: 0;
+ padding: 5;
+}
+
+window {
+ location: northeast;
+ anchor: northeast;
+ //height: 40%;
+ y-offset: 60px;
+ x-offset: -20px;
+ width: 16%;
+ orientation: horizontal;
+ children: [mainbox];
+ border: 2px solid;
+ border-radius: 10px;
+ border-color: #eee8da;
+}
+
+mainbox {
+ spacing: 0.8em;
+ orientation: vertical;
+ children: [ inputbar, listview ];
+}
+
+button { padding: 2px 2px; }
+
+button selected {
+ background-color: @active-background;
+ text-color: @background-color;
+}
+
+inputbar {
+ padding: 2px;
+ spacing: 5px;
+}
+
+listview {
+ spacing: 0.5em;
+ dynamic: true;
+ cycle: false;
+}
+
+element {
+ padding: 10px;
+}
+
+prompt {
+ padding: 10px 0px 0px 20px;
+ font: "Fira Code 10";
+}
+
+entry {
+ expand: true;
+ text-color: @normal-foreground;
+ vertical-align: 0;
+ padding: 5px 0px 0px 20px;
+ enabled: false;
+}
+
+element normal.normal {
+ background-color: @bg;
+ border-radius: 8px;
+ text-color: @normal-foreground;
+}
+
+element normal.urgent {
+ background-color: @bg;
+ border-radius: 8px;
+ text-color: @urgent-foreground;
+}
+
+element normal.active {
+ background-color: @bg;
+ border-radius: 8px;
+ text-color: @active-foreground;
+}
+
+element selected.normal {
+ background-color: @selected-normal-background;
+ text-color: @selected-normal-foreground;
+}
+
+element selected.urgent {
+ background-color: @selected-urgent-background;
+ text-color: @selected-urgent-foreground;
+}
+
+element selected.active {
+ background-color: @selected-active-background;
+ text-color: @selected-active-foreground;
+}
+
+element alternate.normal {
+ background-color: @bg;
+ border-radius: 8px;
+ text-color: @normal-foreground;
+}
+
+element alternate.urgent {
+ background-color: @bg;
+ border-radius: 8px;
+ text-color: @urgent-foreground;
+}
+
+element alternate.active {
+ background-color: @bg;
+ border-radius: 8px;
+ text-color: @active-foreground;
+}
+element-icon {
+ size: 7ch;
+}
+element.selected {
+ border-radius: 8px;
+ border: 0 0 0 5px solid;
+ border-color: @winbg;
+}
diff --git a/linux/home/.config/rofi/colors/gruvbox.rasi b/linux/home/.config/rofi/colors/gruvbox.rasi
new file mode 100644
index 0000000..f5c9169
--- /dev/null
+++ b/linux/home/.config/rofi/colors/gruvbox.rasi
@@ -0,0 +1,10 @@
+/* colors */
+
+* {
+ al: #00000000;
+ bg: #32302f;
+ pg: #7c6f64;
+ se: #101010ff;
+ fg: #FFFFFFff;
+ ac: #fe8019;
+}
diff --git a/linux/home/.config/rofi/colors/nord.rasi b/linux/home/.config/rofi/colors/nord.rasi
new file mode 100644
index 0000000..2e72da1
--- /dev/null
+++ b/linux/home/.config/rofi/colors/nord.rasi
@@ -0,0 +1,10 @@
+/* colors */
+
+* {
+ al: #00000000;
+ bg: #2e3440;
+ pg: #4c566a;
+ se: #101010ff;
+ fg: #FFFFFFff;
+ ac: #88c0d0;
+}
diff --git a/linux/home/.config/rofi/colors/simple.rasi b/linux/home/.config/rofi/colors/simple.rasi
new file mode 100644
index 0000000..31f260e
--- /dev/null
+++ b/linux/home/.config/rofi/colors/simple.rasi
@@ -0,0 +1,10 @@
+/* colors */
+
+* {
+ al: #00000000;
+ bg: #2c2f31;
+ pg: #666666;
+ se: #6cb6eb;
+ fg: #FFFFFFff;
+ ac: #3b4041;
+}
diff --git a/linux/home/.config/rofi/config.rasi b/linux/home/.config/rofi/config.rasi
new file mode 100644
index 0000000..dfe7afd
--- /dev/null
+++ b/linux/home/.config/rofi/config.rasi
@@ -0,0 +1,178 @@
+configuration {
+ bw: 0;
+ columns: 1;
+ location: 0;
+ lines: 5;
+ padding: 0;
+ fixed-num-lines: true;
+ show-icons: true;
+ sidebar-mode: false;
+ separator-style: "beam";
+ hide-scrollbar: false;
+ scroll-method: 0;
+ click-to-exit: true;
+ show-match: true;
+ combi-hide-mode-prefix: false;
+ display-combi: "Combi";
+ display-drun: "Start";
+ display-window: "Window";
+ display-windowcd: "Windowcd";
+ display-run: "Commands";
+ display-ssh: "Ssh";
+ modi: "drun,window,run,ssh,";
+ opacity: "0";
+ fake-transparency: false;
+ kb-row-up: "Up,Control+k,Shift+Tab,Shift+ISO_Left_Tab";
+ kb-row-down: "Down,Control+j";
+ kb-accept-entry: "Control+m,Return,KP_Enter";
+ me-select-entry: "";
+ me-accept-entry: "MousePrimary";
+ terminal: "kitty";
+ kb-remove-to-eol: "Control+Shift+e";
+ kb-mode-next: "Shift+Right,Control+Tab,Control+l";
+ kb-mode-previous: "Shift+Left,Control+Shift+Tab,Control+h";
+ kb-remove-char-back: "BackSpace";
+ kb-mode-complete: "Control+c";
+}
+
+* {
+ background-color: @background;
+ font: "Source Code Pro Semibold 9";
+ spacing: 2;
+}
+
+#window {
+ background-color: @background;
+ border: 3;
+ border-color: @cyber;
+ padding: 0.5ch;
+}
+
+#mainbox {
+ border: 0;
+ border-color: @ac;
+ padding: 0;
+}
+
+#message {
+ border: 0px 0px 0px;
+ border-color: @ac;
+ padding: 1px;
+}
+
+#textbox {
+ text-color: @foreground;
+}
+
+#inputbar {
+ children: [ prompt,textbox-prompt-colon,entry,case-indicator ];
+}
+
+#textbox-prompt-colon {
+ expand: false;
+ str: ":";
+ margin: 0px 0.3em 0em 0em;
+ text-color: inherit;
+}
+
+#listview {
+ fixed-height: 0;
+ border: 0px 0px 0px;
+ border-color: @ac;
+ spacing: 2px;
+ scrollbar: true;
+ padding: 2px 0px 0px;
+}
+
+#element {
+ border: 0;
+ padding: 1px;
+}
+
+#element.normal.normal {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#element.normal.urgent {
+ background-color: @background;
+ text-color: @red;
+}
+
+#element.normal.active {
+ background-color: @foreground;
+ text-color: @background;
+}
+
+#element.selected.normal {
+ background-color: @foreground;
+ text-color: @black;
+}
+
+#element.selected.urgent {
+ background-color: @foreground;
+ text-color: @black;
+}
+
+#element.selected.active {
+ background-color: @foreground;
+ text-color: @black;
+}
+
+#element.alternate.normal {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#element.alternate.urgent {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#element.alternate.active {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#scrollbar {
+ width: 0px;
+ border: 0;
+ handle-width: 0px;
+ padding: 0;
+}
+
+#sidebar {
+ border: 2px 0px 0px;
+ border-color: @ac;
+}
+
+#button {
+ text-color: @background;
+}
+
+#button.selected {
+ background-color: @ac;
+ text-color: @foreground;
+}
+
+#inputbar {
+ spacing: 0;
+ text-color: @foreground;
+ padding: 1px;
+}
+
+#case-indicator {
+ spacing: 0;
+ text-color: @foreground;
+}
+
+#entry {
+ spacing: 0;
+ text-color: @foreground;
+}
+
+#prompt {
+ spacing: 0;
+ text-color: @foreground;
+}
+@import "~/.config/rofi/themes/colors.rasi"
diff --git a/linux/home/.config/rofi/options_menu.rasi b/linux/home/.config/rofi/options_menu.rasi
new file mode 100644
index 0000000..173da88
--- /dev/null
+++ b/linux/home/.config/rofi/options_menu.rasi
@@ -0,0 +1,71 @@
+configuration {
+ show-icons: false;
+ font: "Font Awesome 17";
+}
+
+window {
+ height: 300;
+ width: 300;
+ location: northeast;
+ anchor: northeast;
+ x-offset: -10;
+ y-offset: 10;
+ transparency: "real";
+ background-color: #00000000;
+ border: 4px 4px solid 4px 4px;
+ border-radius: 8;
+ border-color: @primary;
+}
+
+mainbox {
+ children: [message, listview];
+ padding: 0px 0px;
+}
+
+
+textbox {
+ horizontal-align: 0.5;
+ border: 0px 0px 2px;
+ border-color: @primary;
+ margin: 12px;
+ padding: 12px;
+}
+
+listview {
+ padding: 8px 32px;
+}
+
+element {
+ padding: 8px;
+ orientation: horizontal;
+}
+
+element normal.urgent, element alternate.urgent {
+ background-color: @urgent;
+}
+
+element normal.active, element alternate.active {
+ background-color: @background-alt;
+ border: 4px 4px solid 4px 4px;
+ border-radius: 8;
+ border-color: transparent;
+}
+
+element selected {
+ border: 4px 4px solid 4px 4px;
+ border-radius: 8;
+ border-color: @primary;
+}
+
+element selected.urgent {
+ background-color: @urgent;
+}
+
+element selected.active {
+ background-color: @background-alt;
+}
+
+element-text {
+ horizontal-align: 0;
+ padding: 0px 8px;
+}
diff --git a/linux/home/.config/rofi/rofi-network-manager.conf b/linux/home/.config/rofi/rofi-network-manager.conf
new file mode 100644
index 0000000..e182dc1
--- /dev/null
+++ b/linux/home/.config/rofi/rofi-network-manager.conf
@@ -0,0 +1,41 @@
+# Location
+# +---------- +
+# | 1 | 2 | 3 |
+# | 8 | 0 | 4 |
+# | 7 | 6 | 5 |
+# +-----------+
+#The grid represents the screen with the numbers indicating the location of the window.
+#If you want the window to be in the upper right corner, set location to 3.
+LOCATION=3
+#This sets the anchor point for the window displaying the QR code.
+QRCODE_LOCATION=$LOCATION
+#X, Y Offset
+#This sets the distance of the window from the edge of the screen on the X and Y axis.
+Y_AXIS=35
+X_AXIS=-10
+#X_AXIS=-315
+#Use notifications or not
+#Values can be "true" or "false"
+NOTIFICATIONS="false"
+#Location of qrcode wifi image
+QRCODE_DIR="/tmp/"
+#WIDTH_FIX_MAIN/WIDTH_FIX_STATUS
+#These values can be adjusted if the text doesn't fit or
+#if there is too much space at the end when you launch the script.
+#It will depend on the font type and size.
+WIDTH_FIX_MAIN=1
+WIDTH_FIX_STATUS=10
+#Values can be "true" or "false"
+#Set it to true, if the script outputs the signal strength with asterisks
+#and you want bars.
+ASCII_OUT=false
+#Values can be "true" or "false"
+#Set it to true if you want to use custom icons
+#for the signal strength instead of the default ones.
+CHANGE_BARS=false
+#Custom signal strength indicators
+SIGNAL_STRENGTH_0="0"
+SIGNAL_STRENGTH_1="1"
+SIGNAL_STRENGTH_2="12"
+SIGNAL_STRENGTH_3="123"
+SIGNAL_STRENGTH_4="1234"
diff --git a/linux/home/.config/rofi/rofi-network-manager.rasi b/linux/home/.config/rofi/rofi-network-manager.rasi
new file mode 100644
index 0000000..7e2da9b
--- /dev/null
+++ b/linux/home/.config/rofi/rofi-network-manager.rasi
@@ -0,0 +1,127 @@
+configuration {
+ show-icons: false;
+ sidebar-mode: false;
+ hover-select: true;
+ me-select-entry: "";
+ me-accept-entry: [MousePrimary];
+}
+*{
+ font: "DejaVu Sans Mono 9"; //Font
+ //Colors
+ foreground:#f8f8f2; //Text
+ background:#0A1229; //Background
+ accent:#00BCD4; //Highlight
+ foreground-selection:@foreground; //Selection_fg
+ background-selection:#e34039; //Selection_bg
+
+ transparent: #ffffff00;
+ background-color: @transparent;
+ text-color: @foreground;
+ selected-normal-foreground: @foreground-selection;
+ normal-foreground: @foreground;
+ alternate-normal-background: @transparent;
+ selected-urgent-foreground: @foreground;
+ urgent-foreground: @foreground;
+ alternate-urgent-background: @background;
+ active-foreground: @accent;
+ selected-active-foreground: @background-selection;
+ alternate-normal-foreground: @foreground;
+ alternate-active-background: @background;
+ bordercolor: @background;
+ normal-background: @transparent;
+ selected-normal-background: @background-selection;
+ separatorcolor: @accent;
+ urgent-background: @accent;
+ alternate-urgent-foreground: @foreground;
+ selected-urgent-background: @accent;
+ alternate-active-foreground: @foreground;
+ selected-active-background: @transparent;
+ active-background: @transparent;
+}
+window {
+ text-color: @foreground;
+ background-color: @background;
+ border-radius: 6px;
+ padding: 10;
+}
+mainbox {
+ border: 0;
+ padding: 0;
+}
+textbox {
+ text-color: @foreground;
+}
+listview {
+ spacing: 4px;
+ dynamic: true;
+ fixed-height: false;
+ border: 0;
+ scrollbar: false;
+ text-color: @separatorcolor;
+}
+element {
+ border: 0;
+ padding: 0;
+ border-radius: 4px;
+}
+element-text {
+ background-color: inherit;
+ text-color: inherit;
+}
+element.normal.normal {
+ text-color: @normal-foreground;
+ background-color: @normal-background;
+}
+element.normal.urgent {
+ text-color: @urgent-foreground;
+ background-color: @urgent-background;
+}
+element.normal.active {
+ text-color: @active-foreground;
+ background-color: @active-background;
+}
+element.selected.normal {
+ text-color: @selected-normal-foreground;
+ background-color: @selected-normal-background;
+}
+element.selected.urgent {
+ text-color: @selected-urgent-foreground;
+ background-color: @selected-urgent-background;
+}
+element.selected.active {
+ text-color: @selected-active-foreground;
+ background-color: @selected-active-background;
+}
+element.alternate.normal {
+ text-color: @alternate-normal-foreground;
+ background-color: @alternate-normal-background;
+}
+element.alternate.urgent {
+ text-color: @alternate-urgent-foreground;
+ background-color: @alternate-urgent-background;
+}
+element.alternate.active {
+ text-color: @alternate-active-foreground;
+ background-color: @alternate-active-background;
+}
+mode-switcher {
+ border: 0;
+}
+button selected {
+ text-color: @selected-normal-foreground;
+ background-color: @selected-normal-background;
+}
+button normal {
+ text-color: @foreground;
+}
+inputbar {
+ children: [textbox-prompt-colon,entry];
+}
+textbox-prompt-colon{
+ expand: false;
+ margin: 0;
+ str: ":";
+}
+entry {
+ placeholder: "";
+}
diff --git a/linux/home/.config/rofi/styles/appmenu.rasi b/linux/home/.config/rofi/styles/appmenu.rasi
new file mode 100644
index 0000000..83445be
--- /dev/null
+++ b/linux/home/.config/rofi/styles/appmenu.rasi
@@ -0,0 +1,186 @@
+configuration {
+ bw: 0;
+ columns: 1;
+ location: 0;
+ lines: 15;
+ padding: 0;
+ fixed-num-lines: true;
+ show-icons: true;
+ sidebar-mode: false;
+ separator-style: "beam";
+ hide-scrollbar: false;
+ scroll-method: 0;
+ click-to-exit: true;
+ show-match: true;
+ combi-hide-mode-prefix: false;
+ display-combi: "Combi";
+ display-drun: "Start";
+ display-window: "Window";
+ display-windowcd: "Windowcd";
+ display-run: "Commands";
+ display-ssh: "Ssh";
+ modi: "drun,window,run,ssh,";
+ opacity: "0";
+ fake-transparency: false;
+ kb-row-up: "Up,Control+k,Shift+Tab,Shift+ISO_Left_Tab";
+ kb-row-down: "Down,Control+j";
+ kb-accept-entry: "Control+m,Return,KP_Enter";
+ me-select-entry: "";
+ me-accept-entry: "MousePrimary";
+ terminal: "kitty";
+ kb-remove-to-eol: "Control+Shift+e";
+ kb-mode-next: "Shift+Right,Control+Tab,Control+l";
+ kb-mode-previous: "Shift+Left,Control+Shift+Tab,Control+h";
+ kb-remove-char-back: "BackSpace";
+ kb-mode-complete: "Control+c";
+}
+
+* {
+ padding: 0;
+ hide-scrollbar: true;
+ border: 0;
+ width: 15%;
+ columns: 1;
+ background-color: @background;
+ font: "Source Code Pro Semibold 9";
+ spacing: 2;
+}
+
+#window {
+ background-color: @background;
+ border: 3;
+ border-color: @cyber;
+ padding: 0.5ch;
+ location: northwest;
+ margin: 28px 0 0 8px;
+}
+
+#mainbox {
+ border: 0;
+ border-color: @ac;
+ padding: 0;
+}
+
+#message {
+ border: 0px 0px 0px;
+ border-color: @ac;
+ padding: 1px;
+}
+
+#textbox {
+ text-color: @foreground;
+}
+
+#inputbar {
+ children: [ prompt, textbox-prompt-colon, entry, case-indicator ];
+}
+
+#textbox-prompt-colon {
+ expand: false;
+ str: ":";
+ margin: 0px 0.3em 0em 0em;
+ text-color: inherit;
+}
+
+#listview {
+ fixed-height: 0;
+ border: 0px 0px 0px;
+ border-color: @ac;
+ spacing: 2px;
+ scrollbar: true;
+ padding: 2px 0px 0px;
+}
+
+#element {
+ border: 0;
+ padding: 1px;
+}
+
+#element.normal.normal {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#element.normal.urgent {
+ background-color: @background;
+ text-color: @red;
+}
+
+#element.normal.active {
+ background-color: @foreground;
+ text-color: @background;
+}
+
+#element.selected.normal {
+ background-color: @foreground;
+ text-color: @black;
+}
+
+#element.selected.urgent {
+ background-color: @foreground;
+ text-color: @black;
+}
+
+#element.selected.active {
+ background-color: @foreground;
+ text-color: @black;
+}
+
+#element.alternate.normal {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#element.alternate.urgent {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#element.alternate.active {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#scrollbar {
+ width: 0px;
+ border: 0;
+ handle-width: 0px;
+ padding: 0;
+}
+
+#sidebar {
+ border: 2px 0px 0px;
+ border-color: @ac;
+}
+
+#button {
+ text-color: @background;
+}
+
+#button.selected {
+ background-color: @ac;
+ text-color: @foreground;
+}
+
+#inputbar {
+ spacing: 0;
+ text-color: @foreground;
+ padding: 1px;
+}
+
+#case-indicator {
+ spacing: 0;
+ text-color: @foreground;
+}
+
+#entry {
+ spacing: 0;
+ text-color: @foreground;
+}
+
+#prompt {
+ spacing: 0;
+ text-color: @foreground;
+}
+
+@import "~/.config/rofi/themes/colors.rasi"
diff --git a/linux/home/.config/rofi/styles/powermenu.rasi b/linux/home/.config/rofi/styles/powermenu.rasi
new file mode 100644
index 0000000..1b7219d
--- /dev/null
+++ b/linux/home/.config/rofi/styles/powermenu.rasi
@@ -0,0 +1,187 @@
+configuration {
+ bw: 0;
+ columns: 1;
+ location: 0;
+ lines: 5;
+ padding: 0;
+ fixed-num-lines: true;
+ show-icons: true;
+ sidebar-mode: false;
+ separator-style: "beam";
+ hide-scrollbar: false;
+ scroll-method: 0;
+ click-to-exit: true;
+ show-match: true;
+ combi-hide-mode-prefix: false;
+ display-combi: "Combi";
+ display-drun: "Start";
+ display-window: "Window";
+ display-windowcd: "Windowcd";
+ display-run: "Commands";
+ display-ssh: "Ssh";
+ modi: "drun,window,run,ssh,";
+ opacity: "0";
+ fake-transparency: false;
+ kb-row-up: "Up,Control+k,Shift+Tab,Shift+ISO_Left_Tab";
+ kb-row-down: "Down,Control+j";
+ kb-accept-entry: "Control+m,Return,KP_Enter";
+ me-select-entry: "";
+ me-accept-entry: "MousePrimary";
+ terminal: "kitty";
+ kb-remove-to-eol: "Control+Shift+e";
+ kb-mode-next: "Shift+Right,Control+Tab,Control+l";
+ kb-mode-previous: "Shift+Left,Control+Shift+Tab,Control+h";
+ kb-remove-char-back: "BackSpace";
+ kb-mode-complete: "Control+c";
+}
+
+* {
+ lines: 5;
+ padding: 0;
+ hide-scrollbar: true;
+ border: 0;
+ width: 15%;
+ columns: 1;
+ background-color: @background;
+ font: "Source Code Pro Semibold 9";
+ spacing: 2;
+}
+
+#window {
+ background-color: @background;
+ border: 3;
+ border-color: @cyber;
+ padding: 0.5ch;
+ location: northeast;
+ margin: 28px 8px 0 0; /* Adjust these values for spacing and positioning */
+}
+
+#mainbox {
+ border: 0;
+ border-color: @ac;
+ padding: 0;
+}
+
+#message {
+ border: 0px 0px 0px;
+ border-color: @ac;
+ padding: 1px;
+}
+
+#textbox {
+ text-color: @foreground;
+}
+
+#inputbar {
+ children: [ prompt, textbox-prompt-colon, entry, case-indicator ];
+}
+
+#textbox-prompt-colon {
+ expand: false;
+ str: ":";
+ margin: 0px 0.3em 0em 0em;
+ text-color: inherit;
+}
+
+#listview {
+ fixed-height: 0;
+ border: 0px 0px 0px;
+ border-color: @ac;
+ spacing: 2px;
+ scrollbar: true;
+ padding: 2px 0px 0px;
+}
+
+#element {
+ border: 0;
+ padding: 1px;
+}
+
+#element.normal.normal {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#element.normal.urgent {
+ background-color: @background;
+ text-color: @red;
+}
+
+#element.normal.active {
+ background-color: @foreground;
+ text-color: @background;
+}
+
+#element.selected.normal {
+ background-color: @foreground;
+ text-color: @black;
+}
+
+#element.selected.urgent {
+ background-color: @foreground;
+ text-color: @black;
+}
+
+#element.selected.active {
+ background-color: @foreground;
+ text-color: @black;
+}
+
+#element.alternate.normal {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#element.alternate.urgent {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#element.alternate.active {
+ background-color: @background;
+ text-color: @foreground;
+}
+
+#scrollbar {
+ width: 0px;
+ border: 0;
+ handle-width: 0px;
+ padding: 0;
+}
+
+#sidebar {
+ border: 2px 0px 0px;
+ border-color: @ac;
+}
+
+#button {
+ text-color: @background;
+}
+
+#button.selected {
+ background-color: @ac;
+ text-color: @foreground;
+}
+
+#inputbar {
+ spacing: 0;
+ text-color: @foreground;
+ padding: 1px;
+}
+
+#case-indicator {
+ spacing: 0;
+ text-color: @foreground;
+}
+
+#entry {
+ spacing: 0;
+ text-color: @foreground;
+}
+
+#prompt {
+ spacing: 0;
+ text-color: @foreground;
+}
+
+@import "~/.config/rofi/themes/colors.rasi"
diff --git a/linux/home/.config/rofi/themes/colors.rasi b/linux/home/.config/rofi/themes/colors.rasi
new file mode 100644
index 0000000..d1419e4
--- /dev/null
+++ b/linux/home/.config/rofi/themes/colors.rasi
@@ -0,0 +1,18 @@
+/* colors */
+
+* {
+ alternative: #00101212;
+ background: #101010ff;
+ seperator: #00101212;
+ foreground: #FAFAFA;
+ ac: #00000000;
+ red: #CECCC9;
+ green: #CECCC9;
+ yellow: #CECCC9;
+ blue: #CECCC9;
+ purple: #CECCC9;
+ cyan: #CECCC9;
+ black: #101212;
+ cyber: #53E2AE;
+ orange: #fe8019;
+}
diff --git a/linux/home/.config/rofi/themes/dmenu.rasi b/linux/home/.config/rofi/themes/dmenu.rasi
new file mode 100644
index 0000000..1e8f319
--- /dev/null
+++ b/linux/home/.config/rofi/themes/dmenu.rasi
@@ -0,0 +1,38 @@
+
+* {
+ background-color: #1a2026;
+ border-color: #29343d;
+ text-color: #ffffff;
+ font: "Fira Code Nerd Font Mono 11";
+}
+
+window {
+ anchor: north;
+ location: north;
+ width: 100%;
+ padding: 4px;
+ children: [ horibox ];
+}
+
+horibox {
+ orientation: horizontal;
+ children: [ prompt, entry, listview ];
+}
+
+listview {
+ layout: horizontal;
+ spacing: 10px;
+ lines: 100;
+}
+
+entry {
+ expand: false;
+ width: 14em;
+}
+
+element {
+ padding: 2px 5px;
+}
+element selected {
+ color: #a9bcef;
+}
diff --git a/linux/home/.config/rofi/themes/power.rasi b/linux/home/.config/rofi/themes/power.rasi
new file mode 100644
index 0000000..209a9ac
--- /dev/null
+++ b/linux/home/.config/rofi/themes/power.rasi
@@ -0,0 +1,34 @@
+/**
+ * This theme is intended for a 5 items wide menu
+ * on a 1366x768 pixels resolution.
+ */
+@import "colors.rasi"
+#window {
+ width: 1366px;
+ height: 768px;
+ /* vertical horizontal */
+ padding: 270px 88px;
+ children: [ horibox ];
+}
+#horibox {
+ children: [ listview ];
+}
+#listview {
+ layout: horizontal;
+ spacing: 56px;
+ lines: 5;
+}
+#element {
+ /**
+ * Values bellow are 'no-padding' ones, to which we add 70
+ * top right bottom left
+ * -14px 0px -14px -93px */
+ padding: 56px 70px 56px -23px;
+ background-color: @background-light;
+}
+#element.selected {
+ background-color: @accent;
+ text-color: @background;
+}
+
+
diff --git a/linux/home/.config/sxhkd/show_help.sh b/linux/home/.config/sxhkd/show_help.sh
new file mode 100755
index 0000000..bd0cd6c
--- /dev/null
+++ b/linux/home/.config/sxhkd/show_help.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+cat ~/.config/sxhkd/sxhkdrc | awk '/^[a-z]/ && last {print $0,"\t",last} {last=""} /^#/{last=$0}' | column -t -s $'\t' | rofi -dmenu -i -no-show-icons -width 1000
+
diff --git a/linux/home/.config/sxhkd/sxhkdrc b/linux/home/.config/sxhkd/sxhkdrc
new file mode 100755
index 0000000..1b276c7
--- /dev/null
+++ b/linux/home/.config/sxhkd/sxhkdrc
@@ -0,0 +1,490 @@
+#################################################
+# ███████╗██╗ ██╗██╗ ██╗██╗ ██╗██████╗ #
+# ██╔════╝╚██╗██╔╝██║ ██║██║ ██╔╝██╔══██╗ #
+# ███████╗ ╚███╔╝ ███████║█████╔╝ ██║ ██║ #
+# ╚════██║ ██╔██╗ ██╔══██║██╔═██╗ ██║ ██║ #
+# ███████║██╔╝ ██╗██║ ██║██║ ██╗██████╔╝ #
+# ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ #
+#################################################
+
+#---------------------------------------------------------------
+#
+# WM independent hotkeys
+#
+
+# Help
+super + slash
+ ~/.config/sxhkd/show_help.sh -m -3
+
+# Application menu
+super + a
+ ~/.config/polybar/scripts/menu.sh
+
+# Shutdown menu
+#super + alt + Delete
+# ~/.config/polybar/scripts/sysmenu.sh
+
+# lockscreen
+super + Escape
+ betterlockscreen -l
+
+# Program launcher
+super + @space
+ rofi -show drun
+
+# Run launcher (commands)
+super + r
+ rofi -show run
+
+# Task switcher
+alt + Tab
+ rofi -show window
+
+# Turn off compositor
+super + shift + p
+ toggleprogram "picom" "--experimental-backends"
+
+#picom-trans -c -10
+#picom-trans -c +10
+
+# Close all active notifications.
+# ~button1
+# bspc query -D -d .focused.!occupied && $HOME/.local/bin/eww update noti=false; sleep 0.270; $HOME/.local/bin/eww close notification-popup; pkill openEwwPopup.sh
+
+# Toggle control center using middle click.
+~button2
+ xqp 0 $(xdo id -N Bspwm -n root) && sh $HOME/.config/jgmenu/scripts/windows.sh
+#bspc query -D -d .focused.!occupied && sh $HOME/.config/eww/scripts/openControlCenter.sh
+
+# Right click menu
+~button3
+ xqp 0 $(xdo id -N Bspwm -n root) && sh $HOME/.config/jgmenu/scripts/jgmenu.sh
+
+# Toggle control center.
+super + shift + c
+ sh $HOME/.config/eww/scripts/openControlCenter.sh
+
+# Toggle notification center.
+super + shift + n
+ sh $HOME/.config/eww/scripts/openNotificationCenter.sh
+
+## Toggle info center.
+#super + shift + i
+# sh $HOME/.config/eww/scripts/openInfoCenter.sh
+#
+## Toggle color picker.
+#super + shift + x
+# sh $HOME/.local/bin/xcolor-pick
+#
+## Toggle bar.
+#super + shift + b
+# sh $HOME/.local/bin/tglbar
+#
+## Toggle exit screen.
+#super + Escape
+# sh $HOME/.config/eww/scripts/openExitScreen.sh
+#
+## Close exit screen when it's enabled.
+#~Escape
+# [[ -f "$HOME/.cache/eww-escreen.lock" ]] && sh $HOME/.config/eww/scripts/openExitScreen.sh
+
+## Open web browser, and file manager.
+#super + shift + {w,f}
+# {firefox-developer-edition,thunar}
+
+# Terminal emulator
+#super + Return
+# $TERMINAL
+
+# Browser
+super + w
+ $BROWSER
+
+# Keyboard
+super + o
+ onboard
+
+# Show clipmenu
+#alt + x
+# parcellite
+ #~/.scripts/clip.sh
+
+# Screenshots tool (selection)
+ctrl + Print
+ screenshot crop
+
+# Screenshots tool (screen)
+shift + Print
+ screenshot full
+
+# make sxhkd reload its configuration files:
+super + ctrl + x
+ pkill -USR1 -x sxhkd; dunstify "Sxhkd configuration reloaded"
+
+# Kill window
+ctrl + alt + Escape
+ xkill
+
+# Un/mount drives
+ctrl + alt + {m,u}
+ {_,u}mnt
+
+
+#---------------------------------------------------------------
+#
+# bspwm hotkeys
+#
+
+# Quit/restart bspwm
+super + alt + shift + {q,r}
+ bspc {quit,wm -r}
+
+# Close and kill
+super + d
+ bspc node -c
+
+# Alternate between the tiled and monocle layout
+super + m
+ bspc desktop -l next
+super + z
+ bspc desktop -l next
+
+# Send the newest marked node to the newest preselected node
+super + y
+ bspc node newest.marked.local -n newest.!automatic.local
+
+# Swap the current node and the biggest window
+super + g
+ bspc node -s biggest.window
+
+
+#---------------------------------------------------------------
+#
+# state/flags
+#
+
+# Set the window state
+#super + {t,shift + t,s,f}
+# bspc node -t {tiled,pseudo_tiled,floating,fullscreen}
+
+# Floating into monocle layout
+super + shift + m
+ bspc node -t pseudo_tiled; bspc node -t floating && wtp 10 36 $(($(bspc query -T -m | jq '.rectangle.width') * 98 / 100)) $(($(($(bspc query -T -m | jq '.rectangle.height') - $(bspc config top_padding))) * 96 / 100)) "$(pfw)"
+
+# Floating window pane left
+super + shift + h
+ bspc node -t pseudo_tiled; bspc node -t floating && wtp 10 36 $(($(($(bspc query -T -m | jq '.rectangle.width') / 2)) - 5)) $(($(($(bspc query -T -m | jq '.rectangle.height') - $(bspc config top_padding))) * 96 / 100)) "$(pfw)"
+
+# Floating window pane right
+super + shift + l
+ bspc node -t pseudo_tiled && bspc node -t floating && wtp $(($(bspc query -T -m | jq '.rectangle.width') - $(($(bspc query -T -m | jq '.rectangle.width') / 2)))) 36 $(($(($(bspc query -T -m | jq '.rectangle.width') / 2)) - 15)) $(($(($(bspc query -T -m | jq '.rectangle.height') - $(bspc config top_padding))) * 96 / 100)) "$(pfw)"
+
+# Set the window state
+super + {t,shift + t,s}
+ bspc node -t {tiled,pseudo_tiled,floating}; \
+ xdo raise -N Plank; \
+ xdo raise -N '*:*:Picture in picture'; \
+ xdo raise -N "Picture-in-Picture"
+
+# Toggle fullscreen
+super + {f}
+ bspc node -t \~fullscreen
+
+# Toggle sticky
+#super + q
+# bspc node -g sticky=on
+
+# Set the node flags
+super + ctrl + {m,x,y,z}
+ bspc node -g {marked,locked,sticky,private}
+
+# Move layers of windows above/below each other (script)
+super + {equal,minus}
+ layer.sh {+,-}
+
+
+#---------------------------------------------------------------
+#
+# Focus/Swap
+#
+
+# Focus the node in the given direction
+super + {_,shift + }{h,j,k,l}
+ bspc node -{f,s} {west,south,north,east}
+
+# Focus the node for the given path jump
+#super + {p,b,comma,period}
+# bspc node -f @{parent,brother,first,second}
+
+# Focus the next/previous window in the current desktop
+super + {_,shift + }c
+ bspc node -f {next,prev}.local.!hidden.window
+
+# Focus the next/previous desktop in the current monitor
+super + bracket{left,right}
+ bspc desktop -f {prev,next}.local
+
+# Focus the last node/desktop
+super + {grave,Tab}
+ bspc {node,desktop} -f last
+
+# Focus the older or newer node in the focus history
+#super + {o,i}
+# bspc wm -h off; \
+# bspc node {older,newer} -f; \
+# bspc wm -h on
+
+# Focus or send to the given desktop
+super + {_,shift + }{1-9,0}
+ bspc {desktop -f,node -d} '^{1-9,10}'
+
+# Hide window
+super + comma
+ bspc node -g hidden
+
+# Unhide window (script)
+super + period
+ ${HOME}/.config/bspwm/scripts/hide-window unhide
+
+# Toggle the hidden state of the focused node
+super + q
+ ~/.scripts/bspwm-toggle-visibility.sh
+
+#---------------------------------------------------------------
+#
+# Preselect
+#
+
+# Preselect the direction
+super + ctrl + {h,j,k,l}
+ bspc node -p {west,south,north,east}
+
+# Preselect the ratio
+super + ctrl + {1-9}
+ bspc node -o 0.{1-9}
+
+# Cancel the preselection for the focused node
+super + ctrl + space
+ bspc node -p cancel
+
+# Cancel the preselection for the focused desktop
+super + ctrl + shift + space
+ bspc query -N -d | xargs -I id -n 1 bspc node id -p cancel
+
+# Close all receptacle
+super + shift + i
+ for win in `bspc query -N -n .leaf.\!window`; do bspc node $win -k ; done;
+
+# Insert receptacle
+super + i; {h,j,k,l}
+ bspc node --presel-dir {west,south,north,east} -i
+
+# Move to rectacle
+super + ctrl + i
+ bspreceptacle
+
+# Balance nodes
+super + alt + i
+ bspc node @/ -B
+
+
+
+#---------------------------------------------------------------
+#
+# Move/resize
+#
+
+## Drag tiling window to floating
+alt + button1
+ bspdragtofloat
+alt + @button1
+ bspdragtofloat stop
+@button1
+ bspdragtofloat stop
+~button1
+ :
+
+# Move a floating window or swap with any other adjacent tiled/pseudo_tiled window
+ctrl + alt + {h,j,k,l}
+ { dir=west dx=-20 dy=0 \
+ , dir=south dx=0 dy=20 \
+ , dir=north dx=0 dy=-20 \
+ , dir=east dx=20 dy=0 \
+ }; \
+ bspc node --move "$dx" "$dy" || bspc node --swap $dir
+
+# Rotate windows to different nodes
+super + ctrl + r
+ bspc node @parent -R 90
+
+# Resize tiled/floating windows (script)
+shift + alt + {h,j,k,l}
+ bspwm_resize.sh {west,south,north,east}
+#shift + alt + {h,j,k,l}
+# {bspc node @parent/second -z left -20 0; \
+# bspc node @parent/first -z right -20 0, \
+# bspc node @parent/second -z top 0 +20; \
+# bspc node @parent/first -z bottom 0 +20, \
+# bspc node @parent/first -z bottom 0 -20; \
+# bspc node @parent/second -z top 0 -20, \
+# bspc node @parent/first -z right +20 0; \
+# bspc node @parent/second -z left +20 0}
+
+# Resize window into predefined pseudo_tiled window
+super + shift + s
+ bspc node -t pseudo_tiled; bspc node -t floating && wtp 396 185 570 394 "$(pfw)"
+
+# Spawn next window/program into predefined floating window
+super + ctrl + s
+ bspc rule -a '*' -o state=floating rectangle=720x480+320+200
+
+# Spawn next window/program into another desktop
+super + alt + {1-9,0}
+ bspc rule -a '*' -o desktop=^{1-9,10}
+
+# Focused desktop window gaps scroll
+shift + alt + {1,2}
+ bspc config -d focused window_gap $((`bspc config -d focused window_gap` {-,+} 5 ))
+
+# Global window gaps scroll
+ctrl + alt + {1,2}
+ bspc config window_gap $(( $(bspc config window_gap) {-,+} 5 ))
+
+#---------------------------------------------------------------
+#
+# Multimedia
+#
+# Multimedia control
+{XF86AudioStop,XF86AudioPlay,XF86AudioPrev,XF86AudioNext}
+ playerctl {stop,play-pause,previous,next}
+
+# Use arrow keys as multimedia keys
+alt + shift + {Left,Up,Right}
+ playerctl {previous,play-pause,next}
+
+# Brightness control
+XF86MonBrightness{Up,Down}
+ brightnessctl s 5%{+,-}
+
+# Use arrow keys as brightness keys
+alt + {Right,Left}
+ brightnessctl s 5%{+,-}
+
+# Volume control
+XF86Audio{Raise,Lower}Volume
+ pulsemixer --change-volume {+,-}5
+
+# Use arrow keys as volume keys
+alt + {Up,Down}
+ pulsemixer --change-volume {+,-}5
+
+
+#---------------------------------------------------------------
+#
+# Xdotool keys
+#
+
+# Move mouse cursor north, west, south, east
+alt + {w,a,s,d}
+ xdotool mousemove_relative --sync {-- 0 -24, -- -24 0, 0 24, 24 0}
+
+# Move mouse cursor diagonally north-west, north-east, south-west, south-east
+shift + alt + {q,e,a,d}
+ xdotool mousemove_relative --sync {-- -24 -24, -- 24 -24, -- -24 24,-- 24 24}
+
+# Emulate left mouse click
+alt + i
+ xdotool click --clearmodifiers 1
+
+# Emulate left mouse click select
+alt + shift + i
+ xdotool mousedown 1 sleep 0.5 mousemove_relative --sync {-- -8 0, 0 8, -- 0 -8, 8 0} sleep 0.5 mouseup 1
+
+# Emulate mouse right click
+alt + o
+ xdotool click --clearmodifiers 3
+
+# Emulate mouse scroll up
+alt + n
+ xdotool click --clearmodifiers 4
+
+# Emulate mouse scroll down
+alt + m
+ xdotool click --clearmodifiers 5
+
+# Emulate mouse scroll button
+alt + p
+ xdotool click --clearmodifiers 2
+
+# Emulate home key
+alt + ctrl + Left
+ xdotool keyup Left key --clearmodifiers Home
+
+# Emulate end key
+alt + ctrl + Right
+ xdotool keyup Right key --clearmodifiers End
+
+# Emulate delete key
+~alt + BackSpace
+ xte 'keyup Alt_L' 'key Delete' 'keydown Alt_L'
+
+
+#---------------------------------------------------------------
+#
+# Programs
+#
+
+# Scratchpd
+super + semicolon
+ ~/.scripts/scratchpad
+
+super + x
+ ~/.scripts/scratchpad
+
+# Heads-Up-Display scratchpad terminal
+super + e
+ ~/.scripts/heads-up-display
+
+## File manager nnn
+#super + shift + n
+# $TERMINAL -e nnn
+
+# File manager pcmanfm (GUI)
+super + shift + f
+ pcmanfm
+
+# Bitwarden-rofi
+super + shift + b
+ rofi-rbw
+
+# Thunderbird mail
+#super + shift + m
+# thunderbird
+
+# VirtualBox gui
+super + v
+ /usr/bin/VirtualBox -- :0 vt1
+
+# Discord
+super + shift + d
+ discord
+
+# Spotify
+super + shift + y
+ spotify
+
+# Book reader (zathura)
+super + shift + z
+ zathura
+
+# Dictionary
+super + ctrl + w
+ goldendict
+
+# Thesaurus
+super + shift + w
+ artha
+
+# Suspend
+alt + F4
+ systemctl suspend && betterlockscreen --lock dimblur
diff --git a/linux/home/.config/tmux/file_manager.sh b/linux/home/.config/tmux/file_manager.sh
new file mode 100755
index 0000000..b3a70a5
--- /dev/null
+++ b/linux/home/.config/tmux/file_manager.sh
@@ -0,0 +1,108 @@
+#!/usr/bin/env bash
+# tmux file opener with fallback file manager (no preview)
+
+# Mark this pane as the file manager immediately
+tmux select-pane -T "FILE_MANAGER"
+# Also set the option as backup
+tmux set-option -pq @file_manager 1
+
+orig_pane="$1"
+chooser_file="$HOME/.cache/tmux-fm-selected"
+rm -f "$chooser_file"
+
+# Function: pick available file manager
+pick_fm() {
+ if command -v lf >/dev/null 2>&1; then
+ echo "lf"
+ elif command -v nnn >/dev/null 2>&1; then
+ echo "nnn"
+ elif command -v ranger >/dev/null 2>&1; then
+ echo "ranger"
+ else
+ echo ""
+ fi
+}
+
+fm=$(pick_fm)
+if [[ -z "$fm" ]]; then
+ echo "No file manager found (lf, nnn, ranger)." >&2
+ cleanup
+ exit 1
+fi
+
+# Cleanup function to reset both title and option
+cleanup() {
+ tmux select-pane -T ""
+ tmux set-option -puq @file_manager
+ rm -f "$chooser_file"
+}
+
+# Set trap to cleanup on exit (including when user presses 'q')
+trap cleanup EXIT INT TERM
+
+# Launch the chosen file manager with no preview where possible
+case "$fm" in
+ nnn)
+ # Disable preview completely and use picker mode
+ # -A: disable dir auto-select, -e: open text files in editor
+ # -o: open files with opener, -x: show only selection
+ NNN_OPENER="tee \"$chooser_file\"" nnn -Axo
+ ;;
+ lf)
+ # Disable preview and set selection path
+ lf -command 'set preview false' -selection-path="$chooser_file"
+ ;;
+ ranger)
+ # Disable all previews
+ ranger --choosefile="$chooser_file" \
+ --cmd='set preview_files false' \
+ --cmd='set preview_directories false' \
+ --cmd='set preview_images false'
+ ;;
+esac
+
+# Exit if no file chosen (user pressed 'q' or cancelled)
+if [[ ! -s "$chooser_file" ]]; then
+ exit 0
+fi
+
+file="$(head -n 1 "$chooser_file")"
+rm -f "$chooser_file"
+
+# Restrict to current window panes and exclude the file manager pane
+current_window=$(tmux display-message -p '#I')
+mapfile -t panes < <(
+ tmux list-panes -t "$current_window" -F '#S:#I.#P' |
+ grep -v "^$(tmux display-message -p '#S:#I').$(tmux display-message -p '#P')$"
+)
+
+# Choose target pane
+if [[ ${#panes[@]} -eq 0 ]]; then
+ exit 1
+elif [[ ${#panes[@]} -eq 1 ]]; then
+ target="${panes[0]}"
+else
+ echo "Select target pane:"
+ for i in "${!panes[@]}"; do
+ letter=$(printf "\\$(printf '%03o' $((97 + i)))") # a, b, c...
+ echo "$letter) ${panes[$i]}"
+ done
+ read -n 1 -p "Choice: " choice
+ echo
+ idx=$(( $(printf "%d" "'$choice") - 97 ))
+ if [[ $idx -ge 0 && $idx -lt ${#panes[@]} ]]; then
+ target="${panes[$idx]}"
+ else
+ exit 1
+ fi
+fi
+
+# Decide if file is text or binary
+if file --mime-type "$file" 2>/dev/null | grep -q 'text/'; then
+ opener="${EDITOR:-$(command -v nvim || command -v vim || echo 'vi')}"
+else
+ opener="$(command -v xdg-open || command -v open || echo 'cat')"
+fi
+
+# Send open command to target pane
+tmux send-keys -t "$target" "$opener \"$file\"" C-m
diff --git a/linux/home/.config/tmux/fzf-menu.sh b/linux/home/.config/tmux/fzf-menu.sh
new file mode 100755
index 0000000..d7863d9
--- /dev/null
+++ b/linux/home/.config/tmux/fzf-menu.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+# fzf session name
+FZF_SESSION_NAME="fzf"
+
+# Print error messages
+error() {
+ echo "Error: $1" >&2
+}
+
+# Check if tmux is installed
+if ! command -v tmux >/dev/null 2>&1; then
+ error "tmux is not installed."
+ exit 1
+fi
+
+# Function to handle file or directory opening
+open_selected_item() {
+ # Use fzf to select a file from the specified directory
+ SELECTED_FILE=$(find ~ -type f | fzf --preview "bat --style=numbers --color=always --line-range=:500 {}" \
+ --preview-window=up:60% --height=90% --layout=reverse --border=sharp --ansi)
+
+ if [ "$SELECTED_FILE" != "" ]; then
+ # Ask whether to open the file or its directory
+ read -p "Open file (f) or directory (d)? " choice
+ case "$choice" in
+ f | F)
+ # Open the selected file in nvim
+ nvim "$SELECTED_FILE"
+ ;;
+ d | D)
+ # Change to the directory containing the selected file
+ cd "$(dirname "$SELECTED_FILE")"
+ ;;
+ *)
+ echo "Invalid choice. Please enter 'f' for file or 'd' for directory."
+ ;;
+ esac
+ else
+ echo "No file selected."
+ fi
+}
+
+# Check if the fzf session exists
+if tmux has-session -t "$FZF_SESSION_NAME" 2>/dev/null; then
+ # Get the current tmux session name
+ CURRENT_SESSION=$(tmux display-message -p '#S')
+
+ # If currently in the fzf session, detach; otherwise, attach to it
+ if [ "$CURRENT_SESSION" = "$FZF_SESSION_NAME" ]; then
+ tmux detach-client
+ else
+ tmux set -gF '@last_session_name' '#S' # Store the current session name
+ tmux display-popup -E -x200% -y0 -w50% -h99% "tmux attach-session -t $FZF_SESSION_NAME"
+ fi
+else
+ # If the fzf session doesn't exist, create it and run file selection logic in a popup
+ tmux set -gF '@last_session_name' '#S' # Store the current session name
+ tmux display-popup -E -w100% -h100% -y0 -x0 "tmux new-session -A -s fzf '$0 open_selected_item'"
+fi
diff --git a/linux/home/.config/tmux/left-status.sh b/linux/home/.config/tmux/left-status.sh
new file mode 100755
index 0000000..e4a8c49
--- /dev/null
+++ b/linux/home/.config/tmux/left-status.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+function ip-address() {
+ # Loop through the interfaces and check for the interface that is up.
+ for file in /sys/class/net/*; do
+ iface=$(basename $file);
+ read status < $file/operstate;
+ [ "$status" == "up" ] && ip addr show $iface | awk '/inet /{printf $2""}'
+ done
+}
+
+function vpn-connection() {
+ # Check for tun0 interface.
+ [ -d /sys/class/net/tun0 ] && printf "%s " 'VPN*'
+}
+
+function main() {
+ # Comment out any function you do not need.
+ ip-address
+ vpn-connection
+}
+
+# Calling the main function which will call the other functions.
+main
+
diff --git a/linux/home/.config/tmux/notes.sh b/linux/home/.config/tmux/notes.sh
new file mode 100755
index 0000000..71a8dc7
--- /dev/null
+++ b/linux/home/.config/tmux/notes.sh
@@ -0,0 +1,113 @@
+#!/usr/bin/env bash
+
+# Notes/TODO management & quick search engine via tmux
+
+NOTES_DIR="$HOME/documents/main"
+TODO_FILE="$NOTES_DIR/inbox/tasks/TODO.md"
+EDITOR="nvim"
+NOTE_SESSION_NAME="note"
+BROWSER_PREFERENCES=("firefox" "chromium" "google-chrome" "brave-browser" "chrome")
+SEARCH_URL="https://www.google.com/search?q="
+
+# simple error printing
+error() {
+ echo "Error: $1" >&2
+}
+
+# add a TODO entry with timestamp
+add_todo() {
+ local todo_text="$1"
+ [ -z "$todo_text" ] && return 1
+
+ [ ! -f "$TODO_FILE" ] && echo -e "# TODO List\n" > "$TODO_FILE"
+
+ echo "- [ ] $todo_text ($(date '+%Y-%m-%d %H:%M'))" >> "$TODO_FILE"
+ tmux display-message "Added TODO: $todo_text"
+}
+
+# open a web search
+search_web() {
+ local query="$1"
+ [ -z "$query" ] && return 1
+
+ local encoded_query=$(printf '%s' "$query" | sed 's/ /+/g' | sed 's/[^a-zA-Z0-9+._-]//g')
+ local search_url="${SEARCH_URL}${encoded_query}"
+
+ if command -v xdg-open >/dev/null 2>&1; then
+ xdg-open "$search_url" >/dev/null 2>&1 &
+ else
+ for browser in "${BROWSER_PREFERENCES[@]}"; do
+ command -v "$browser" >/dev/null 2>&1 && $browser "$search_url" >/dev/null 2>&1 & break
+ done
+ fi
+
+ tmux display-message "Opening search for: $query"
+}
+
+# display the notes menu (in-editor or popup)
+open_menu() {
+ tmux set -gF '@last_session_name' '#S'
+
+ if tmux has-session -t "$NOTE_SESSION_NAME" 2>/dev/null && tmux list-panes -t "$NOTE_SESSION_NAME" -F "#{pane_current_command}" | grep -q "^nvim$"; then
+ # menu for active nvim session
+ tmux display-menu -T "#[align=center] Notes (nvim-mode)" \
+ "New note" n "command-prompt -p 'Enter note title:' 'send-keys -t $NOTE_SESSION_NAME \":e $NOTES_DIR/%%.md\" Enter'" \
+ "Open note" o "send-keys -t $NOTE_SESSION_NAME \":cd $NOTES_DIR | FzfLua files\" Enter" \
+ "TODO List" t "send-keys -t $NOTE_SESSION_NAME \":e $TODO_FILE\" Enter" \
+ "Add Quick TODO" T "command-prompt -p 'Enter TODO:' 'run-shell \"$0 --add-todo %%\"'" \
+ "Grep/find patterns" g "send-keys -t $NOTE_SESSION_NAME \":cd $NOTES_DIR | FzfLua live_grep\" Enter" \
+ "Web Search" s "command-prompt -p 'Search query:' 'run-shell \"$0 --search %%\"'" \
+ "Quit (q)" q ""
+ else
+ # popup menu outside of nvim
+ tmux display-menu -T "#[align=center] Notes (popup-mode)" \
+ "New note" n "command-prompt -p 'Enter note title:' \"display-popup -w 100% -h 100% -E 'tmux new-session -A -s $NOTE_SESSION_NAME \\\"$EDITOR $NOTES_DIR/%%.md\\\"'\"" \
+ "Open note" o "display-popup -w 100% -h 100% -E \"tmux new-session -A -s $NOTE_SESSION_NAME 'fzf --preview \\\"bat --style=numbers --color=always --line-range=:500 {}\\\" --preview-window=up:60% --height=90% --layout=reverse --border=sharp --ansi < <(find $NOTES_DIR -type f -name \\\"*.md\\\") | xargs -r $EDITOR'\"" \
+ "TODO List" t "display-popup -w 100% -h 100% -E \"tmux new-session -A -s $NOTE_SESSION_NAME \\\"$EDITOR $TODO_FILE\\\"\"" \
+ "Add Quick TODO" T "command-prompt -p 'Enter TODO:' 'run-shell \"$0 --add-todo %%\"'" \
+ "Grep/find patterns" g "display-popup -w 100% -h 100% -E \"tmux new-session -A -s $NOTE_SESSION_NAME 'rg --color=always --line-number --no-heading --smart-case . $NOTES_DIR | fzf --delimiter=: --preview \\\"bat --style=numbers --color=always --line-range=:500 {1}\\\" --preview-window=up:60% --height=90% --layout=reverse --border=sharp --ansi | cut -d ':' -f 1 | xargs -r $EDITOR'\"" \
+ "Web Search" s "command-prompt -p 'Search query:' 'run-shell \"$0 --search %%\"'" \
+ "Quit (q)" q ""
+ fi
+}
+
+# make sure tmux is installed
+command -v tmux >/dev/null 2>&1 || { error "tmux is not installed."; exit 1; }
+
+# handle CLI arguments
+if [ "$1" = "--add-todo" ]; then
+ shift
+ add_todo "$*"
+ exit 0
+fi
+
+if [ "$1" = "--search" ]; then
+ shift
+ search_web "$*"
+ exit 0
+fi
+
+if [ "$1" = "--new" ]; then
+ if tmux has-session -t "$NOTE_SESSION_NAME" 2>/dev/null; then
+ # reuse existing session
+ tmux display-popup -w 100% -h 100% -E "
+ FILE=\$(find $NOTES_DIR -type f -name '*.md' \
+ | fzf --preview 'bat --style=numbers --color=always --line-range=:500 {}' \
+ --preview-window=up:60% --height=90% --layout=reverse --border=sharp --ansi)
+ [ -n \"\$FILE\" ] && tmux send-keys -t $NOTE_SESSION_NAME \":e \$FILE\" Enter
+ "
+ else
+ open_menu
+ fi
+ exit 0
+fi
+
+# default behavior: toggle or open menu
+if [ -z "$1" ]; then
+ if tmux has-session -t "$NOTE_SESSION_NAME" 2>/dev/null; then
+ CURRENT_SESSION=$(tmux display-message -p '#S')
+ [ "$CURRENT_SESSION" = "$NOTE_SESSION_NAME" ] && tmux detach-client || tmux display-popup -E -x200% -y0 -w50% -h99% "tmux attach-session -t $NOTE_SESSION_NAME"
+ else
+ open_menu
+ fi
+fi
diff --git a/linux/home/.config/tmux/right-status.sh b/linux/home/.config/tmux/right-status.sh
new file mode 100755
index 0000000..a14bbe3
--- /dev/null
+++ b/linux/home/.config/tmux/right-status.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# Get CPU average
+getCPU=$[100-$(vmstat 1 2|tail -1|awk '{print $15}')]
+
+# Grab the second line of the ouput produced by the command: free -g (displays output in Gb)
+getMem=$(free -h | sed -n '2p')
+getMemPct=$(free -g | sed -n '2p')
+
+# Split the string in secondLine into an array
+read -ra ADDR <<< "$getMem"
+read -ra ADDRPct <<< "$getMemPct"
+
+# Get the total RAM from arrays
+totalRam="${ADDR[1]//[^0-9.0-9]/}"
+totalRamPct="${ADDRPct[1]}"
+
+# Get the used RAM from arrays
+usedRam="${ADDR[2]//[^0-9.0-9]/}"
+usedRamPct="${ADDRPct[2]}"
+
+# Calculate and display the percentages
+pct="$(($usedRamPct*100/$totalRamPct))"
+usage="$usedRam/$totalRam"
+#echo "cpu:$getCPU% | mem:$pct% ($usage""G)"
+echo "Cpu:$getCPU% | Mem:$pct% |"
+
diff --git a/linux/home/.config/tmux/tmux-popup-pane-manager.sh b/linux/home/.config/tmux/tmux-popup-pane-manager.sh
new file mode 100755
index 0000000..bb0cfef
--- /dev/null
+++ b/linux/home/.config/tmux/tmux-popup-pane-manager.sh
@@ -0,0 +1,152 @@
+#!/usr/bin/bash
+# tmux-popup-pane-manager.sh - menu driven tmux pane activities
+# github repo: https://github.com/pl643/tmux-scripts
+# resize, selection, syncronize, layout, splits, kill, break
+
+# sample tmux.conf binding:
+# bind-key -n M-p tmux-popup-pane-manager.sh
+
+[ "$TMUX" = "" ] && echo "NOTE: needs to be run inside a tmux sessions" && exit 1
+
+run_after_popup="/tmp/.run_after_popup"
+realpath="$(realpath "$0")"
+if [ "$1" != "--no-popup" ]; then
+ tmux popup -E -T "────────────── Pane Manager ─────" -w 46 -h 35 "$realpath --no-popup"
+
+ [ -f "$run_after_popup" ] && bash "$run_after_popup" && rm "$run_after_popup"
+ exit 0
+fi
+
+pane_border_status="off"
+display_menu() {
+ clear
+ tmux list-windows | grep active | awk '{print $2}' | tail -c2 | grep -q Z && zoom_status="on" || zoom_status="off"
+ tmux show-options -w | grep -q 'synchronize-panes.*on' && synchronize_panes="on" || synchronize_panes="off"
+ tmux show-options -w | grep -q 'pane-border-status.*top' && pane_border_status="top"
+ tmux show-options -w | grep -q 'pane-border-status.*bottom' && pane_border_status="bottom"
+ printf "
+ Resize
+
+ hjkl x 5 HJKL x 1
+ 1 - 9 | x 10%% ! - ) ─ x 10%%
+ = equally | + equally ─
+
+ Split
+
+ s - spilt - v | spilt |
+
+ Navigation
+
+ n p next/prev pane
+ N P next/prev layout
+ u d swap pane up/down
+
+ Toggles
+
+ b border [ %s ]
+ S syncronize [ %s ]
+ z zoom [ %s ]
+
+ Misc
+
+ B break (make pane into window)
+ o join this pane to window
+ D send C-d
+ e display panes / exit
+ t rename pane
+ X kill (no confirm!)
+ q quit" "$pane_border_status" "$synchronize_panes" "$zoom_status"
+}
+display_menu
+
+# https://www.reddit.com/r/tmux/comments/g9nr01/how_to_show_message_or_effect_when/
+# Uncomment this setting if want status of pane sync on the status bar
+tmux set -ag status-right '#{?pane_synchronized, #[fg=red]IN_SYNC#[default],}'
+
+# https://www.reddit.com/r/tmux/comments/dfj5ye/rename_pane_not_window_is_there_a_builtin/
+tmux set -g pane-border-format " [ ###P #T ] "
+
+# If C-c is press in the while [ true ] loop, a run runaway process occurs, limiting
+# it to 20 will cause the loop to exit after 20 loops. Modify MAXNUMLOOP if you
+# need more keys presses.
+MAXNUMLOOP=100
+COUNTER=0
+while [ "$COUNTER" -lt "$MAXNUMLOOP" ]; do
+
+ read -sn1 c || exit
+
+ # Resize x 1
+ [ "$c" = "H" ] && tmux resize-pane -L 1
+ [ "$c" = "L" ] && tmux resize-pane -R 1
+ [ "$c" = "J" ] && tmux resize-pane -D 1
+ [ "$c" = "K" ] && tmux resize-pane -U 1
+
+ # Resize x 5
+ [ "$c" = "h" ] && tmux resize-pane -L 5
+ [ "$c" = "l" ] && tmux resize-pane -R 5
+ [ "$c" = "j" ] && tmux resize-pane -D 5
+ [ "$c" = "k" ] && tmux resize-pane -U 5
+
+ # Resize X percent
+ [ "$c" = "1" ] && tmux resize-pane -x $(($(tmux display-message -p "#{window_width}") * 10 / 100))
+ [ "$c" = "2" ] && tmux resize-pane -x $(($(tmux display-message -p "#{window_width}") * 20 / 100))
+ [ "$c" = "3" ] && tmux resize-pane -x $(($(tmux display-message -p "#{window_width}") * 30 / 100))
+ [ "$c" = "4" ] && tmux resize-pane -x $(($(tmux display-message -p "#{window_width}") * 40 / 100))
+ [ "$c" = "5" ] && tmux resize-pane -x $(($(tmux display-message -p "#{window_width}") * 50 / 100))
+ [ "$c" = "6" ] && tmux resize-pane -x $(($(tmux display-message -p "#{window_width}") * 60 / 100))
+ [ "$c" = "7" ] && tmux resize-pane -x $(($(tmux display-message -p "#{window_width}") * 70 / 100))
+ [ "$c" = "8" ] && tmux resize-pane -x $(($(tmux display-message -p "#{window_width}") * 80 / 100))
+ [ "$c" = "9" ] && tmux resize-pane -x $(($(tmux display-message -p "#{window_width}") * 90 / 100))
+
+ # Resize Y percent
+ [ "$c" = "!" ] && tmux resize-pane -y $(($(tmux display-message -p "#{window_height}") * 10 / 100))
+ [ "$c" = "@" ] && tmux resize-pane -y $(($(tmux display-message -p "#{window_height}") * 20 / 100))
+ [ "$c" = "#" ] && tmux resize-pane -y $(($(tmux display-message -p "#{window_height}") * 30 / 100))
+ [ "$c" = "$" ] && tmux resize-pane -y $(($(tmux display-message -p "#{window_height}") * 40 / 100))
+ [ "$c" = "%" ] && tmux resize-pane -y $(($(tmux display-message -p "#{window_height}") * 50 / 100))
+ [ "$c" = "^" ] && tmux resize-pane -y $(($(tmux display-message -p "#{window_height}") * 60 / 100))
+ [ "$c" = "&" ] && tmux resize-pane -y $(($(tmux display-message -p "#{window_height}") * 70 / 100))
+ [ "$c" = "*" ] && tmux resize-pane -y $(($(tmux display-message -p "#{window_height}") * 80 / 100))
+ [ "$c" = "(" ] && tmux resize-pane -y $(($(tmux display-message -p "#{window_height}") * 90 / 100))
+
+ # Pane layout cycle
+ [ "$c" = "N" ] || [ "$c" = " " ] && tmux next-layout
+ [ "$c" = "P" ] && tmux previous-layout
+
+ # Pane selection cycle
+ [ "$c" = "n" ] && tmux select-pane -t :.+
+ [ "$c" = "p" ] && tmux select-pane -t :.-
+
+ # Pane layout selection even horizontal/vertical
+ [ "$c" = "=" ] && tmux select-layout even-horizontal
+ [ "$c" = "+" ] && tmux select-layout even-vertical
+
+ # Rotate pane
+ [ "$c" = "u" ] && tmux swap-pane -U
+ [ "$c" = "d" ] && tmux swap-pane -D
+
+ # Syncronize pane
+ [ "$c" = "S" ] && tmux setw synchronize-pane && display_menu
+
+ # border status ( 3 toggle off, top, bottom )
+ [ "$c" = "b" ] && [ "$pane_border_status" = "off" ] && tmux set pane-border-status && display_menu && continue
+ [ "$c" = "b" ] && [ "$pane_border_status" = "top" ] && tmux set pane-border-status bottom && display_menu && continue
+ [ "$c" = "b" ] && [ "$pane_border_status" = "bottom" ] && tmux set pane-border-status off &&
+ pane_border_status="off" && display_menu && continue
+
+ # Split panes
+ [ "$c" = "s" ] || [ "$c" = "-" ] && tmux split -v
+ [ "$c" = "v" ] || [ "$c" = "|" ] && tmux split -h
+
+ # Misc
+ [ "$c" = "B" ] && tmux break-pane
+ [ "$c" = "o" ] && printf "\n\n join window: " && read window && tmux join-pane -t "$window"
+ [ "$c" = "X" ] && tmux kill-pane
+ [ "$c" = "D" ] && tmux send-key C-d
+ display_menu
+ [ "$c" = "q" ] && exit
+ [ "$c" = "e" ] && echo tmux display-panes >"$run_after_popup" && exit
+ [ "$c" = "z" ] && tmux resize-pane -Z && display_menu
+ [ "$c" = "t" ] && printf "\n\n pane name: " && read pane_name && tmux select-pane -T "$pane_name" && display_menu
+ let COUNTER=COUNTER+1
+done
diff --git a/linux/home/.config/tmux/tmux-toggle-option.sh b/linux/home/.config/tmux/tmux-toggle-option.sh
new file mode 100755
index 0000000..52d5fdb
--- /dev/null
+++ b/linux/home/.config/tmux/tmux-toggle-option.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/bash
+
+#USAGE="USAGE: $0 OPTION_NAME ON_STATE OFF_STATE"
+
+#OPTION_NAME=$1
+#ON_STATE=$2
+#OFF_STATE=$3
+#
+#if [[ "$#" != 3 ]]; then
+# echo $USAGE
+# exit 1
+#fi
+#
+#if [[ `tmux show-option -w | grep "$OPTION_NAME $ON_STATE"` ]]; then
+# OPTION_VALUE=$OFF_STATE
+#else
+# OPTION_VALUE=$ON_STATE
+#fi
+#
+#tmux display-message "monitor activity: $OPTION_NAME $OPTION_VALUE"
+#tmux set-option -w $OPTION_NAME $OPTION_VALUE > /dev/null
+
+if [ $(tmux show-option -A status-left) != 'status-left* "#[fg=#50fa7b,bg=default] #[bg=#50fa7b,fg=black]❐ #S #[fg=#50fa7b,bg=default]"' ]; then
+ tmux set -g status-left "#[fg=#50fa7b,bg=default] #[bg=#50fa7b,fg=black]❐ #S #[fg=#50fa7b,bg=default] ";
+else
+ tmux set -g status-left "#[fg=#50fa7b,bg=default]#[bg=#50fa7b,fg=black] ❐ #S #( ~/.config/tmux/left-status.sh ) #[fg=#50fa7b,bg=default]" && tmux set -g status-right "#[fg=#50fa7b,bg=default] #{?client_prefix,#[reverse] Prefix #[noreverse] ,}#[bg=default,fg=#50fa7b]#[bg=#50fa7b,fg=black] #( ~/.config/tmux/right-status.sh ) %d-%b-%y | %H:%M #[bg=default,fg=#50fa7b]";
+fi
diff --git a/linux/home/.config/tmux/tmux.conf b/linux/home/.config/tmux/tmux.conf
new file mode 100644
index 0000000..a62e3e3
--- /dev/null
+++ b/linux/home/.config/tmux/tmux.conf
@@ -0,0 +1,835 @@
+# ████████╗███╗ ███╗██╗ ██╗██╗ ██╗
+# ╚══██╔══╝████╗ ████║██║ ██║╚██╗██╔╝
+# ██║ ██╔████╔██║██║ ██║ ╚███╔╝
+# ██║ ██║╚██╔╝██║██║ ██║ ██╔██╗
+# ██║ ██║ ╚═╝ ██║╚██████╔╝██╔╝ ██╗
+# ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
+#――――――――――――――――――――――――――――――――――――――――――
+
+### Options ###
+
+# Setting the Prefix from Ctrl+b to Ctrl+s
+unbind C-b
+set -g prefix C-s
+#set -g prefix M-Space
+
+# Ensure that we can send Ctrl+s to other apps
+bind C-s send-prefix
+
+# Check if in (n)vim
+is_vim="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'"
+
+# Send the prefix to client inside window (nested tmux)
+#bind-key -n C-a send-prefix
+bind -n C-a if-shell "$is_vim" "send-keys C-a" "send-prefix"
+
+# Disable local tmux keys (nested tmux)
+bind -T root F12 \
+ set prefix None \;\
+ set key-table off \;\
+ if -F '#{pane_in_mode}' 'send-keys -X cancel' \;\
+ refresh-client -S \;\
+
+bind -T off F12 \
+ set -u prefix \;\
+ set -u key-table \;\
+ refresh-client -S
+
+# Toggle Sync Panes
+bind C-y set-window-option synchronize-panes\; display-message "Synchronize-Panes is now #{?pane_synchronized,on,off}"
+
+# Setting the delay between Prefix and Command
+set -sg escape-time 10
+#set-option -sg escape-time 10
+
+# Reload tmux with <Prefix>r
+bind r source-file ~/.config/tmux/tmux.conf \; display "Reloaded!"
+
+# Use <Prefix>L to clear terminal
+bind -r L send-keys "clear" Enter
+
+# Rename current window (Ctrl + A, A)
+bind R rename-window '' \; \
+ command-prompt -I "#W" "rename-window -- '%%'"
+
+# Auto rename pane
+set -wg automatic-rename on
+set -g automatic-rename-format "#{pane_current_command}"
+
+# Renumber all windows when one is killed
+set -g renumber-windows on
+
+# Mouse
+set -g mouse on
+
+# Right Click Menu
+bind -n MouseDown3Pane \
+ if-shell -F "#{||:#{match:#{pane_current_command},nvim},#{match:#{pane_current_command},vim}}" \
+ "send-keys -M" \
+ "display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' \
+ 'Vim' v 'send-keys \"vi\" Enter' \
+ 'File-manager' f 'send-keys \"yazi\" Enter' \
+ '' \
+ 'Horizontal Split' '\\\\' 'split-window -h -c \"#{pane_current_path}\"' \
+ 'Vertical Split' '-' 'split-window -v -c \"#{pane_current_path}\"' \
+ '' \
+ 'Copy Mode' 'C' 'copy-mode -e' \
+ 'Paste' 'p' {send-keys C-v} \
+ 'Open with xdg-open' 'o' 'send-keys \"xdg-open #{pane_current_path}\"' \
+ 'Enter' 'e' 'send-keys Enter' \
+ '' \
+ 'Go To Top' '↑' 'copy-mode -e; send-keys gg' \
+ 'Go To Bottom' '↓' 'copy-mode -e; send-keys G' \
+ '' \
+ 'Break Pane' 't' 'break-pane' \
+ 'Join Pane' 'j' 'choose-window \"join-pane -h -s %%\"' \
+ '#{?pane_marked,Unmark,Mark}' 'm' 'select-pane -m' \
+ '#{?#{>:#{window_panes},1},,-}Swap Up' 'u' 'swap-pane -U' \
+ '#{?#{>:#{window_panes},1},,-}Swap Down' 'd' 'swap-pane -D' \
+ '' \
+ 'New Window' 'n' 'new-window' \
+ 'Previous Window' '🡠' 'previous-window' \
+ 'Next Window' '🡢' 'next-window' \
+ 'Swap Window Left' '<' 'swap-window -t -1' \
+ 'Swap Window Right' '>' 'swap-window -t +1' \
+ '' \
+ 'Kill' 'X' 'kill-pane' \
+ 'Respawn' 'R' 'respawn-pane -k' \
+ 'Interrupt (Ctrl+C)' 'c' 'send-keys C-c' \
+ '#{?#{>:#{window_panes},1},,-}#{?window_zoomed_flag,Unzoom,Zoom}' 'z' 'resize-pane -Z'"
+
+
+# Disable copy on primary selection (prevents auto-copy on mouse drag release)
+unbind -T root MouseDrag1Pane
+unbind -T copy-mode MouseDragEnd1Pane
+unbind -T copy-mode-vi MouseDragEnd1Pane
+
+# Left-click to select a pane (no copy mode)
+bind -T root MouseDown1Pane {
+ select-pane -t= # Select pane under mouse
+ send-keys -M # Enable mouse interaction in pane
+}
+
+# Don't go into copy-mode on first click/drag
+bind -T root MouseDrag1Pane if-shell '[ "#{mouse_flags}" = "drag" ]' {
+ if-shell '[ "#{pane_id}" = "#{mouse_pane}" ]' {
+ copy-mode
+ send-keys -X begin-selection
+ } {
+ select-pane -t=
+ send-keys -M
+ }
+} {
+ select-pane -t=
+ send-keys -M
+}
+
+# Left-click to start selection in copy-mode
+bind -T root DoubleClick1Pane {
+ copy-mode
+ send-keys -X begin-selection
+}
+
+# Left-click to start selection in copy-mode
+bind -T copy-mode-vi MouseDown1Pane {
+ send-keys -X cancel-selection
+ send-keys -X begin-selection
+}
+
+# Left-click drag to start selection in copy-mode
+bind -T copy-mode-vi MouseDown1Pane {
+ send-keys -X begin-selection
+}
+
+# Right click to copy the selected text and exit copy mode
+bind -T copy-mode-vi MouseDown3Pane {
+ send-keys -X copy-pipe-no-clear
+ send-keys -X end-selection
+ send-keys Escape
+}
+
+# Middle click exit copy mode without copying (cancel the selection)
+bind -T copy-mode-vi MouseDown2Pane {
+ send-keys -X end-selection
+ send-keys Escape
+}
+
+# Middle click paste if not in copy-mode
+# Conditional behavior for MouseDown2Pane
+if-shell '[ "$CTRL_V_PASTE" = "true" ]' \
+ "bind -T root MouseDown2Pane send-keys C-v" \
+ "bind -T root MouseDown2Pane send-keys C-S-v"
+
+## Right-click drag to exit copy mode without selecting text
+#bind -T copy-mode-vi MouseDragEnd3Pane {
+# send-keys -X end-selection # End selection
+# send-keys Escape # Exit copy mode
+#}
+
+# Make mouse-drag work only with Ctrl
+#unbind -T root MouseDrag1Pane
+#unbind -T copy-mode-vi MouseDrag1Pane
+#bind -n C-MouseDrag1Pane if -Ft= \
+#'#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" send-keys -X \"send-keys -M\"' 'copy-mode -M'
+#bind -T copy-mode-vi C-MouseDrag1Pane send-keys -X begin-selection
+
+# Exiting copy mode without copying (Left-click to exit)
+#bind -T copy-mode-vi MouseDown1Pane {
+# select-pane -t= # Deselect pane
+# send-keys Escape # Exit copy mode
+#}
+
+
+# Scrollback
+set -g history-limit 10000
+
+# Sane scrolling
+set -g terminal-overrides 'xterm*:smcup@:rmcup@'
+
+# Sane scrolling
+bind -n WheelUpPane {
+ if -F '#{==:#{window_name},nvim}' {
+ #send-keys -M
+ send-keys Up
+ } {
+ copy-mode -e
+ }
+}
+bind -n WheelDownPane {
+ if -F '#{==:#{window_name},nvim}' {
+ #send-keys -M
+ send-keys Down
+ } {
+ copy-mode -e
+ }
+}
+
+# Focus events, allow supported requests from applications to passthrough/run in tmux
+set-option -g focus-events on
+
+# Update the TERM variable of terminal emulator when creating a new session or attaching a existing session
+set -g update-environment 'DISPLAY SSH_ASKPASS SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY TERM'
+
+# New session
+#bind -n M-N new-session
+
+
+# Lock
+#set-option -g lock-command "vlock -c"
+#set -g lock-after-time 910 # Seconds; 0 = never
+#bind -n M-Escape lock-session
+
+
+#――――――――――――――――――――――――――――――――――――――――――
+
+### Window Movement/Control ###
+
+# Aggressive resizing, useful when using "grouped sessions" and multi-monitor setup
+setw -g aggressive-resize on
+
+# Menu
+bind-key -n M-m display-menu -x W -y S \
+ "New Session" S "command-prompt -p \"New Session:\" \"new-session -A -s '%%'\"" \
+ "Kill Session" x "kill-session" \
+ "Kill Other Session(s)" X "kill-session -a" \
+ "" \
+ "New Window" ␍ new-window \
+ "Kill Window" k "killw" \
+ "Choose Window" w choose-window \
+ "Previous Window" 🡠 previous-window \
+ "Next Window" 🡢 next-window \
+ "Swap Window Right" ↑ "swap-window -t -1" \
+ "Swap Window Left" ↓ "swap-window -t +1" \
+ "Horizontal Split" v "split-window -h" \
+ "Vertical Split" s "split-window -v" \
+ "" \
+ "Layout Horizontal" h "select-layout even-horizontal" \
+ "Layout Vertical" k "select-layout even-horizontal" \
+ "" \
+ "Swap Pane Up" < "swap-pane -U" \
+ "Swap Pane Down" > "swap-pane -D" \
+ "Break Pane" t break-pane \
+ "Join Pane" j "choose-window 'join-pane -h -s \"%%\"'" \
+ "#{?window_zoomed_flag,Unzoom,Zoom}" z "resize-pane -Z"
+
+
+# Popup Pane Manager
+bind-key -n M-/ run-shell ~/.config/tmux/tmux-popup-pane-manager.sh
+
+# List sessions
+
+#bind M-q display-popup -E -w 75% -h 75% "\
+# tmux list-sessions -F '#{?session_attached,,#{session_name}}' |\
+# sed '/^$/d' |\
+# fzf --reverse --header jump-to-session --preview 'tmux capture-pane -pt {}' |\
+# xargs tmux switch-client -t"
+
+# Kill sessions
+bind M-q display-popup -E "\
+ tmux list-sessions -F '#{?session_attached,,#{session_name}}' |\
+ fzf --reverse -m --header=kill-session |\
+ xargs -I {} tmux kill-session -t {}"
+
+# List sessions
+bind -n M-Space display-popup -E "\
+ tmux list-sessions -F '#{?session_attached,,#{session_name}}' |\
+ sed '/^$/d' |\
+ fzf --reverse --header jump-to-session --preview 'tmux capture-pane -pt {}' |\
+ xargs tmux switch-client -t"
+
+# Session chooser
+#bind -n M-q choose-tree -Zs -O time
+
+# Quick window select
+bind -n M-? list-keys
+bind -n M-0 select-window -t :=0
+bind -n M-1 select-window -t :=1
+bind -n M-2 select-window -t :=2
+bind -n M-3 select-window -t :=3
+bind -n M-4 select-window -t :=4
+bind -n M-5 select-window -t :=5
+bind -n M-6 select-window -t :=6
+bind -n M-7 select-window -t :=7
+bind -n M-8 select-window -t :=8
+bind -n M-9 select-window -t :=9
+
+# move pane to existing window or create it
+bind-key -n M-! run -C '#{?#{m:*|1|*,|#{W:#I|}},joinp -ht :1,breakp -t :1}'
+bind-key -n M-@ run -C '#{?#{m:*|2|*,|#{W:#I|}},joinp -ht :2,breakp -t :2}'
+bind-key -n M-# run -C '#{?#{m:*|3|*,|#{W:#I|}},joinp -ht :3,breakp -t :3}'
+bind-key -n M-$ run -C '#{?#{m:*|4|*,|#{W:#I|}},joinp -ht :4,breakp -t :4}'
+bind-key -n M-% run -C '#{?#{m:*|5|*,|#{W:#I|}},joinp -ht :5,breakp -t :5}'
+bind-key -n M-^ run -C '#{?#{m:*|6|*,|#{W:#I|}},joinp -ht :6,breakp -t :6}'
+bind-key -n M-& run -C '#{?#{m:*|7|*,|#{W:#I|}},joinp -ht :7,breakp -t :7}'
+bind-key -n M-* run -C '#{?#{m:*|8|*,|#{W:#I|}},joinp -ht :8,breakp -t :8}'
+bind-key -n M-( run -C '#{?#{m:*|9|*,|#{W:#I|}},joinp -ht :9,breakp -t :9}'
+bind-key -n M-) run -C '#{?#{m:*|0|*,|#{W:#I|}},joinp -ht :0,breakp -t :0}'
+
+#――――――――――――――――――――――――――――――――――――――――――
+
+### Pane Movement/Control ###
+
+# Smart pane switching with awareness of Vim splits.
+# See: https://github.com/christoomey/vim-tmux-navigator
+# Navigate across tmux-vim
+
+# {{{ keybinds: select-pane
+#bind -Tnav h select-pane -L
+#bind h select-pane -L
+#bind -Tnav C-h select-pane -L
+#bind C-h select-pane -L
+#bind -Tnav j select-pane -D
+#bind j select-pane -D
+#bind -Tnav C-j select-pane -D
+#bind C-j select-pane -D
+#bind -Tnav k select-pane -U
+#bind k select-pane -U
+#bind -Tnav C-k select-pane -U
+#bind C-k select-pane -U
+#bind -Tnav l select-pane -R
+#bind l select-pane -R
+#bind -Tnav C-l select-pane -R
+#bind C-l select-pane -R
+
+# {{{ keybinds: select-pane
+# Smart pane switching with Vim awareness
+is_vim="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +(n?vim|vimx?)'"
+
+bind -n C-h if-shell "$is_vim" "send-keys C-\\ C-n C-h" "select-pane -L"
+bind -n C-j if-shell "$is_vim" "send-keys C-\\ C-n C-j" "select-pane -D"
+bind -n C-k if-shell "$is_vim" "send-keys C-\\ C-n C-k" "select-pane -U"
+bind -n C-l if-shell "$is_vim" "send-keys C-\\ C-n C-l" "select-pane -R"
+# keybinds: select-pane }}}
+
+# {{{ keybinds: split-window
+bind -Tnav "\\" split-window -h -c "#{pane_current_path}" # vertical
+bind "\\" split-window -h -c "#{pane_current_path}" # vertical
+bind -Tnav "|" split-window -fh -c "#{pane_current_path}" # full vertical
+bind "|" split-window -fh -c "#{pane_current_path}" # full vertical
+bind -Tnav "-" split-window -v -c "#{pane_current_path}" # horizontal
+bind "-" split-window -v -c "#{pane_current_path}" # horizontal
+bind -Tnav "_" split-window -fv -c "#{pane_current_path}" # full horizontal
+bind "_" split-window -fv -c "#{pane_current_path}" # full horizontal
+# keybinds: split-window }}}
+
+# {{{ keybinds: resize-pane
+bind -r -Tnav M-h resize-pane -L 10
+bind -r M-h resize-pane -L 10
+bind -r -Tnav M-j resize-pane -D 5
+bind -r M-j resize-pane -D 5
+bind -r -Tnav M-k resize-pane -U 5
+bind -r M-k resize-pane -U 5
+bind -r -Tnav M-l resize-pane -R 10
+bind -r M-l resize-pane -R 10
+# keybinds: resize-pane }}}
+
+# Hide a pane and bring it back with <Prefix>! and <Prefix>@ respectively
+bind-key ! break-pane -d -n _hidden_pane
+bind-key @ join-pane -s $.0
+
+# Send Pane to another session/window with <Prefix>=
+bind-key = command-prompt -p "send pane to:" "join-pane -t '%%'"
+
+#――――――――――――――――――――――――――――――――――――――――――
+
+### Popup ###
+
+# Toggle popup "term" session
+set -g @term_session_name "term"
+bind -n M-t if-shell -F '#{==:#{session_name},term}' {
+ detach-client
+ } {
+ if-shell "tmux has-session -t term" {
+ set -gF '@last_session_name' '#S' # Store the current session name
+ display-popup -E -xC -yC -w60% -h60% "tmux attach-session -t term"
+ } {
+ set -gF '@last_session_name' '#S' # Store the current session name
+ display-popup -E -xC -yC -w60% -h60% "tmux new-session -d -c '#{pane_current_path}' -s term && tmux set-option -t term status off && tmux attach-session -t term"
+ }
+}
+
+
+## Toggle popup "note" session
+bind-key -n M-n if-shell -F "#{client_in_popup}" \
+ "detach-client -P" \
+ "run-shell ~/.config/tmux/notes.sh"
+
+bind-key -n M-N run-shell "~/.config/tmux/notes.sh --new"
+
+# Toggle popup "pack" session
+bind-key -n M-p if-shell -F '#{==:#{session_name},pack}' {
+ detach-client
+} {
+ set -gF '@last_session_name' '#S' # Store the current session name
+ #display-popup -w 80% -h 80% -E "tmux new-session -A -s pack"
+ display-popup -E -x200% -y0 -w50% -h99% "tmux new-session -A -s pack"
+}
+
+# M-T → Open TODO.md in popup
+bind-key -n M-o if-shell -F '#{==:#{session_name},todo}' {
+
+ detach-client
+} {
+ set -gF '@last_session_name' '#S'
+ display-popup -E -x200% -y0 -w50% -h99% "tmux new-session -A -s todo 'nvim ~/documents/main/inbox/tasks/TODO.md'"
+}
+
+# M-y → Toggle VM popup (starts/attaches session "virt" inside popup)
+bind-key -n M-y if-shell -F '#{==:#{session_name},virt}' {
+ detach-client -P
+} {
+ set -gF '@last_session_name' '#S'
+ display-popup -E -x200% -y0 -w40% -h60% "tmux new-session -A -s virt bash -lc 'echo \"### VM Manager ###\"; echo; echo \"Available VM scripts:\"; ls -1 ~/.scripts/env/virt/ 2>/dev/null || echo \"No scripts found in ~/.scripts/env/virt/\"; echo; echo \"Run your VM by typing its script name (e.g., ubuntu, fedora, win11).\"; exec \$SHELL'"
+}
+
+# M-H → Open history in popup
+bind-key -n M-H if-shell -F '#{==:#{session_name},hist}' {
+ detach-client
+} {
+ set -gF '@last_session_name' '#S'
+ display-popup -E -x200% -y0 -w50% -h99% "tmux new-session -A -s hist \"bash -c \'
+ shell=\\\$(basename \\\$SHELL)
+ if [[ \\\$shell == zsh ]]; then
+ if [[ -f \\\$HOME/.config/zsh/.zhistory ]]; then
+ nvim \\\$HOME/.config/zsh/.zhistory
+ elif [[ -f \\\$HOME/.zhistory ]]; then
+ nvim \\\$HOME/.zhistory
+ else
+ echo \\\"No Zsh history found\\\"; sleep 5
+ fi
+ elif [[ \\\$shell == bash ]]; then
+ if [[ -f \\\$HOME/.bash_history ]]; then
+ nvim \\\$HOME/.bash_history
+ else
+ echo \\\"No Bash history found\\\"; sleep 5
+ fi
+ else
+ echo \\\"Unknown shell: \\\$shell\\\"; sleep 5
+ fi
+ \'\""
+}
+
+# Move popup session (note, term, or pack) to scratchpad (tmux)/last session or back to respective popup
+bind -n M-x run-shell '
+ current_session=$(tmux display-message -p "#S");
+
+ # Check if the current session is one of the popups (note, term, pack)
+ if [ "$current_session" = "note" ] || [ "$current_session" = "term" ] || [ "$current_session" = "pack" ]; then
+ # Detach, break the pane out of the popup, and join it to the last session
+ tmux detach-client;
+ tmux break-pane;
+ last_session=$(tmux show -gvq @last_session_name || echo tmux);
+ tmux set -g @last_popup_session "$current_session"; # Save which popup it was
+ tmux join-pane -s "$current_session" -t "$last_session" -h;
+ else
+ # If not in one of the popups, move the last popup session back to its popup
+ last_popup=$(tmux show -gqv @last_popup_session);
+
+ if [ "$last_popup" = "note" ]; then
+ if ! tmux has-session -t note; then
+ # Spawn note session in detached mode without blocking
+ tmux new-session -d -s note;
+ fi
+ tmux join-pane -s $(tmux display-message -p "#P") -t note;
+ tmux kill-pane -a -t note:0;
+ elif [ "$last_popup" = "term" ]; then
+ if ! tmux has-session -t term; then
+ tmux new-session -d -s term;
+ fi
+ tmux join-pane -s $(tmux display-message -p "#P") -t term;
+ tmux kill-pane -a -t term:0;
+ elif [ "$last_popup" = "pack" ]; then
+ if ! tmux has-session -t pack; then
+ tmux new-session -d -s pack;
+ fi
+ tmux join-pane -s $(tmux display-message -p "#P") -t pack;
+ tmux kill-pane -a -t pack:0;
+ fi
+ fi'
+# TODO: add keybinding that will allow going across the dipslay popup and main tmux session
+# TODO: add keybinding for toggling the display popup size to/from fullscreen and it's original size
+
+# Toggle popup "fzf" session
+#bind-key -n M-o run-shell ~/.config/tmux/fzf-menu.sh
+
+# Toggle popup "htop" session
+bind -n M-i if-shell -F '#{==:#{session_name},htop}' {
+ detach-client
+ } {
+ if-shell "tmux has-session -t HUD" {
+ display-popup -E -x200% -y0 -w40% -h40% "tmux new-session -A -s HUD"
+ } {
+ display-popup -E -x200% -y0 -w40% -h40% "tmux new-session -A -s htop 'htop'"
+ }
+}
+
+# Toggle popup "lazygit" session
+bind-key -n M-g if-shell -F '#{==:#{session_name},lazygit}' {
+ detach-client
+} {
+ display-popup -w 90% -h 90% -E "tmux new-session -A -s lazygit 'lazygit'"
+}
+
+## Toggle popup "yazi" session
+#bind-key -n M-f if-shell -F '#{==:#{session_name},yazi}' {
+# detach-client
+#} {
+# display-popup -w 90% -h 90% -E "tmux new-session -A -s yazi 'yazi'"
+#}
+#tmux split-window -h -p 30 \
+# "NNN_OPENER=~/.config/tmux/file_manager.sh nnn"
+
+#bind-key -n M-f run-shell 'tmux split-window -hb -p 30 "~/.config/tmux/file_manager.sh $(tmux display-message -p "#I.#P")"'
+
+# Robust file manager toggle - checks both title and option
+bind-key -n M-f run-shell "\
+fm_pane_title=\$(tmux list-panes -F '#{pane_id} #{pane_title}' | awk '\$2==\"FILE_MANAGER\" {print \$1}'); \
+fm_pane_option=\$(tmux list-panes -F '#{pane_id} #{@file_manager}' | awk '\$2==\"1\" {print \$1}'); \
+fm_pane=\${fm_pane_title:-\$fm_pane_option}; \
+if [ -n \"\$fm_pane\" ]; then \
+ tmux kill-pane -t \"\$fm_pane\"; \
+else \
+ current_pane=\$(tmux display-message -p '#{session_name}:#{window_index}.#{pane_index}'); \
+ tmux split-window -hb -p 30 \"~/.config/tmux/file_manager.sh \$current_pane\"; \
+fi"
+
+## Toggle popup "speedtest" session
+#bind-key -n M-s if-shell -F '#{==:#{session_name},speedtest}' {
+# detach-client
+#} {
+# display-popup -E "tmux new-session -A -s speedtest 'speedtest'"
+#}
+
+
+# Toggle popup "nvim" session
+bind-key -n M-v if-shell -F '#{==:#{session_name},edit}' {
+ detach-client
+} {
+ display-popup -w 100% -h 100% -E "tmux new-session -A -s edit 'cd && nvim'"
+}
+
+
+# Toggle popup "ssh" session
+bind-key -n M-s if-shell -F '#{==:#{session_name},ssh}' {
+ detach-client
+} {
+ display-popup -w 40% -h 50% -E "tmux new-session -A -s ssh"
+}
+
+# Toggle popup session to fullscreen
+bind-key -n M-F run-shell '
+ popup_session=$(tmux display-message -p "#{session_name}");
+ if [[ "$popup_session" == "term" || "$popup_session" == "note" || "$popup_session" == "lazygit" || "$popup_session" == "yazi" || "$popup_session" == "speedtest" || "$popup_session" == "nvim" || "$popup_session" == "ssh" || "$popup_session" == "pack" || "$popup_session" == "htop" ]]; then
+ tmux detach-client
+ sleep 0.1
+ tmux display-popup -E -w 100% -h 100% "tmux new-session -A -s $popup_session"
+ fi
+'
+
+bind -n M-L lock-session
+
+#bind-key -n M-o display-popup -E nvim -c ":ObsidianNew"
+#bind-key -n M-/ display-popup -w "90%" -h "85%" -E nvim -c ":ObsidianSearch"
+
+
+#proc
+#netw
+#ssh
+
+
+#――――――――――――――――――――――――――――――――――――――――――
+
+### Copy Mode (Copy/Paste) ###
+
+# Set Vi copy mode, use <prefix>[ to enter copy mode
+setw -g mode-keys vi # `<prefix>:list-keys -T copy-mode-vi` to confirm
+
+# super fast way to reach copy-mode and search upwards
+bind-key / copy-mode \; send-key ?
+
+# Alt + Space copy-mode without prefix
+bind-key -n M-c copy-mode
+
+# Shift up/down copy-mode without prefix
+bind-key -n S-Up copy-mode \; send-key Up
+bind-key -n S-Down copy-mode \; send-key Down
+bind-key -n Pageup copy-mode \; send-key Pageup
+bind-key -n Pagedown copy-mode \; send-key Pagedown
+#bind -n Pageup if-shell "$is_vim" "send-keys Pageup" "copy-mode -u"
+#bind -n S-Pageup if-shell "$is_vim" "send-keys Pageup" "copy-mode -u"
+bind -n Pageup if-shell "$is_vim" "send-keys Pageup"
+bind -n S-Pageup if-shell "$is_vim" "send-keys Pageup"
+bind -n S-Pagedown send-keys Pagedown
+
+# Change selection <space> and enter to vi keybinding
+bind-key -T copy-mode-vi 'v' send -X begin-selection
+bind-key -T copy-mode-vi 'y' send -X copy-selection-and-cancel
+bind -T copy-mode-vi Escape send -X cancel
+bind -T copy-mode-vi C-c send -X clear-selection
+bind -T copy-mode-vi C-v send -X begin-selection \; send-keys -X rectangle-toggle
+
+# Unbind any previous 'y' in copy-mode-vi to prevent conflicts
+unbind -T copy-mode-vi 'y'
+
+# Clipboard copy for X11/XWayland
+if-shell -b '[ "$DISPLAY" ] && command -v xclip >/dev/null' "\
+ bind-key -T copy-mode-vi 'y' send-keys -X copy-pipe-and-cancel 'xclip -in -selection clipboard > /dev/null 2>&1'"
+
+# Clipboard copy for Wayland
+if-shell -b '[ \"$WAYLAND_DISPLAY\" ] && command -v wl-copy >/dev/null' "\
+ bind-key -T copy-mode-vi 'y' send-keys -X copy-pipe-and-cancel 'wl-copy --foreground --type text/plain > /dev/null 2>&1'"
+
+# macOS clipboard copy
+if-shell -b '[ \"$(uname -s)\" = \"Darwin\" ] && command -v pbcopy >/dev/null' "\
+ bind-key -T copy-mode-vi 'y' send-keys -X copy-pipe-and-cancel 'pbcopy'"
+
+# Windows (WSL) clipboard copy
+if-shell -b 'uname -r | grep -qi microsoft && command -v clip.exe >/dev/null' "\
+ bind-key -T copy-mode-vi 'y' send-keys -X copy-pipe-and-cancel 'clip.exe > /dev/null 2>&1'"
+
+# For OSC-Yank in vim to work over ssh
+# https://github.com/ojroques/vim-oscyank
+#set -s set-clipboard on
+#set -g set-clipboard on
+#set -g terminal-overrides 'xterm*:paste:Ctrl+Shift+V'
+
+set -g allow-passthrough on
+set extended-keys on
+
+# notify when a window has activity
+set-window-option -g monitor-activity on
+
+# Copy mode search with a simple shortcut (@see https://superuser.com/a/1253137)
+bind-key / copy-mode \; send-key ?
+
+#――――――――――――――――――――――――――――――――――――――――――
+
+### Colors ###
+
+## Assume external terminal supports the 256 colors palette (when TERM=xterm-256color)
+#set -sa terminal-features ",xterm-256color:256"
+## Assume external terminal supports RGB colors (when TERM=xterm-256color)
+#set -sa terminal-features ",xterm-256color:RGB"
+#
+## Set TERM for proper colors
+#set -g default-terminal "tmux-256color"
+#set -g default-terminal "xterm-256color"
+
+# Set 256 color terminal
+#set-option -sa terminal-overrides ",tmux-256color:Tc"
+#set-option -sa terminal-overrides ",xterm*:Tc,alacritty*:Tc"
+
+# Colors for pane borders(default)
+setw -g pane-border-style fg=white
+setw -g pane-active-border-style fg=green
+
+# Active pane normal, other shaded out
+setw -g window-style fg=colour240
+setw -g window-active-style fg=white
+
+# Popup border
+set -g popup-border-lines rounded
+
+#
+
+#――――――――――――――――――――――――――――――――――――――――――
+
+### Status ###
+
+set -g status-position bottom # [top, bottom]
+set -g status on
+#set -g status-interval 1
+set -g status-interval 500
+#set -g status-style fg=#50fa7b,bg=default
+
+# Toggle status
+bind T set status
+
+# Inactive windows
+set -g status-style bg=default
+set -g window-status-activity-style noreverse
+set -g window-status-format "#[fg=#ffffff,bg=default]#I: #W"
+
+## Left
+set -g status-left ""
+set -g status-left-length 60
+
+## Center
+set -g status-justify absolute-centre
+
+## Right
+set -g status-right-length 60
+set-option -g status-right ""
+
+
+### Status Style
+set -g status-style "bg=default,fg=#CDD6F4"
+#set -g status-left-length 100
+#set -g status-right-length 150
+
+### Left
+set -g status-left '#( \
+ if fc-list | grep -qi nerd; then \
+ cat ~/.vi-mode | awk '\''/-- NORMAL --/ {print "#[fg=#39BAE6]#[bg=default]#[bg=#39BAE6,fg=#000000] ❐ #S #[fg=#39BAE6,bg=default]"} \
+ /-- INSERT --/ {print "#[fg=#50fa7b]#[bg=default]#[bg=#50fa7b,fg=#000000] ❐ #S #[fg=#50fa7b,bg=default]"}'\''; \
+ else \
+ cat ~/.vi-mode | awk '\''/-- NORMAL --/ {print "#[bg=default,fg=#39BAE6] #S "} \
+ /-- INSERT --/ {print "#[bg=default,fg=#50fa7b] #S "}'\''; \
+ fi \
+)'
+
+### Center
+set -g window-status-current-format '#( \
+ if fc-list | grep -qi nerd; then \
+ cat ~/.vi-mode | awk '\''/-- NORMAL --/ {print "#[fg=#39BAE6]#[bg=default]#[fg=#000000,bg=#39BAE6] #I:#W #[bg=default,fg=#39BAE6]"} \
+ /-- INSERT --/ {print "#[fg=#50fa7b]#[bg=default]#[fg=#000000,bg=#50fa7b] #I:#W #[bg=default,fg=#50fa7b]"}'\''; \
+ else \
+ cat ~/.vi-mode | awk '\''/-- NORMAL --/ {print "#[fg=#39BAE6,bg=default] #I:#W "} \
+ /-- INSERT --/ {print "#[fg=#50fa7b,bg=default] #I:#W "}'\''; \
+ fi \
+)'
+
+### Right
+set -g status-right '#( \
+ key_off="#[fg=#50fa7b,bg=default]#([ $(tmux show-option -qv key-table) = off ] && echo KEYS\ OFF)#[default]"; \
+ vi_segment=$(cat ~/.vi-mode | awk '\''/-- NORMAL --/ {print "#[fg=#50fa7b,bg=default] #[fg=#39BAE6]#[bg=#39BAE6,fg=#000000] %H:%M #[bg=default,fg=#39BAE6]"} \
+ /-- INSERT --/ {print "#[fg=#39BAE6,bg=default] #[fg=#50fa7b]#[bg=#50fa7b,fg=#000000] %H:%M #[bg=default,fg=#50fa7b]"}'\'')
+
+ if fc-list | grep -qi nerd; then \
+ echo "$key_off $vi_segment"; \
+ else \
+ cat ~/.vi-mode | awk '\''/-- NORMAL --/ {print "#[fg=#50fa7b,bg=default] #[fg=#39BAE6] %H:%M "} \
+ /-- INSERT --/ {print "#[fg=#39BAE6,bg=default] #[fg=#50fa7b] %H:%M "}'\''; \
+ fi \
+)'
+
+# Force refresh after session creation
+#set-hook -g after-new-session 'source-file ~/.tmux.conf'
+#set-hook -g client-attached 'source-file ~/.tmux.conf'
+
+
+# Set environment variable from script output
+#run-shell 'tmux set-environment -g NERD_FONT_DETECTED "$(~/.config/tmux/detect_nerd_font)"'
+
+## Reload Status with IP addr, Cpu, Mem and Date
+bind a run-shell ~/.config/tmux/tmux-toggle-option.sh
+
+## Numbers
+bind < command-prompt -p index "run-shell '~/.config/tmux/tmux_number.sh %%'"
+
+## Reload Config/Status silently
+bind b source-file ~/.config/tmux/tmux.conf
+
+set-option -g default-shell "/usr/bin/zsh"
+
+
+#――――――――――――――――――――――――――――――――――――――――――
+
+### Plugin Install ###
+
+set -g @plugin "tmux-plugins/tpm"
+set -g @plugin "tmux-plugins/tmux-sensible"
+set -g @plugin "tmux-plugins/tmux-resurrect"
+set -g @plugin "tmux-plugins/tmux-continuum"
+#set -g @plugin "loichyan/tmux-toggle-popup"
+set -g @plugin "christoomey/vim-tmux-navigator"
+#set -g @plugin "tmux-plugins/tmux-yank"
+#set -g @plugin 'srdusr/tmux-vi-mode'
+#set -g @plugin 'vi-mode ~/.config/tmux/plugins/vi-mode.sh'
+
+
+set -g @plugin 'catppuccin/tmux'
+set -g @plugin 'tmux-plugins/tmux-online-status'
+set -g @plugin 'tmux-plugins/tmux-battery'
+
+#――――――――――――――――――――――――――――――――――――――――――
+
+### Plugins Settings ###
+
+# Plugin to save and restore tmux sessions after restart
+# * Save with: <Prefix> + Ctrl-s
+# * Restore with: <Prefix> + Ctlr-r
+# Change default save and restore keybindings
+ set -g @resurrect-save "W" # <Prefix> + W
+ set -g @resurrect-restore "E" # <Prefix> + E
+
+# Restore vim and nvim sessions as well
+# For vim:
+ set -g @resurrect-strategy-vim "session"
+# For neovim:
+ set -g @resurrect-strategy-nvim "session"
+
+# Automatic restore
+ #set -g @continuum-restore "on"
+ #set -g @continuum-boot "on"
+
+# Restore Panes
+# set -g @resurrect-capture-pane-contents "on"
+
+# This is a hook for tmux-resurrect which tells it to kill session 0 before restoring the panels
+ set -g @resurrect-hook-pre-restore-pane-processes "tmux switch-client -n && tmux kill-session -t=0"
+
+# Tmux navigation
+ #set -g @plugin 'christoomey/vim-tmux-navigator'
+# Plugin uninstall
+ # Delete or comment out the plugin in .tmux.conf.
+ # Press <prefix> + alt + u to remove the plugin.
+
+# >>>>> CATPPUCCIN CONFIGS <<<<<
+
+# Configure Catppuccin
+set -g @catppuccin_flavor "mocha"
+set -g @catppuccin_status_background "none"
+set -g @catppuccin_window_status_style "none"
+set -g @catppuccin_pane_status_enabled "off"
+set -g @catppuccin_pane_border_status "off"
+
+# Configure Online status
+set -g @online_icon "ok"
+set -g @offline_icon "nok"
+
+# TMUX plugin manager (keep at the bottom of tmux.conf)
+ #run "~/.config/tmux/plugins/tpm/tpm"
+ if "test ! -d ~/.config/tmux/plugins/tpm" \
+ "run 'git clone https://github.com/tmux-plugins/tpm ~/.config/tmux/plugins/tpm && ~/.config/tmux/plugins/tpm/bin/install_plugins'"
+run -b "~/.config/tmux/plugins/tpm/tpm"
diff --git a/linux/home/.config/tmux/tmux_number.sh b/linux/home/.config/tmux/tmux_number.sh
new file mode 100755
index 0000000..5d239d0
--- /dev/null
+++ b/linux/home/.config/tmux/tmux_number.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -x
+dest="$1"
+[ "x""$dest" != "x" ]
+tmux list-windows -F "#{window_index}" | grep "^${dest}$" 2>&1 >/dev/null
+ret="$?"
+if [ "x""$ret" = "x0" ]; then
+ tmux swap-window -t ":${dest}"
+else
+ tmux move-window -t ":${dest}"
+fi
diff --git a/linux/home/.config/tridactyl/tridactylrc b/linux/home/.config/tridactyl/tridactylrc
new file mode 100644
index 0000000..a9547cc
--- /dev/null
+++ b/linux/home/.config/tridactyl/tridactylrc
@@ -0,0 +1,170 @@
+" -*- vimrc-generic -*-
+"
+" Base on
+" https://raw.githubusercontent.com/tridactyl/tridactyl/master/.tridactylrc
+" See that for more advanced examples.
+
+" Installing Tridactyl:
+"
+" * Put this config in ~/.tridactylrc (or $XDG_CONFIG_DIR/tridactyl/tridactylrc).
+"
+" * Install the native messenger by running :installnative in Tridactyl
+" and then running the shell command it copies to clipboard.
+"
+" * Run :source in the browser or just restart.
+
+" NB: If you want "vim-like" behaviour where removing a line from
+" here makes the setting disappear, uncomment the line below.
+"
+"sanitise tridactyllocal tridactylsync
+
+" Use this to see current config in the browser
+" :viewconfig --user
+
+" WARNING: THERE IS A BUG WHEREBY SOMETIMES SOME LINES IN THE CONFIG GET IGNORED :/
+" https://github.com/tridactyl/tridactyl/issues/1409
+
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+"
+" Search Urls
+"
+" These work in the 'o' -> ':open ' dialog, but not in the regular
+" address bar.
+"
+" In addition to using %s for a single query param, you can use %1,
+" %2, etc, for multiple query params.
+
+"set searchurls.hackage http://hackage.haskell.org/package/%s
+"set searchurls.hayoo http://hayoo.fh-wedel.de/?query=%s
+"set searchurls.h4 https://www.haskell.org/hoogle/?hoogle=%s
+"set searchurls.h5 https://hoogle.haskell.org/?hoogle=%s&scope=set%3Astackage
+
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+"
+" Quickmarks
+"
+" Use go<key>/gn<key>/gw<key> to open quickmark for <key> in
+" current/new tab/new window
+
+quickmark g https://mail.google.com/mail/u/0/#inbox
+quickmark G https://mail.google.com/mail/u/1/#inbox
+
+""""""""""""""""
+"
+" Disable on some sites
+"
+
+"blacklistadd youtube.com
+blacklistadd calendar.google.com
+blacklistadd docs.google.com
+blacklistadd drive.google.com
+blacklistadd keep.google.com
+blacklistadd mail.google.com
+blacklistadd monkeytype.com
+blacklistadd typeracer.com
+blacklistadd codepen.io
+blacklistadd codesandbox.io
+"blacklistadd github.dev
+
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+"
+" Binds
+"
+" Some defaults:
+" - :help
+" - :tutor
+" - b : buffers/tabs navigation
+" - yy : yank url
+" - gg : top of page
+" - G : bottom of page
+" - insert, shift+insert, ctrl+alt+`, shift+escape : toggle pass thru
+" - d/D : close current tab and move focus right/left
+" - p/P : open url in clipboard or search for clipboard content in current/new tab
+" - zi/zo/zz : zoom in/out/reset
+" - \[\[ / \]\] : guess previous/next page (seems smart, e.g. works when
+" url does not end in a number, but "next" occurs in link text)
+" - ;; : focus an element. Useful if you want to scroll something non-default with up/down or j/k
+" - ;p : copy element (e.g. link, paragraph) to clipboard
+" - ;k : kill element (e.g. a big "please disable your ad blocker" banner)
+" - :viewconfig nmaps : see all key bindings (but how to delete?)
+" - f/;t/F: follow hint in current tab/new foreground tab (switch focus)/new background tab (don't switch focus)
+" - C-o : run a single command (in normal mode) when in ignore mode, then switch back to ignore mode
+
+" Make 'd' switch to previous active tab after close
+bind d composite tabclose | buffer #
+
+" Don't bind paste to ignore mode. Can use 'S-Esc' or 'C-A-Esc' instead.
+unbind <S-Insert>
+
+" Don't bind 'f' in youtube
+unbindurl youtube\.com/watch\?v=.* f
+
+" Bind <Insert> to toggle normal/ignore mode. I use <Insert> to toggle
+" Ctrl-lock in Emacs, so hopefully this will be memorable. We leave
+" insert mode, but don't enter it. The insert mode bind doesn't work,
+" but <C-,> still allows escape.
+bind --mode=normal <Insert> mode ignore
+bind --mode=ignore <Insert> mode normal
+"bind --mode=insert <Insert> mode normal
+
+" Bind <C-o> to toggle normal/ignore mode for one command. Already
+" bound to C-o in normal mode, make it work everywhere. I'm rebinding
+" the existing normal mode bind for completeness/clairity. We leave
+" insert mode, but don't enter. The insert mode bind doesn't work, but
+" <C-,> still allows escape.
+bind --mode=normal <C-o> nmode normal 1 mode ignore
+bind --mode=ignore <C-o> nmode ignore 1 mode normal
+"bind --mode=insert <C-o> nmode insert 1 mode normal
+
+bind / fillcmdline find
+bind n findnext 1
+bind N findnext -1
+"bind <Space>/ nohlsearch
+set findcase smart
+
+" Workaround bug on web.whatsapp.com that prevents focus from leaving
+" message entry
+"" box. https://github.com/tridactyl/tridactyl/issues/3070
+"bindurl web.whatsapp.com --mode=normal <Escape> composite hint -f m2 ; fillcmdline ; ex.hide_and_clear
+"bindurl web.whatsapp.com --mode=insert <Escape> composite hint -f m2 ; fillcmdline ; ex.hide_and_clear
+"bindurl web.whatsapp.com --mode=input <Escape> composite hint -f m2 ; fillcmdline ; ex.hide_and_clear
+
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+"
+" Misc settings
+"
+
+" but make sure i can always get into the console"
+bind : fillcmdline_notrail
+
+" newtab
+set newtab about:blank
+
+" I’m a smooth operator
+set smoothscroll true
+
+" Vimperator-style hinting, using numbers to select and letters to
+" narrow, instead of just letters to select.
+set hintfiltermode vimperator-reflow
+set hintnames numeric
+" " I use Programmer Dvorak
+" set hintchars dhtnaoeuifgcrl',.pybm;qjkx
+
+" Defaults to 300ms
+set hintdelay 100
+
+" Don't autofocus!
+autocmd TabEnter .* unfocus
+autocmd DocLoad .* unfocus
+
+
+" Include numbers in tab names, to make 'b' and '<A-<number>>'
+" switching easier.
+guiset tabs numbers
+
+" Make Tridactyl work on more sites at the expense of some security
+" set csp clobber
+" fixamo_quiet
+
+" This will have to do until someone writes us a nice syntax file :)
+" vim: set filetype=vim:
diff --git a/linux/home/.config/user-dirs.dirs b/linux/home/.config/user-dirs.dirs
new file mode 100644
index 0000000..0db0cae
--- /dev/null
+++ b/linux/home/.config/user-dirs.dirs
@@ -0,0 +1,15 @@
+# This file is written by xdg-user-dirs-update
+# If you want to change or add directories, just edit the line you're
+# interested in. All local changes will be retained on the next run.
+# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
+# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
+# absolute path. No other format is supported.
+#
+XDG_DESKTOP_DIR="$HOME/"
+XDG_DOWNLOAD_DIR="$HOME/downloads"
+XDG_TEMPLATES_DIR="$HOME/"
+XDG_PUBLICSHARE_DIR="$HOME/"
+XDG_DOCUMENTS_DIR="$HOME/documents"
+XDG_MUSIC_DIR="$HOME/music"
+XDG_PICTURES_DIR="$HOME/pictures"
+XDG_VIDEOS_DIR="$HOME/videos"
diff --git a/linux/home/.config/user-dirs.locale b/linux/home/.config/user-dirs.locale
new file mode 100644
index 0000000..3e0b419
--- /dev/null
+++ b/linux/home/.config/user-dirs.locale
@@ -0,0 +1 @@
+en_US \ No newline at end of file
diff --git a/linux/home/.config/waybar/config.jsonc b/linux/home/.config/waybar/config.jsonc
new file mode 100644
index 0000000..dbf6556
--- /dev/null
+++ b/linux/home/.config/waybar/config.jsonc
@@ -0,0 +1,292 @@
+{
+ "layer": "top", // Waybar at top layer
+ "position": "top", // Waybar position (top|bottom|left|right)
+ //"gtk-layer-shell": "false",
+ "height": 45, // Waybar height (to be removed for auto height)
+ // "width": 2560, // Waybar width
+ "spacing": 0, // Gaps between modules (0px) Adjusted in the css
+ "margin-top": 0,
+ //"margin-bottom":-10,
+ "margin-left": 10,
+ "margin-right": 10,
+ // Choose the order of the modules
+ "modules-left": [
+ "custom/launcher",
+ "wlr/workspaces",
+ "cpu",
+ "memory",
+ "temperature",
+ "disk",
+ "custom/updates"
+ ],
+ "modules-center": [
+ "custom/music"
+ ],
+ "modules-right": [
+ "network",
+ "pulseaudio",
+ "backlight",
+ "battery",
+ "custom/notifications",
+ "tray",
+ "clock",
+ "custom/weather",
+ "custom/cycle_wall",
+ "custom/clipboard",
+ "custom/power",
+ "custom/custom"
+ ],
+ "custom/launcher": {
+ "format": "{}",
+ "tooltip": true,
+ "exec": "echo '{\"text\":\"💧\",\"tooltip\":\"Drun | Run\"}'",
+ "return-type": "json",
+ "on-click": "pkill wofi || wofi --show drun -n",
+ "on-click-right": "pkill wofi || wofi --show run -n",
+ },
+ "wlr/workspaces": {
+ // "format": "{icon} {name}",
+ "format": "",
+ "format-icons": {
+ "active": "",
+ "default": ""
+ },
+ "on-scroll-up": "hyprctl dispatch workspace e-1",
+ "on-scroll-down": "hyprctl dispatch workspace e+1",
+ "on-click": "activate"
+ },
+ "cpu": {
+ "format": " {usage}%",
+ "tooltip": true,
+ "on-click": "kitty --start-as=fullscreen --title all_is_kitty sh -c 'btop'",
+ "interval": 2
+ },
+ "memory": {
+ "format": " {}%",
+ "tooltip": true,
+ "on-click": "kitty --start-as=fullscreen --title all_is_kitty sh -c 'btop'",
+ "interval": 2
+ },
+ "temperature": {
+ // "thermal-zone": 2,
+ // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input",
+ "critical-threshold": 40,
+ "format-critical": "{icon} {temperatureC}°C",
+ "format": "{icon} {temperatureC}°C",
+ "format-icons": [
+ "",
+ "",
+ ""
+ ],
+ "tooltip": true,
+ "on-click": "kitty --start-as=fullscreen --title all_is_kitty sh -c 'btop'",
+ "interval": 2
+ },
+ "disk": {
+ "format": " {percentage_used}% ({free})",
+ "tooltip": true,
+ "on-click": "kitty --start-as=fullscreen --title all_is_kitty sh -c 'btop'",
+ "interval": 2
+ },
+ "custom/updates": {
+ "format": "{}",
+ "exec": "~/.scripts/updates",
+ "on-click": "~/.scripts/updates update",
+ "interval": 300,
+ "tooltip": true,
+ // "tooltip-format": "{}",
+ // "exec-tooltip": "~/.scripts/updates tooltip"
+ },
+ "custom/music": {
+ "format": "{icon}{}",
+ "format-icons": {
+ // "Playing": " ", // Uncomment if not using the dynamic script
+ "Paused": " ",
+ "Stopped": "&#x202d;ﭥ " // This stop symbol is RTL. So &#x202d; is left-to-right override.
+ },
+ "escape": true,
+ "tooltip": true,
+ "exec": "~/.scripts/caway -b 10",
+ "return-type": "json",
+ "on-click": "playerctl play-pause",
+ "on-scroll-up": "playerctl previous",
+ "on-scroll-down": "playerctl next",
+ "on-click-right": "g4music",
+ "max-length": 35
+ },
+ "hyprland/window": {
+ "format": "{}",
+ "separate-outputs": true,
+ "max-length": 35
+ },
+ "network": {
+ // "interface": "wlp2*", // (Optional) To force the use of this interface
+ "format": "↕{bandwidthTotalBytes}",
+ "format-disconnected": "{icon} No Internet",
+ "format-linked": " {ifname} (No IP)",
+ "format-alt": "↕{bandwidthUpBytes} | ↕{bandwidthDownBytes}",
+ "tooltip-format": "{ifname}: {ipaddr}/{cidr}  {gwaddr}",
+ "tooltip-format-wifi": "{icon} {essid} ({signalStrength}%)",
+ "tooltip-format-ethernet": "{icon} {ipaddr}/{cidr}",
+ "tooltip-format-disconnected": "{icon} Disconnected",
+ "on-click-right": "nm-connection-editor",
+ "format-icons": {
+ "ethernet": "",
+ "disconnected": "⚠",
+ "wifi": [
+ "睊",
+ "直"
+ ]
+ },
+ "interval": 2
+ },
+ "pulseaudio": {
+ // "scroll-step": 1, // %, can be a float
+ "format": "{icon} {volume}%", // {format_source}
+ "format-bluetooth": "{icon} {volume}%", // {format_source}
+ "format-bluetooth-muted": "", // {format_source}
+ "format-muted": "", // {format_source}
+ "format-source": "{volume}% ",
+ "format-source-muted": "",
+ "format-icons": {
+ "headphone": "",
+ "headset": "",
+ "phone": "",
+ "portable": "",
+ "car": " ",
+ "default": [
+ "",
+ "",
+ ""
+ ]
+ },
+ "on-click": "pavucontrol"
+ },
+ "backlight": {
+ // "device": "acpi_video1",
+ "format": "{icon} {percent}%",
+ "format-icons": [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ ],
+ "on-scroll-up": "light -A 1",
+ "on-scroll-down": "light -U 1",
+ "interval": 2
+ },
+ "battery": {
+ "states": {
+ "good": 100,
+ "warning": 30,
+ "critical": 10
+ },
+ "format": "{icon} {capacity}%",
+ "format-charging": " {capacity}%",
+ "format-plugged": " {capacity}%",
+ "format-alt": "{icon} {time}",
+ // "format-good": "", // An empty format will hide the module
+ "format-full": " {capacity}%",
+ "format-icons": [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+ ],
+ "interval": 2
+ },
+ "custom/notifications": {
+ "tooltip": false,
+ "format": "{icon}",
+ "format-icons": {
+ "notification": "<span foreground='red'><sup></sup></span>",
+ "none": "",
+ "dnd-notification": "<span foreground='red'><sup></sup></span>",
+ "dnd-none": ""
+ },
+ "return-type": "json",
+ "exec-if": "which swaync-client",
+ "exec": "swaync-client -swb",
+ "on-click": "swaync-client -t -sw",
+ "on-click-right": "swaync-client -d -sw",
+ "escape": true
+ },
+ "tray": {
+ "icon-size": 15,
+ "spacing": 15
+ },
+ "clock": {
+ "timezone": "Asia/Calcutta",
+ "format": " {:%d <small>%a</small> %H:%M}",
+ //"format": " {:%a %b %d %Y | %H:%M}",
+ "format-alt": " {:%A %B %d %Y (%V) | %r}",
+ "tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
+ "calendar-weeks-pos": "right",
+ "today-format": "<span color='#f38ba8'><b><u>{}</u></b></span>",
+ "format-calendar": "<span color='#f2cdcd'><b>{}</b></span>",
+ "format-calendar-weeks": "<span color='#94e2d5'><b>W{:%U}</b></span>",
+ "format-calendar-weekdays": "<span color='#f9e2af'><b>{}</b></span>",
+ "interval": 60
+ },
+ "idle_inhibitor": {
+ "format": "{icon}",
+ "format-icons": {
+ "activated": "",
+ "deactivated": ""
+ }
+ },
+ "custom/weather": {
+ "tooltip": true,
+ "format": "{}",
+ "exec": "~/.scripts/tools/expand weather",
+ "return-type": "json"
+ },
+ "custom/ss": {
+ "format": "{}",
+ "exec": "~/./scripts/tools/expand ss-icon",
+ "return-type": "json",
+ "on-click": "~/.scripts/screenshot_full"
+ },
+ "custom/cycle_wall": {
+ "format": "{}",
+ "exec": "~/.scripts/tools/expand wall",
+ "return-type": "json",
+ // "interval": 1,
+ "on-click": "~/.scripts/tools/expand cycle",
+ "on-click-right": "~/.scripts/tools/expand cycler"
+ },
+ "custom/clipboard": {
+ "format": "{}",
+ "exec": "~/.scripts/tools/expand clipboard",
+ "return-type": "json",
+ // Here "-l top_right -x -15 -y 10" doesn't matter as '-n' mode is used
+ // Window position is managed in Hyperland config's windowrulev2
+ "on-click": "pkill wofi || cliphist list | wofi --dmenu -p clippick -l top_right -x -15 -y 10 -n | cliphist decode | wl-copy",
+ "on-click-middle": "rm -f ~/.cache/cliphist/db",
+ "on-click-right": "pkill wofi || cliphist list | wofi --dmenu -p clippick -l top_right -x -15 -y 10 -n | cliphist delete",
+ "escape": true
+ },
+ "custom/power": {
+ "format": "{}",
+ "exec": "~/.scripts/tools/expand power",
+ "return-type": "json",
+ "on-click": "~/.config/wlogout/launch.sh"
+ },
+ "custom/custom": {
+ "format": "{}",
+ "exec": "~/.scripts/tools/expand arrow-icon",
+ "on-click": "~/.scripts/tools/expand_toolbar",
+ "return-type": "json"
+ }
+}
diff --git a/linux/home/.config/waybar/style.css b/linux/home/.config/waybar/style.css
new file mode 100644
index 0000000..348bb04
--- /dev/null
+++ b/linux/home/.config/waybar/style.css
@@ -0,0 +1,434 @@
+/*
+@import "catppuccin/mocha.css";
+*/
+
+/*
+* Catppuccin Mocha palette
+*/
+
+@define-color base #1e1e2e;
+@define-color mantle #181825;
+@define-color crust #11111b;
+
+@define-color text #cdd6f4;
+@define-color subtext0 #a6adc8;
+@define-color subtext1 #bac2de;
+
+@define-color surface0 rgba(22, 25, 37, 0.9);
+@define-color surface1 #45475a;
+@define-color surface2 #585b70;
+@define-color surface3 #394161;
+
+@define-color overlay0 #6c7086;
+@define-color overlay1 #7f849c;
+@define-color overlay2 #9ba3c3;
+
+@define-color blue #89b4fa;
+@define-color lavender #b4befe;
+@define-color sapphire #74c7ec;
+@define-color sky #89dceb;
+@define-color teal #94e2d5;
+@define-color green #a6e3a1;
+@define-color yellow #f9e2af;
+@define-color peach #fab387;
+@define-color maroon #eba0ac;
+@define-color red #f38ba8;
+@define-color mauve #cba6f7;
+@define-color pink #f5c2e7;
+@define-color flamingo #f2cdcd;
+@define-color rosewater #f5e0dc;
+
+/* =============================== */
+/* Universal Styling */
+* {
+ border: none;
+ border-radius: 0;
+ font-family: 'CaskaydiaCove Nerd Font', monospace;
+ font-size: 13px;
+ min-height: 0;
+}
+
+/* =============================== */
+
+
+/* =============================== */
+/* Bar Styling */
+#waybar {
+ background: transparent;
+ color: @text;
+}
+
+/* =============================== */
+
+
+/* =============================== */
+/* Main Modules */
+#custom-launcher,
+#workspaces,
+#window,
+#tray,
+#backlight,
+#clock,
+#battery,
+#pulseaudio,
+#network,
+#mpd,
+#cpu,
+#memory,
+#disk,
+#temperature,
+#custom-music,
+#custom-updates,
+#custom-nordvpn,
+#custom-notifications,
+#custom-power,
+#custom-custom,
+#custom-cycle_wall,
+#custom-clipboard,
+#custom-ss,
+#custom-weather {
+ background-color: @surface0;
+ color: @text;
+ border-radius: 16px;
+ padding: 0.5rem 1rem;
+ box-shadow: rgba(0, 0, 0, 0.116) 2px 2px 5px 2px;
+ margin-top: 10px;
+ /*
+ margin-bottom: 10px;
+*/
+ margin-right: 10px;
+}
+
+/* =============================== */
+/* Launcher Module */
+#custom-launcher {
+ color: @green;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ padding-right: 10px;
+}
+
+/* =============================== */
+/* Workspaces */
+#workspaces {
+ padding-left: 8px;
+ padding-right: 8px;
+}
+
+#workspaces * {
+ font-size: 0px;
+}
+
+#workspaces button {
+ background-color: @surface3;
+ color: @mauve;
+ border-radius: 100%;
+ min-height: 14px;
+ min-width: 14px;
+ margin: 5px 8px;
+ padding: 0px;
+ /*transition: all 0.5s cubic-bezier(0.33, 1.0, 0.68, 1.0); easeInOutCubic */
+ transition: all 0.5s cubic-bezier(.55, -0.68, .48, 1.68);
+ box-shadow: rgba(0, 0, 0, 0.288) 2px 2px 5px 2px;
+}
+
+#workspaces button.active {
+ /*color: @surface0;
+ border-radius: 1rem;
+ padding: 0rem 10px;*/
+ background: radial-gradient(circle, rgba(203, 166, 247, 1) 0%, rgba(193, 168, 247, 1) 12%, rgba(249, 226, 175, 1) 19%, rgba(189, 169, 247, 1) 20%, rgba(182, 171, 247, 1) 24%, rgba(198, 255, 194, 1) 36%, rgba(177, 172, 247, 1) 37%, rgba(170, 173, 248, 1) 48%, rgba(255, 255, 255, 1) 52%, rgba(166, 174, 248, 1) 52%, rgba(160, 175, 248, 1) 59%, rgba(148, 226, 213, 1) 66%, rgba(155, 176, 248, 1) 67%, rgba(152, 177, 248, 1) 68%, rgba(205, 214, 244, 1) 77%, rgba(148, 178, 249, 1) 78%, rgba(144, 179, 250, 1) 82%, rgba(180, 190, 254, 1) 83%, rgba(141, 179, 250, 1) 90%, rgba(137, 180, 250, 1) 100%);
+ background-size: 400% 400%;
+ animation: gradient_f 20s ease-in-out infinite;
+ transition: all 0.3s cubic-bezier(.55, -0.68, .48, 1.682);
+}
+
+#workspaces button:hover {
+ background-color: @mauve;
+}
+
+@keyframes gradient {
+ 0% {
+ background-position: 0% 50%;
+ }
+
+ 50% {
+ background-position: 100% 30%;
+ }
+
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+
+@keyframes gradient_f {
+ 0% {
+ background-position: 0% 200%;
+ }
+
+ 50% {
+ background-position: 200% 0%;
+ }
+
+ 100% {
+ background-position: 400% 200%;
+ }
+}
+
+@keyframes gradient_f_nh {
+ 0% {
+ background-position: 0% 200%;
+ }
+
+ 100% {
+ background-position: 200% 200%;
+ }
+}
+
+/* =============================== */
+
+
+/* =============================== */
+/* System Monitoring Modules */
+#cpu,
+#memory,
+#temperature {
+ color: @blue;
+}
+
+#cpu {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ margin-right: 0px;
+ padding-right: 5px;
+}
+
+#memory {
+ border-radius: 0px;
+ margin-right: 0px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+#temperature {
+ border-radius: 0px;
+ margin-right: 0px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+#disk {
+ color: @peach;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ padding-left: 5px;
+ padding-right: 1rem;
+}
+
+/* Updates Module */
+#custom-updates {
+ color: @sky;
+}
+
+/* =============================== */
+
+
+/* =============================== */
+/* Clock Module */
+#clock {
+ color: @flamingo;
+}
+
+/* =============================== */
+
+
+#custom-music.low {
+ background: rgb(148, 226, 213);
+ background: linear-gradient(52deg, rgba(148, 226, 213, 1) 0%, rgba(137, 220, 235, 1) 19%, rgba(116, 199, 236, 1) 43%, rgba(137, 180, 250, 1) 56%, rgba(180, 190, 254, 1) 80%, rgba(186, 187, 241, 1) 100%);
+ background-size: 300% 300%;
+ text-shadow: 0px 0px 5px rgba(0, 0, 0, 0.377);
+ animation: gradient 15s ease infinite;
+ font-weight: bold;
+ color: #fff;
+}
+
+#custom-music.random {
+ background: rgb(148, 226, 213);
+ background: radial-gradient(circle, rgba(148, 226, 213, 1) 0%, rgba(156, 227, 191, 1) 21%, rgba(249, 226, 175, 1) 34%, rgba(158, 227, 186, 1) 35%, rgba(163, 227, 169, 1) 59%, rgba(148, 226, 213, 1) 74%, rgba(164, 227, 167, 1) 74%, rgba(166, 227, 161, 1) 100%);
+ background-size: 400% 400%;
+ animation: gradient_f 4s ease infinite;
+ text-shadow: 0px 0px 5px rgba(0, 0, 0, 0.377);
+ font-weight: bold;
+ color: #fff;
+}
+
+#custom-music.critical {
+ background: rgb(235, 160, 172);
+ background: linear-gradient(52deg, rgba(235, 160, 172, 1) 0%, rgba(243, 139, 168, 1) 30%, rgba(231, 130, 132, 1) 48%, rgba(250, 179, 135, 1) 77%, rgba(249, 226, 175, 1) 100%);
+ background-size: 300% 300%;
+ animation: gradient 15s cubic-bezier(.55, -0.68, .48, 1.68) infinite;
+ text-shadow: 0px 0px 5px rgba(0, 0, 0, 0.377);
+ font-weight: bold;
+ color: #fff;
+}
+
+#custom-music.Playing {
+ background: rgb(137, 180, 250);
+ background: radial-gradient(circle, rgba(137, 180, 250, 120) 0%, rgba(142, 179, 250, 120) 6%, rgba(148, 226, 213, 1) 14%, rgba(147, 178, 250, 1) 14%, rgba(155, 176, 249, 1) 18%, rgba(245, 194, 231, 1) 28%, rgba(158, 175, 249, 1) 28%, rgba(181, 170, 248, 1) 58%, rgba(205, 214, 244, 1) 69%, rgba(186, 169, 248, 1) 69%, rgba(195, 167, 247, 1) 72%, rgba(137, 220, 235, 1) 73%, rgba(198, 167, 247, 1) 78%, rgba(203, 166, 247, 1) 100%);
+ background-size: 400% 400%;
+ animation: gradient_f 9s cubic-bezier(.72, .39, .21, 1) infinite;
+ text-shadow: 0px 0px 5px rgba(0, 0, 0, 0.377);
+ font-weight: bold;
+ color: #fff;
+}
+
+#custom-music.Paused,
+#custom-music.Stopped {
+ background: @surface0;
+}
+
+
+/* =============================== */
+/* Music/PlayerCTL Module */
+#custom-music {
+ color: @mauve;
+}
+
+/* =============================== */
+
+
+/* =============================== */
+/* Network Module */
+#network {
+ color: @blue;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ margin-right: 0px;
+ padding-right: 5px;
+}
+
+/* =============================== */
+
+
+/* =============================== */
+/* PulseAudio Module */
+#pulseaudio {
+ color: @mauve;
+ border-radius: 0;
+ margin-right: 0px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+/* =============================== */
+
+
+/* =============================== */
+/* Backlight Module */
+#backlight {
+ color: @teal;
+ border-radius: 0;
+ margin-right: 0px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+/* =============================== */
+
+
+/* =============================== */
+/* Battery Module */
+#battery {
+ color: @green;
+ border-radius: 0;
+ margin-right: 0px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+#battery.charging {
+ color: @green;
+}
+
+#battery.warning:not(.charging) {
+ color: @maroon;
+}
+
+#battery.critical:not(.charging) {
+ color: @red;
+ animation-name: blink;
+ animation-duration: 1s;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ animation-direction: alternate;
+}
+
+@keyframes blink {
+ to {
+ background: @red;
+ color: @surface1;
+ }
+}
+
+/* =============================== */
+
+/* Notifications Module */
+#custom-notifications {
+ color: @mauve;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ padding-left: 5px;
+ padding-right: 1.25rem;
+}
+
+
+/* =============================== */
+/* Tray Module */
+#tray {
+ color: @mauve;
+ padding-right: 1.25rem;
+}
+
+/* =============================== */
+
+
+/* =============================== */
+/* | Custom Modules | */
+/* =============================== */
+#custom-custom {
+ color: @peach;
+ padding-right: 1.25rem;
+ margin-right: 0px;
+}
+
+/* Screenshot */
+#custom-ss {
+ color: @mauve;
+ padding-right: 1.5rem;
+}
+
+/* Wallpaper */
+#custom-cycle_wall {
+ background: linear-gradient(45deg, rgba(245, 194, 231, 1) 0%, rgba(203, 166, 247, 1) 0%, rgba(243, 139, 168, 1) 13%, rgba(235, 160, 172, 1) 26%, rgba(250, 179, 135, 1) 34%, rgba(249, 226, 175, 1) 49%, rgba(166, 227, 161, 1) 65%, rgba(148, 226, 213, 1) 77%, rgba(137, 220, 235, 1) 82%, rgba(116, 199, 236, 1) 88%, rgba(137, 180, 250, 1) 95%);
+ background-size: 500% 500%;
+ animation: gradient 7s linear infinite;
+}
+
+/* Notifications Module */
+#custom-clipboard {
+ color: @mauve;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ margin-right: 0px;
+ padding-right: 8px;
+}
+
+/* Powermenu Module */
+#custom-power {
+ color: @mauve;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ padding-left: 8px;
+ padding-right: 1.20rem;
+}
+
+/* =============================== */
diff --git a/linux/home/.config/wofi/config b/linux/home/.config/wofi/config
new file mode 100644
index 0000000..d1ea4c8
--- /dev/null
+++ b/linux/home/.config/wofi/config
@@ -0,0 +1,17 @@
+width=420
+height=550
+location=center
+show=drun
+matching=fuzzy
+prompt=Search...
+filter_rate=100
+allow_markup=true
+no_actions=true
+halign=fill
+orientation=vertical
+content_halign=fill
+insensitive=true
+allow_images=true
+image_size=28
+gtk_dark=false
+term=kitty
diff --git a/linux/home/.config/wofi/style.css b/linux/home/.config/wofi/style.css
new file mode 100644
index 0000000..6fb5107
--- /dev/null
+++ b/linux/home/.config/wofi/style.css
@@ -0,0 +1,99 @@
+* {
+ transition: 0.2s;
+}
+
+window {
+ font-family: "FiraCode Nerd Font Mono";
+ font-size: 13px;
+}
+
+window {
+ margin: 0px;
+ border: 2px solid #cba6f7;
+/*
+ background-color: #161925;
+*/
+ background-color: transparent;
+ border-radius: 16px;
+}
+
+#input {
+ padding: 4px;
+ margin: 20px;
+ padding-left: 20px;
+ border: none;
+ color: #fff;
+ font-weight: bold;
+ background: linear-gradient(90deg, #cba6f7 0%, #94e2d5 100%);
+ outline: none;
+ border-radius: 16px;
+}
+
+#input image {
+ color: #fff;
+}
+
+#input:focus {
+ border: none;
+ outline: none;
+}
+
+#inner-box {
+ margin: 20px;
+ margin-top: 0px;
+ border: none;
+ color: #cba6f7;
+ border-radius: 16px;
+}
+
+#inner-box * {
+ transition: none;
+}
+
+#outer-box {
+ margin: 0px;
+ border: none;
+ padding: 0px;
+ border-radius: 16px;
+}
+
+#scroll {
+ margin-top: 5px;
+ border: none;
+ border-radius: 16px;
+ margin-bottom: 5px;
+}
+
+#text:selected {
+ color: #fff;
+ font-weight: bold;
+}
+
+#img {
+ margin-right: 20px;
+ background: transparent;
+}
+
+#text {
+ margin: 0px;
+ border: none;
+ padding: 0px;
+ background: transparent;
+}
+
+#entry {
+ margin: 0px;
+ border: none;
+ border-radius: 16px;
+ background-color: transparent;
+ min-height:32px;
+ font-weight: bold;
+}
+
+#entry:selected {
+ outline: none;
+ margin: 0px;
+ border: none;
+ border-radius: 16px;
+ background: linear-gradient(90deg, #cba6f7 0%, #94e2d5 100%);
+}
diff --git a/linux/home/.config/xkb/symbols/custom-us b/linux/home/.config/xkb/symbols/custom-us
new file mode 100644
index 0000000..9faa2e9
--- /dev/null
+++ b/linux/home/.config/xkb/symbols/custom-us
@@ -0,0 +1,27 @@
+// Clear existing modifiers
+//default partial modifier_keys
+partial alphanumeric_keys
+xkb_symbols "basic" {
+ include "us(basic)"
+
+ // Define a custom modifier (Mode_switch)
+ modifier_map Mod5 { <MDSW> };
+
+ // Remap Caps Lock to AltGr (ISO_Level3_Shift) and clear other modifiers
+ key <CAPS> {
+ type[Group1] = "ONE_LEVEL",
+ symbols[Group1] = [ ISO_Level3_Shift ],
+ actions[Group1] = [ SetMods(modifiers=Mod5) ]
+ };
+
+ // Remap h, j, k, l to arrow keys when Mode_switch is active and clear other modifiers
+ key <AC06> { [ h, H, Left ] };
+ key <AC07> { [ j, J, Down ] };
+ key <AC08> { [ k, K, Up ] };
+ key <AC09> { [ l, L, Right ] };
+
+ key <AD02> { [ w, W, Up ] };
+ key <AC01> { [ a, A, Left ] };
+ key <AC02> { [ s, S, Down ] };
+ key <AC03> { [ d, D, Right ] };
+};
diff --git a/linux/home/.config/xob/launch.sh b/linux/home/.config/xob/launch.sh
new file mode 100755
index 0000000..46d4861
--- /dev/null
+++ b/linux/home/.config/xob/launch.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+killall -q xob
+pkill -f manage-volume
+pkill -f manage-brightness
+pkill -f manage-microphone
+
+"$HOME"/.config/xob/manage-volume | xob -s default &
+"$HOME"/.config/xob/manage-brightness | xob -s default &
+"$HOME"/.config/xob/manage-microphone | xob -s default &
diff --git a/linux/home/.config/xob/manage-brightness b/linux/home/.config/xob/manage-brightness
new file mode 100755
index 0000000..496900f
--- /dev/null
+++ b/linux/home/.config/xob/manage-brightness
@@ -0,0 +1,16 @@
+#!/bin/node
+
+const fs = require('fs')
+const { exec } = require("child_process");
+
+let device = "intel_backlight";
+
+brightnessFile = `/sys/class/backlight/${device}/brightness`;
+maxBrightnessFile = `/sys/class/backlight/${device}/max_brightness`;
+
+maxValue = parseInt(fs.readFileSync(maxBrightnessFile, 'utf8'));
+
+fs.watch(brightnessFile, () => {
+ value = parseInt(fs.readFileSync(brightnessFile, 'utf8'));
+ console.log(Math.round(value / maxValue * 100));
+})
diff --git a/linux/home/.config/xob/manage-microphone b/linux/home/.config/xob/manage-microphone
new file mode 100755
index 0000000..3bf5972
--- /dev/null
+++ b/linux/home/.config/xob/manage-microphone
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+
+import sys
+
+from pulsectl import Pulse, PulseLoopStop
+
+
+def callback(ev):
+ if ev.index == source_index:
+ raise PulseLoopStop
+
+
+def current_status(source):
+ return round(source.volume.value_flat * 100), source.mute == 1
+
+
+def get_default_source_idx():
+ default_source_name = pulse.server_info().default_source_name
+ try:
+ source_index = next(index for index, source in sources.items()
+ if source.name == default_source_name)
+ return source_index
+ except StopIteration:
+ raise StopIteration("No default source was found.")
+
+
+try:
+ with Pulse() as pulse:
+ sources = {s.index: s for s in pulse.source_list()}
+
+ if len(sys.argv) > 1:
+ # Source index from command line argument if provided
+ source_index = int(sys.argv[1])
+ if source_index not in sources:
+ raise KeyError(
+ f"Source index {source_index} not found in list of sources."
+ )
+ else:
+ # Automatic determination of default source otherwise
+ source_index = get_default_source_idx()
+
+ pulse.event_mask_set('source')
+ pulse.event_callback_set(callback)
+ last_value, last_mute = current_status(sources[source_index])
+
+ while True:
+ pulse.event_listen()
+ sources = {s.index: s for s in pulse.source_list()}
+ value, mute = current_status(sources[source_index])
+ if value != last_value or mute != last_mute:
+ print(str(value) + ('!' if mute else ''))
+ last_value, last_mute = value, mute
+ sys.stdout.flush()
+
+except Exception as e:
+ print(f"ERROR: {e}", file=sys.stderr)
diff --git a/linux/home/.config/xob/manage-volume b/linux/home/.config/xob/manage-volume
new file mode 100755
index 0000000..d05a92f
--- /dev/null
+++ b/linux/home/.config/xob/manage-volume
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+
+from pulsectl import Pulse, PulseLoopStop
+import sys
+
+with Pulse() as pulse:
+ while True:
+ def callback(ev):
+ if ev.index == sink_index: raise PulseLoopStop
+
+ def current_status(sink):
+ return round(sink.volume.value_flat * 100), sink.mute == 1
+
+ def get_default_sink_idx():
+ default_sink_name = pulse.server_info().default_sink_name
+ try:
+ sink_index = next(index for index,sink in sinks.items() if sink.name == default_sink_name)
+ return sink_index
+ except StopIteration: raise StopIteration("No default sink was found.")
+
+ try:
+ sinks = {s.index:s for s in pulse.sink_list()}
+ if len(sys.argv) > 1:
+ # Sink index from command line argument if provided
+ sink_index = int(sys.argv[1])
+ if not sink_index in sinks:
+ raise KeyError(f"Sink index {sink_index} not found in list of sinks.")
+ else:
+ # Automatic determination of default sink otherwise
+ sink_index = get_default_sink_idx()
+
+ pulse.event_mask_set('sink')
+ pulse.event_callback_set(callback)
+ last_value, last_mute = current_status(sinks[sink_index])
+
+ while True:
+ pulse.event_listen()
+ sinks = {s.index:s for s in pulse.sink_list()}
+ value, mute = current_status(sinks[sink_index])
+ if value != last_value or mute != last_mute:
+ print(str(value) + ('!' if mute else ''))
+ last_value, last_mute = value, mute
+ sys.stdout.flush()
+
+ except Exception as e:
+ print(f"ERROR: {e}", file=sys.stderr)
diff --git a/linux/home/.config/xob/styles.cfg b/linux/home/.config/xob/styles.cfg
new file mode 100644
index 0000000..d236ff8
--- /dev/null
+++ b/linux/home/.config/xob/styles.cfg
@@ -0,0 +1,100 @@
+#vol = {
+# x = {relative = 1 ;offset = 88}
+# y = {relative = 0 ;offset = 32}
+# length = {relative = 0; offset = 0;};
+# padding = 0;
+# border = 0;
+# outline = 0;
+# thickness = 20;
+# orientation = "vertical"
+# color = {
+# normal = {
+# fg = "#ffffff";
+# bg = "#000000";
+# border = "#ffffff";
+# };
+# alt = {
+# fg = "#dc322f";
+# bg = "#000000";
+# border = "#dc322f";
+# };
+# overflow = {
+# fg = "#555555";
+# bg = "#000000";
+# border = "#555555";
+# };
+# altoverflow = {
+# fg = "#550000";
+# bg = "#000000";
+# border = "#550000";
+# };
+# };
+#};
+#
+#brightness = {
+# x = {relative = 1 ;offset = 88}
+# y = {relative = 0 ;offset = 88}
+# length = {relative = 0; offset = 0;};
+# padding = 0;
+# border = 0;
+# outline = 0;
+# thickness = 20;
+# orientation = "vertical"
+# color = {
+# normal = {
+# fg = "#ffffff";
+# bg = "#000000";
+# border = "#ffffff";
+# };
+# alt = {
+# fg = "#dc322f";
+# bg = "#000000";
+# border = "#dc322f";
+# };
+# overflow = {
+# fg = "#555555";
+# bg = "#000000";
+# border = "#555555";
+# };
+# altoverflow = {
+# fg = "#550000";
+# bg = "#000000";
+# border = "#550000";
+# };
+# };
+#};
+default = {
+ x = {relative = 1; offset = -48;};
+ y = {relative = 0.5; offset = 0;};
+ length = {relative = 0.3; offset = 0;};
+ thickness = 24;
+ outline = 3;
+ border = 4;
+ padding = 3;
+ orientation = "vertical";
+
+ overflow = "proportional";
+
+ color = {
+ normal = {
+ fg = "#ffffff";
+ bg = "#00000090";
+ border = "#ffffff";
+ };
+ alt = {
+ fg = "#555555";
+ bg = "#00000090";
+ border = "#555555";
+ };
+ overflow = {
+ fg = "#ff0000";
+ bg = "#00000090";
+ border = "#ff0000";
+ };
+ altoverflow = {
+ fg = "#550000";
+ bg = "#00000090";
+ border = "#550000";
+ };
+ };
+};
diff --git a/linux/home/.config/zathura/zathurarc b/linux/home/.config/zathura/zathurarc
new file mode 100644
index 0000000..78d66a1
--- /dev/null
+++ b/linux/home/.config/zathura/zathurarc
@@ -0,0 +1,27 @@
+set window-title-basename "true"
+
+
+
+
+# Basic Settings
+
+set highlight-transparency .1
+set zoom-center "true"
+set selection-clipboard "clipboard"
+set render-loading "false"
+set pages-per-row 1
+set scroll-page-aware "true"
+set scroll-full-overlap 0.01
+set scroll-step 50
+set zoom-min 10
+set guioptions ""
+set render-loading "false"
+
+
+# Startup options
+set adjust-open "best-fit"
+set recolor true
+
+# Side by side view (view 2 pages like an open book)
+map D set "first-page-column 1:1"
+map <C-d> set "first-page-column 1:2"
diff --git a/linux/home/.local/bin/control-center b/linux/home/.local/bin/control-center
new file mode 100755
index 0000000..d0b3320
--- /dev/null
+++ b/linux/home/.local/bin/control-center
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+LOCK_FILE="$HOME/.cache/eww-control-center.lock"
+EWW_BIN="$HOME/.local/bin/eww"
+ACTIVE_PLAYERS=$(playerctl -l | head -n 1)
+
+run() {
+ ${EWW_BIN} open control-center
+ sleep 0.2
+ xdo raise -N eww-bar
+ ${EWW_BIN} update ccenter=true
+
+ sleep 1 && [[ ! -z "$ACTIVE_PLAYERS" ]] && ${EWW_BIN} update mp=true
+}
+
+# Run eww daemon if not running
+if [[ ! `pidof eww` ]]; then
+ ${EWW_BIN} daemon
+ sleep 1
+else
+ if [[ ! -f "$LOCK_FILE" ]]; then
+ touch "$LOCK_FILE"
+ run
+ else
+ [[ ! -z "$ACTIVE_PLAYERS" ]] && ${EWW_BIN} update mp=false && sleep 0.4
+ ${EWW_BIN} update ccenter=false
+ sleep 0.6
+ ${EWW_BIN} close control-center
+ rm "$LOCK_FILE"
+ fi
+fi
diff --git a/linux/home/.local/bin/eww b/linux/home/.local/bin/eww
new file mode 100755
index 0000000..585d6d2
--- /dev/null
+++ b/linux/home/.local/bin/eww
Binary files differ
diff --git a/linux/home/.local/bin/ffmpeg b/linux/home/.local/bin/ffmpeg
new file mode 100755
index 0000000..2258fbd
--- /dev/null
+++ b/linux/home/.local/bin/ffmpeg
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# audio
+A="$(pactl list sources | grep 'analog.*monitor' | awk '{print $2}')"
+# screen size
+S="$(xdpyinfo | grep dimensions | awk '{print $2}')"
+# file name
+N="$(date +"%m-%d-%Y_%I:%M%p").mp4"
+
+# Desktop audio + screen recording
+ffmpeg \
+-s "$S" -r 25 -f x11grab -i :0.0+0,0 \
+-ac 2 ~/"$N"
+
+# ffmpeg can output high quality GIF. Before you start it is always recommended to use a recent version: download or compile.
+
+# ffmpeg -ss 30 -t 3 -i input.mp4 -vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 output.gif
+
+# This example will skip the first 30 seconds (-ss 30) of the input and create a 3 second output (-t 3).
+# fps filter sets the frame rate. A rate of 10 frames per second is used in the example.
+# scale filter will resize the output to 320 pixels wide and automatically determine the height while preserving the aspect ratio. The lanczos scaling algorithm is used in this example.
+# palettegen and paletteuse filters will generate and use a custom palette generated from your input. These filters have many options, so refer to the links for a list of all available options and values. Also see the Advanced options section below.
+# split filter will allow everything to be done in one command and avoids having to create a temporary PNG file of the palette.
+# Control looping with -loop output option but the values are confusing. A value of 0 is infinite looping, -1 is no looping, and 1 will loop once meaning it will play twice. So a value of 10 will cause the GIF to play 11 times.
diff --git a/linux/home/.local/bin/nitrogen b/linux/home/.local/bin/nitrogen
new file mode 100755
index 0000000..b8afe27
--- /dev/null
+++ b/linux/home/.local/bin/nitrogen
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+
+# Wrapper for nitrogen setting the freedesktop.org AccountsService/BackgroundFile so LightDM wallpaper is synced.
+# Inspired by <https://rafaelc.org/posts/sync-wm-wallpaper-with-lightdm-on-linux-mint/>
+
+key_value_retriever() {
+ KEY="${1}"
+ if [[ -z "$KEY" ]]; then
+ printf "ERROR: KEY should not be empty\n"
+ return
+ fi
+ FILE="${2}"
+ if [[ ! -f "$FILE" ]]; then
+ printf "ERROR: Cannot find FILE: %s\n" "$FILE"
+ return
+ fi
+ VALUE_VARNAME="${3}"
+ if [[ -z "$VALUE_VARNAME" ]]; then
+ printf "ERROR: VALUE_VARNAME should not be empty\n"
+ return
+ fi
+ MATCH=$(grep -m1 "^[[:space:]]*${KEY}=" "$FILE")
+ INDEX_OF_FIRST_EQUAL=$(expr index "$MATCH" =)
+ VALUE="${MATCH:${INDEX_OF_FIRST_EQUAL}}"
+ export "$VALUE_VARNAME"="$VALUE"
+}
+
+/usr/bin/nitrogen "$@"
+if [[ "${1:-}" == "--restore" ]]; then
+ exit $?
+fi
+
+NITROGEN_BG_SAVED_CFG_FILE="${HOME}/.config/nitrogen/bg-saved.cfg"
+if [[ ! -f "$NITROGEN_BG_SAVED_CFG_FILE" ]]; then
+ printf "!ERROR! Cannot find NITROGEN_BG_SAVED_CFG_FILE[%s]\n" "$NITROGEN_BG_SAVED_CFG_FILE" 1>&2
+ exit 1
+fi
+
+key_value_retriever "file" "$NITROGEN_BG_SAVED_CFG_FILE" "NITROGEN_BG_SAVED_CFG_FIRST_BACKGROUND_FILE"
+
+if [[ -z "$NITROGEN_BG_SAVED_CFG_FIRST_BACKGROUND_FILE" ]]; then
+ printf "!ERROR! Cannot retrieve NITROGEN_BG_SAVED_CFG_FIRST_BACKGROUND_FILE from NITROGEN_BG_SAVED_CFG_FILE[%s]\n" "NITROGEN_BG_SAVED_CFG_FIRST_BACKGROUND_FILE" 1>&2
+ exit 1
+fi
+
+if [[ ! -f "$NITROGEN_BG_SAVED_CFG_FIRST_BACKGROUND_FILE" ]]; then
+ printf "!ERROR! Cannot find NITROGEN_BG_SAVED_CFG_FIRST_BACKGROUND_FILE[%s]\n" "$NITROGEN_BG_SAVED_CFG_FIRST_BACKGROUND_FILE" 1>&2
+ exit 1
+fi
+
+printf "Setting nitrogen background into freedesktop.org AccountsService...\n"
+printf "NITROGEN_BG_SAVED_CFG_FIRST_BACKGROUND_FILE[%s]\n" "$NITROGEN_BG_SAVED_CFG_FIRST_BACKGROUND_FILE"
+
+"$HOME"/.scripts/lockscreen-wallpaper &
+dbus-send \
+ --print-reply \
+ --system \
+ --dest=org.freedesktop.Accounts \
+ /org/freedesktop/Accounts/User"$(id -u)" \
+ org.freedesktop.DBus.Properties.Set \
+ string:org.freedesktop.DisplayManager.AccountsService \
+ string:BackgroundFile \
+ variant:string:"$NITROGEN_BG_SAVED_CFG_FIRST_BACKGROUND_FILE"
diff --git a/linux/home/.local/bin/notification-center b/linux/home/.local/bin/notification-center
new file mode 100755
index 0000000..d785628
--- /dev/null
+++ b/linux/home/.local/bin/notification-center
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+LOCK_FILE="$HOME/.cache/eww-notification-center.lock"
+EWW_BIN="$HOME/.local/bin/eww"
+
+run() {
+ "$EWW_BIN" open notification-center
+ sleep 0.2
+ "$EWW_BIN" update noticenter=true
+}
+
+# Run eww daemon if not running
+if [[ ! $(pidof eww) ]]; then
+ "$EWW_BIN" daemon
+ sleep 1
+else
+ if [[ ! -f "$LOCK_FILE" ]]; then
+ touch "$LOCK_FILE"
+ run
+ else
+ "$EWW_BIN" update noticenter=false
+ sleep 0.8
+ "$EWW_BIN" close notification-center
+ rm "$LOCK_FILE"
+ fi
+fi
diff --git a/linux/home/.local/bin/xcolor-pick b/linux/home/.local/bin/xcolor-pick
new file mode 100755
index 0000000..459246f
--- /dev/null
+++ b/linux/home/.local/bin/xcolor-pick
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+# ██╗ ██╗ ██████╗ ██████╗ ██╗ ██████╗ ██████╗
+# ╚██╗██╔╝██╔════╝██╔═══██╗██║ ██╔═══██╗██╔══██╗
+# ╚███╔╝ ██║ ██║ ██║██║ ██║ ██║██████╔╝
+# ██╔██╗ ██║ ██║ ██║██║ ██║ ██║██╔══██╗
+# ██╔╝ ██╗╚██████╗╚██████╔╝███████╗╚██████╔╝██║ ██║
+# ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝
+# color picker for X.
+# Simple Script To Pick Color Quickly Using Gpick.
+# Created By: rxyhn.
+
+TEMP_DIR=/tmp/xcolor
+MSG=${XDG_CACHE_HOME:-$HOME/.cache}/xcolor.msg
+
+EXPIRE_TIME=5000
+
+main() {
+
+ HEX_COLOR=$(gpick -pso --no-newline)
+ mkdir -p $TEMP_DIR
+ HEX="${HEX_COLOR#\#}"
+ FNAME="$TEMP_DIR/$HEX.png"
+ convert -size 100x100 xc:"$HEX_COLOR" "$FNAME"
+ COLOR_CODE="$HEX_COLOR"
+
+ printf %s "$COLOR_CODE" | xclip -sel c
+ notify-send -a XColor "$COLOR_CODE" --icon="$FNAME" --expire-time="$EXPIRE_TIME"
+}
+
+main
diff --git a/linux/home/.local/share/applications/com.obsproject.Studio.desktop b/linux/home/.local/share/applications/com.obsproject.Studio.desktop
new file mode 100644
index 0000000..a0a09fd
--- /dev/null
+++ b/linux/home/.local/share/applications/com.obsproject.Studio.desktop
@@ -0,0 +1,99 @@
+[Desktop Entry]
+Version=1.0
+Name=OBS Studio
+GenericName=Streaming/Recording Software
+Comment=Free and Open Source Streaming/Recording Software
+Exec=obs
+#Icon=$HOME/.icons/WhiteSur-dark/apps/scalable/obs.svg
+#Icon=$HOME/.local/share/icons/WhiteSur-dark/apps/scalable/obs.svg
+Icon=/home/srdusr/.local/share/icons/WhiteSur-dark/apps/scalable/obs.svg
+Terminal=false
+Type=Application
+Categories=AudioVideo;Recorder;
+StartupNotify=true
+StartupWMClass=obs
+
+GenericName[an_ES]=Programa de retransmisión/gravación
+Comment[an_ES]=Program de retransmisión/gravación libre y de codigo ubierto
+GenericName[ar_SA]=برامج البث / التسجيل
+Comment[ar_SA]=برنامج بث / تسجيل مجاني ومفتوح المصدر
+GenericName[bn_BD]=স্ট্রিমিং/রেকর্ডিং সফটওয়্যার
+Comment[bn_BD]=ফ্রি এবং মুক্ত সোর্স স্ট্রিমিং/রেকর্ডিং সফ্টওয়্যার
+GenericName[ca_ES]=Programa de retransmissió/enregistrament
+Comment[ca_ES]=Programa de retransmissió/enregistrament de codi lliure i gratuït
+GenericName[cs_CZ]=Software pro vysílání a nahrávání
+Comment[cs_CZ]=Svobodný software pro vysílání a nahrávání
+GenericName[da_DK]=Streaming-/optagelsessoftware
+Comment[da_DK]=Gratis og open-source streaming-/optagelsessoftware
+GenericName[de_DE]=Streaming-/Aufnahme-Software
+Comment[de_DE]=Freie und Open-Source-Streaming-/Aufnahme-Software
+GenericName[el_GR]=Λογισμικό Ροής/Καταγραφής
+Comment[el_GR]=Δωρεαν Λογισμικό Streaming/Kαταγραφή ανοιχτου κωδικα
+GenericName[en_GB]=Streaming/Recording Software
+Comment[en_GB]=Free and Open Source Streaming/Recording Software
+GenericName[es_ES]=Disfusion digital/ Software de grabacion
+Comment[es_ES]=Difusion Digital/Software de grabacion Gratis y con Fuentes Abiertas
+GenericName[et_EE]=Video voogesituse ja salvestuse tarkvara
+Comment[et_EE]=Tasuta ja avatud lähtekoodiga video voogesituse ja salvestuse tarkvara
+GenericName[fa_IR]=نرم افزار جریان/ضبط
+Comment[fa_IR]=نرم افزار منبع باز و رایگان جریان/ضبط
+GenericName[fi_FI]=Striimaus-/tallennusohjelmisto
+Comment[fi_FI]=Ilmainen ja avoimen lähdekoodin striimaus-/tallennusohjelmisto
+GenericName[fil_PH]=Software para sa Streaming/Recording
+Comment[fil_PH]=Libre at Open Source na Streaming/Recording Software
+GenericName[fr_FR]=Logiciel de diffusion/enregistrement
+Comment[fr_FR]=Logiciel de diffusion/enregistrement gratuit et Open Source
+GenericName[gd_GB]=Bathar-bog sruthaidh/clàraidh
+Comment[gd_GB]=Bathar-bog sruthaidh/clàraidh saor le bun-tùs fosgailte
+GenericName[he_IL]=תוכנה לשידורים חיים והקלטה
+Comment[he_IL]=תכנה חינמית בקוד פתוח לשידורים חיים ולהקלטה
+GenericName[hi_IN]=स्ट्रीमिंग/रिकॉर्डिंग सॉफ्टवेयर
+Comment[hi_IN]=स्वतंत्र एवं खुले स्रोत वाला स्ट्रीमिंग/रिकॉर्डिंग सॉफ्टवेयर
+GenericName[hr_HR]=Softver za emitiranje/snimanje
+Comment[hr_HR]=Slobodan softver otvorenog koda za emitiranje/snimanje
+GenericName[hu_HU]=Közvetítő/rögzítő szoftver
+Comment[hu_HU]=Szabad és nyílt forráskódú közvetítő/rögzítő szoftver
+GenericName[id_ID]=Perangkat Lunak Streaming/Perekaman
+Comment[id_ID]=Perangkat Lunak Streaming/Perekaman Gratis dan Sumber Terbuka
+GenericName[it_IT]=Software per dirette e registrazione schermo
+Comment[it_IT]=Software Libero e Open Source Streaming/Registrazione
+GenericName[ja_JP]=配信/録画ソフトウェア
+Comment[ja_JP]=無料のオープンソース配信/録画ソフトウェア
+GenericName[ka_GE]=ვიდეოს ეთერში გამშვები/ჩამწერი პროგრამა
+Comment[ka_GE]=თავისუფალი და ღია წყაროს მქონე, ვიდეოს ეთერში გამშვები/ჩამწერი პროგრამა
+GenericName[kmr_TR]=Nermalava weşandin/tomarkirin-ê
+Comment[kmr_TR]=Nermalava weşandin/tomarkirin-ê belaş û çavkaniya azad
+GenericName[ko_KR]=방송 및 녹화 프로그램
+Comment[ko_KR]=무료 오픈소스 방송 및 녹화 프로그램
+GenericName[ms_MY]=Perisian Penstriman/Rakaman
+Comment[ms_MY]=Perisian Penstriman/Rakaman Bersumber Terbuka dan Bebas
+GenericName[nb_NO]=Strømming- og Opptaksprogramvare
+Comment[nb_NO]=Gratis Strømming- og Opptaksprogramvare med Åpen Kildekode
+GenericName[nl_NL]=Streaming/Opname Software
+Comment[nl_NL]=Vrij en Open Source Streaming/Opname Sofware
+GenericName[pl_PL]=Oprogramowanie do transmisji strumieniowej/nagrywania
+Comment[pl_PL]=Darmowe i otwarte oprogramowanie do transmisji strumieniowej/nagrywania
+GenericName[pt_BR]=Software de Streaming/Gravação
+Comment[pt_BR]=Software de Streaming/Gravação de Código Aberto e Livre
+GenericName[pt_PT]=Programa de transmissão/gravação
+Comment[pt_PT]=Programa de transmissão/gravação livre e de código aberto
+GenericName[ro_RO]=Program de Streaming/Înregistrare
+Comment[ro_RO]=Program de streaming / înregistrare gratuit și open source
+GenericName[ru_RU]=Программа для видеостриминга и видеозаписи
+Comment[ru_RU]=Свободное и открытое ПО для видеостриминга и видеозаписи
+GenericName[sk_SK]=Streamovací/Nahrávací Software
+Comment[sk_SK]=Bezplatný a otvorený streamovací/nahrávací software
+GenericName[sl_SI]=Pretočna/snemalna programska oprema
+Comment[sl_SI]=Brezplačni in odprtokodna programska oprema za pretakanje/snemanje
+GenericName[sv_SE]=Programvara för strömning/inspelning
+Comment[sv_SE]=Fri programvara för strömning/inspelning med öppen källkod
+GenericName[tr_TR]=Yayın/Kayıt Yazılımı
+Comment[tr_TR]=Ücretsiz ve Açık Kaynaklı Yayın/Kayıt Yazılımı
+GenericName[uk_UA]=Програма для трансляцій/запису
+Comment[uk_UA]=Вільне та відкрите програмне забезпечення для трансляцій/запису
+GenericName[vi_VN]=Phần mềm ghi hình/phát luồng
+Comment[vi_VN]=Phần mềm ghi hình / phát luồng mở và miễn phí
+GenericName[zh_CN]=直播/录像软件
+Comment[zh_CN]=自由且开源的用于直播串流以及视频录制的软件
+GenericName[zh_TW]=串流與錄影軟體
+Comment[zh_TW]=免費,開源的串流與錄影軟體
diff --git a/linux/home/.local/share/applications/phototonic.desktop b/linux/home/.local/share/applications/phototonic.desktop
new file mode 100644
index 0000000..b99875e
--- /dev/null
+++ b/linux/home/.local/share/applications/phototonic.desktop
@@ -0,0 +1,17 @@
+[Desktop Entry]
+Name=Phototonic
+Name[de]=Phototonic
+Name[fr]=Phototonic
+Comment=View photos on your computer
+Comment[de]=Photos betrachten und verwalten
+Comment[fr]=Visionner et gérer des photos sur votre ordinateur
+GenericName=Image Viewer
+GenericName[de]=Bildbetrachter
+GenericName[fr]=Visionneuse d'images
+Exec=phototonic %F
+Icon=/home/srdusr/.local/share/icons/WhiteSur-dark/apps/scalable/gwenview.svg
+Terminal=false
+Type=Application
+Categories=Graphics;Viewer;
+StartupNotify=true
+MimeType=image/png;image/gif;image/jpeg;image/bmp;image/svg+xml;
diff --git a/linux/home/.scripts/Heads-Up-Display b/linux/home/.scripts/Heads-Up-Display
new file mode 100755
index 0000000..8680123
--- /dev/null
+++ b/linux/home/.scripts/Heads-Up-Display
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# Created By: srdusr
+# Created On: Wed 05 Feb 2023 01:24:37 AM CAT
+# Project: bspwm scratchpad (Heads-Up-Display) with tmux session
+
+if id="$(xdo id -N Heads-Up-Display)"
+ then bspc node "$id" -g hidden -f
+ else kitty --class "Heads-Up-Display" -e tmux new-session -A -s HUD -e bash > /dev/null 2>&1 &
+fi
+
+#- - - - - - - - - -
+
+
+### Alternative method
+
+#id=$(xdotool search --class Heads-Up-Display);
+#if [ -z "$id" ]; then
+# #kitty --class "Heads-Up-Display" -e tmux new-session -A -s HUD -e bash > /dev/null 2>&1 &
+# alacritty --class "Heads-Up-Display" -e tmux new-session -A -s HUD -e bash > /dev/null 2>&1 &
+#else
+# if [ ! -f /tmp/hide_hud ]; then
+# touch /tmp/hide_hud && xdo hide "$id"
+# elif [ -f /tmp/hide_hud ]; then
+# rm /tmp/hide_hud && xdo show "$id"
+# fi
+#fi
+
diff --git a/linux/home/.scripts/README.md b/linux/home/.scripts/README.md
new file mode 100644
index 0000000..458b3cc
--- /dev/null
+++ b/linux/home/.scripts/README.md
@@ -0,0 +1 @@
+# scripts
diff --git a/linux/home/.scripts/bspwm-toggle-visibility.sh b/linux/home/.scripts/bspwm-toggle-visibility.sh
new file mode 100755
index 0000000..45a4c53
--- /dev/null
+++ b/linux/home/.scripts/bspwm-toggle-visibility.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Created By: srdusr
+# Created On: Mon 18 Sep 2023 18:37:21 CAT
+# Project: Bspwm script to toggle visibility of initial window and bring focus back to it
+
+# Get the ID of the currently focused desktop
+current_desktop_id=$(bspc query -D -d focused --names)
+
+# Get the ID of the first hidden window in the current desktop
+hidden_window_id=$(bspc query -N -d "$current_desktop_id" -n .hidden | head -n 1)
+
+# Check if there's a hidden window in the current desktop
+if [[ -n "$hidden_window_id" ]]; then
+ # There's a hidden window, so unhide it
+ bspc node "$hidden_window_id" -g hidden=off
+ # Bring focus back to the previously hidden window
+ bspc node -f "$hidden_window_id"
+else
+ # There's no hidden window in the current desktop, hide the first available window
+ first_window_id=$(bspc query -N -n focused.window)
+ bspc node "$first_window_id" -g hidden=on
+fi
diff --git a/linux/home/.scripts/bspwm_resize.sh b/linux/home/.scripts/bspwm_resize.sh
new file mode 100755
index 0000000..29ab5cf
--- /dev/null
+++ b/linux/home/.scripts/bspwm_resize.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+size=${2:-'10'}
+dir=$1
+
+# Find current window mode
+is_tiled() {
+bspc query -T -n | grep -q '"state":"tiled"'
+}
+# If the window is floating, move it
+if ! is_tiled; then
+#only parse input if window is floating,tiled windows accept input as is
+ case "$dir" in
+ west) switch="-w"
+ sign="-"
+ ;;
+ east) switch="-w"
+ sign="+"
+ ;;
+ north) switch="-h"
+ sign="-"
+ ;;
+ south) switch="-h"
+ sign="+"
+ ;;
+ esac
+ xdo resize ${switch} ${sign}${size}
+
+# Otherwise, window is tiled: switch with window in given direction
+else
+ case "$dir" in
+ west) bspc node @west -r -$size || bspc node @east -r -${size}
+ ;;
+ east) bspc node @west -r +$size || bspc node @east -r +${size}
+ ;;
+ north) bspc node @south -r -$size || bspc node @north -r -${size}
+ ;;
+ south) bspc node @south -r +$size || bspc node @north -r +${size}
+ ;;
+ esac
+fi
+
+##!/bin/bash
+#
+#[ "$#" -eq 3 ] || { echo "Needs exactly three arguments."; exit 1; }
+#
+#motion="$1"
+#direction="$2"
+#size="$3"
+#
+#if [ "$motion" = 'expand' ]; then
+# # These expand the window's given side
+# case "$direction" in
+# north) bspc node -z top 0 -"$size" ;;
+# east) bspc node -z right "$size" 0 ;;
+# south) bspc node -z bottom 0 "$size" ;;
+# west) bspc node -z left -"$size" 0 ;;
+# esac
+#else
+# # These contract the window's given side
+# case "$direction" in
+# north) bspc node -z top 0 "$size" ;;
+# east) bspc node -z right -"$size" 0 ;;
+# south) bspc node -z bottom 0 -"$size" ;;
+# west) bspc node -z left "$size" 0 ;;
+# esac
+#fi
diff --git a/linux/home/.scripts/check-updates.sh b/linux/home/.scripts/check-updates.sh
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/linux/home/.scripts/check-updates.sh
diff --git a/linux/home/.scripts/colors.sh b/linux/home/.scripts/colors.sh
new file mode 100755
index 0000000..fc1c10c
--- /dev/null
+++ b/linux/home/.scripts/colors.sh
@@ -0,0 +1,78 @@
+#!/usr/bin/env bash
+colors=$@
+for (( n=0; n < $colors; n++ )) do
+ printf " [%d] $(tput setaf $n)%s$(tput sgr0)" $n "Hello World!
+"
+done
+PADDING='Padding'
+
+main() {
+ local xterm_start=0 \
+ xterm_width=8 \
+ xterm_height=2
+
+ local cube_start=$((xterm_start + xterm_width * xterm_height)) \
+ cube_width=6 \
+ cube_height=$((6 * 6))
+
+ local greys_start=$((cube_start + cube_width * cube_height)) \
+ greys_width=8 \
+ greys_height=3
+
+ color_block $xterm_start $xterm_width $xterm_height
+ color_block $cube_start $cube_width $cube_height use_padding
+ color_block $greys_start $greys_width $greys_height
+ echo
+}
+
+color_block() {
+ local start=$1 width=$2 height=$3 use_padding=$4
+ local max s color_nums colors
+
+ max=$((start + width * height - 1))
+
+ echo
+ for s in $(seq $start $width $max); do
+ color_nums=$(seq $s $((s + width - 1)))
+ colors="${use_padding:+$PADDING }${color_nums}${use_padding:+ $PADDING}"
+
+ printf '%s%s %s%s\n' \
+ "$(fg_bars $colors)" $ansi_reset \
+ "$(bg_bars $colors)" $ansi_reset
+ done
+}
+
+fg_bars() {
+ for color in $@; do
+ color_bar ansi_fg $color ''
+ done
+}
+
+bg_bars() {
+ for color in $@; do
+ color_bar ansi_bg $color ' '
+ done
+}
+
+color_bar() {
+ local ansi=$1 color=$2 trail=$3
+
+ if [ "$color" == $PADDING ]; then
+ printf '%s %s' $ansi_reset "$trail"
+ else
+ local color_seq=$($ansi $color)
+ printf '%s %03d%s' $color_seq $color "$trail"
+ fi
+}
+
+ansi_reset=$'\033[0m'
+
+ansi_fg() {
+ printf '\033[38;5;%dm' $1
+}
+
+ansi_bg() {
+ printf '\033[48;5;%dm' $1
+}
+
+main
diff --git a/linux/home/.scripts/cryptocheck b/linux/home/.scripts/cryptocheck
new file mode 100755
index 0000000..02ba42d
--- /dev/null
+++ b/linux/home/.scripts/cryptocheck
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+if [ ! -d ~/.cache/crypto ]; then
+ mkdir ~/.cache/crypto
+fi
+ticker=(BTC ETH ADA DOT SOL XMR)
+
+for currency in "${ticker[@]}"; do
+ echo "$currency"
+done | while read coin
+ do
+ price=$(curl rate.sx/1$coin)
+ if [ $coin = "BTC" ]; then
+ icon=󰠓
+ elif [ $coin = "ETH" ]; then
+ icon=󰡪
+ elif [ $coin = "ADA" ]; then
+ icon=󰝨
+ elif [ $coin = "DOT" ]; then
+ icon=󰐇
+ elif [ $coin = "SOL" ]; then
+ icon=󰘙
+ elif [ $coin = "XMR" ]; then
+ icon=󰝴
+ fi
+
+ echo "$icon $coin: $price" > ~/.cache/crypto/$coin
+
+ done
+
+date > ~/.cache/crypto/time
+
diff --git a/linux/home/.scripts/cryptonotify b/linux/home/.scripts/cryptonotify
new file mode 100755
index 0000000..47883c3
--- /dev/null
+++ b/linux/home/.scripts/cryptonotify
@@ -0,0 +1,19 @@
+#!/bin/sh
+source cryptocheck
+Output=
+for file in ~/.cache/crypto/*
+do
+ if [ ! -z "$Output" ]; then
+ if [ ! $(basename $file) = "time" ]; then
+ Output="$Output\n$(cat $file)"
+ fi
+ else
+ if [ ! $(basename $file) = "time" ]; then
+ Output="$Output$(cat $file)"
+ fi
+ fi
+
+done
+
+Output="$Output\n$(cat ~/.cache/crypto/time)"
+notify-send "Crypto Prices" "$Output"
diff --git a/linux/home/.scripts/dotfiles.sh b/linux/home/.scripts/dotfiles.sh
new file mode 100755
index 0000000..b231367
--- /dev/null
+++ b/linux/home/.scripts/dotfiles.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+# Set the bare dotfiles repo directory
+dotfiles_dir="$HOME/.cfg"
+
+# Set the home directory
+home_dir="$HOME"
+
+# Exclude the .cfg directory and any other files/directories you want to ignore
+exclude_list=(".cfg")
+
+# Change to the home directory
+cd "$home_dir"
+
+# Get a list of all dotfiles in the repository
+files=$(find "$dotfiles_dir" -maxdepth 1 -type f -not -name ".*" -not -name "${exclude_list[*]}" -printf "%f\n")
+
+# Link each file to its corresponding location in $HOME
+for file in $files; do
+ ln -sf "$dotfiles_dir/$file" "$home_dir/.$file"
+done
+
+# Get a list of all dot directories in the repository
+dirs=$(find "$dotfiles_dir" -maxdepth 1 -type d -not -path "$dotfiles_dir" -not -name ".*" -not -name "${exclude_list[*]}" -printf "%f\n")
+
+# Link each directory to its corresponding location in $HOME
+for dir in $dirs; do
+ ln -sf "$dotfiles_dir/$dir" "$home_dir/.$dir"
+done
+
+# Remove any symlinks that are no longer present in the repo
+while IFS= read -r -d '' link; do
+ if [[ ! -e "$link" ]]; then
+ rm "$link"
+ fi
+done < <(find "$home_dir" -maxdepth 1 -type l -name ".*" -not -name ".cfg" -print0)
+
diff --git a/linux/home/.scripts/ffmpeg b/linux/home/.scripts/ffmpeg
new file mode 100755
index 0000000..2258fbd
--- /dev/null
+++ b/linux/home/.scripts/ffmpeg
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# audio
+A="$(pactl list sources | grep 'analog.*monitor' | awk '{print $2}')"
+# screen size
+S="$(xdpyinfo | grep dimensions | awk '{print $2}')"
+# file name
+N="$(date +"%m-%d-%Y_%I:%M%p").mp4"
+
+# Desktop audio + screen recording
+ffmpeg \
+-s "$S" -r 25 -f x11grab -i :0.0+0,0 \
+-ac 2 ~/"$N"
+
+# ffmpeg can output high quality GIF. Before you start it is always recommended to use a recent version: download or compile.
+
+# ffmpeg -ss 30 -t 3 -i input.mp4 -vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 output.gif
+
+# This example will skip the first 30 seconds (-ss 30) of the input and create a 3 second output (-t 3).
+# fps filter sets the frame rate. A rate of 10 frames per second is used in the example.
+# scale filter will resize the output to 320 pixels wide and automatically determine the height while preserving the aspect ratio. The lanczos scaling algorithm is used in this example.
+# palettegen and paletteuse filters will generate and use a custom palette generated from your input. These filters have many options, so refer to the links for a list of all available options and values. Also see the Advanced options section below.
+# split filter will allow everything to be done in one command and avoids having to create a temporary PNG file of the palette.
+# Control looping with -loop output option but the values are confusing. A value of 0 is infinite looping, -1 is no looping, and 1 will loop once meaning it will play twice. So a value of 10 will cause the GIF to play 11 times.
diff --git a/linux/home/.scripts/get_zle_keymap_select.sh b/linux/home/.scripts/get_zle_keymap_select.sh
new file mode 100755
index 0000000..1e2eaf4
--- /dev/null
+++ b/linux/home/.scripts/get_zle_keymap_select.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# Get the value of the zle-keymap-select variable
+value=$(print -v zle-keymap-select)
+
+# Specify the file path to save the value
+file_path="~/file.txt"
+
+# Write the value to the file
+echo "$value" > "$file_path"
+
+# Optionally, you can also print the value to the console
+echo "The value of zle-keymap-select is: $value"
diff --git a/linux/home/.scripts/gsettings.sh b/linux/home/.scripts/gsettings.sh
new file mode 100755
index 0000000..0cd28c2
--- /dev/null
+++ b/linux/home/.scripts/gsettings.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# Disable screen lock
+gsettings set org.gnome.desktop.screensaver lock-enabled false
+gsettings set org.gnome.desktop.session idle-delay 0
+
+# Mutter Overlay Key
+gsettings set org.gnome.mutter overlay-key ''
+
+# Disable update notification
+#gsettings set org.gnome.software download-updates false
+#gsettings set com.ubuntu.update-notifier no-show-notifications true
+#sudo rm /etc/xdg/autostart/upg-notifier-autostart.desktop
+
+#sudo mv /etc/xdg/autostart/update-notifier.desktop /etc/xdg/autostart/update-notifier.desktop.old
+#sudo mv /etc/xdg/autostart/gnome-software-service.desktop /etc/xdg/autostart/gnome-software-service.desktop.old
+
+# Custom Keybinding Names
+gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/']"
+
+# Custom Keybinding 0
+gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ binding "<Alt>T"
+gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ command "scratchpad"
+gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ name "scratchpad"
+
+# Disable keyboard plugin
+gsettings set org.gnome.settings-daemon.plugins.keyboard active false
diff --git a/linux/home/.scripts/killandnotify b/linux/home/.scripts/killandnotify
new file mode 100755
index 0000000..2e7222e
--- /dev/null
+++ b/linux/home/.scripts/killandnotify
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Kills an application and sends a notification that it's been killed
+
+killall "$1" && notify-send "$1" "$2"
diff --git a/linux/home/.scripts/layer.sh b/linux/home/.scripts/layer.sh
new file mode 100755
index 0000000..4b17ed1
--- /dev/null
+++ b/linux/home/.scripts/layer.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+current_layer="$(bspc query -T -n | jq -r '.client.layer')"
+case $1 in
+ +|-)
+ declare -A _layers=( [below]=0 [normal]=1 [above]=2 )
+ layers=( below normal above )
+ maxl=$(( ${#layers[@]} - 1 ))
+ current_layer="$(bspc query -T -n | jq -r '.client.layer')"
+ i=$(( ${_layers[$current_layer]} $1 1 ))
+ if [[ $i -lt 0 ]]; then
+ i=0
+ elif [[ $i -gt $maxl ]]; then
+ i=$maxl
+ fi
+ #cycle? nah
+ #i=$(( (${_layers[$current_layer]} + ${#layers[@]} ${1} 1) % ${#layers[@]} ))
+ new_layer="${layers[$i]}"
+ ;;
+ *)
+ new_layer="$(bspc query -T -n | jq -r '.client.lastLayer')"
+ ;;
+esac
+[[ "$current_layer" != "$new_layer" ]] && bspc node -l "$new_layer"
diff --git a/linux/home/.scripts/neovim.sh b/linux/home/.scripts/neovim.sh
new file mode 100755
index 0000000..39554cb
--- /dev/null
+++ b/linux/home/.scripts/neovim.sh
@@ -0,0 +1,421 @@
+#!/bin/bash
+
+# Created By: srdusr
+# Created On: Sat 12 Aug 2023 13:11:39 CAT
+# Project: Install/update/uninstall/change version Neovim script, primarily for Linux but may work in other platforms
+
+# Dependencies: wget/curl, fuse
+
+# Color definitions
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+NC='\033[0m' # No Color
+
+# Function to handle errors
+handle_error() {
+ local message="$1"
+ printf "${RED}Error: $message${NC}\n"
+}
+
+# Check if necessary dependencies are installed
+check_dependencies() {
+ if [ -x "$(command -v wget)" ]; then
+ DOWNLOAD_COMMAND="wget"
+ elif [ -x "$(command -v curl)" ]; then
+ DOWNLOAD_COMMAND="curl"
+ else
+ printf "${RED}Error: Neither wget nor curl found. Please install one of them to continue!${NC}\n"
+ exit 1
+ fi
+}
+
+# Check for privilege escalation tools
+check_privilege_tools() {
+ if [ -x "$(command -v sudo)" ]; then
+ PRIVILEGE_TOOL="sudo"
+ elif [ -x "$(command -v doas)" ]; then
+ PRIVILEGE_TOOL="doas"
+ elif [ -x "$(command -v pkexec)" ]; then
+ PRIVILEGE_TOOL="pkexec"
+ elif [ -x "$(command -v dzdo)" ]; then
+ PRIVILEGE_TOOL="dzdo"
+ elif [ "$(id -u)" -eq 0 ]; then
+ PRIVILEGE_TOOL="" # root
+ else
+ PRIVILEGE_TOOL="" # No privilege escalation mechanism found
+ printf "\n${RED}Error: No privilege escalation tool (sudo, doas, pkexec, dzdo, or root privileges) found. You may not have sufficient permissions to run this script.${NC}\n"
+ printf "\nAttempt to continue Installation (might fail without a privilege escalation tool)? [yes/no] "
+ read continue_choice
+ case $continue_choice in
+ [Yy] | [Yy][Ee][Ss]) ;;
+ [Nn] | [Nn][Oo]) exit ;;
+ *) handle_error "Invalid choice. Exiting..." && exit ;;
+ esac
+ fi
+}
+
+# Check if Neovim is already installed
+check_neovim_installed() {
+ if [ -x "$(command -v nvim)" ]; then
+ return 0 # Neovim is installed
+ else
+ return 1 # Neovim is not installed
+ fi
+}
+
+# Nightly version
+nightly_version() {
+ local url="https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage"
+ install_neovim "$url"
+ local version_output=$(nvim --version)
+ version_id="Nightly $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+')"
+}
+
+# Stable version
+stable_version() {
+ local url="https://github.com/neovim/neovim/releases/download/stable/nvim.appimage"
+ install_neovim "$url"
+ local version_output=$(nvim --version)
+ version_id="Stable $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+')"
+}
+
+# Specific version
+specific_version() {
+ local version="$1"
+
+ # Add 'v' prefix if not present
+ if [[ $version != v* ]]; then
+ version="v$version"
+ fi
+
+ local url="https://github.com/neovim/neovim/releases/download/$version/nvim.appimage"
+ install_neovim "$url"
+ local version_output=$(nvim --version)
+ version_id="Version $version $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+')"
+}
+
+# Function to download a file using wget or curl
+download_file() {
+ local url="$1"
+ local output="$2"
+
+ if [ "$DOWNLOAD_COMMAND" = "wget" ]; then
+ if ! "$DOWNLOAD_COMMAND" -q --show-progress -O "$output" "$url"; then
+ handle_error "Download failed. Exiting..."
+ exit 1
+ fi
+ elif [ "$DOWNLOAD_COMMAND" = "curl" ]; then
+ if ! "$DOWNLOAD_COMMAND" --progress-bar -# -o "$output" "$url"; then
+ handle_error "Download failed. Exiting..."
+ exit 1
+ fi
+ else
+ echo "Unsupported download command: $DOWNLOAD_COMMAND"
+ exit 1
+ fi
+}
+
+# Check if a specific version of Neovim exists
+version_exists() {
+ local version="$1"
+
+ # Add 'v' prefix if not present
+ if [[ $version != v* ]]; then
+ version="v$version"
+ fi
+
+ # Fetch all the release tags from GitHub
+ ALL_TAGS=$(curl -s "https://api.github.com/repos/neovim/neovim/tags" | grep '"name":' | cut -d '"' -f 4)
+
+ # Check if the desired version is in the list of release tags
+ if echo "$ALL_TAGS" | grep -q "$version"; then
+ return 0 # Version exists
+ else
+ return 1 # Version does not exist
+ fi
+}
+
+# Update Neovim to the latest version (nightly/stable)
+update_version() {
+ valid_choice=false
+ while [ "$valid_choice" = false ]; do
+ # Determine which version to update to (nightly/stable)
+ printf "Select version to install/update to:\n"
+ printf " 1. Nightly\n"
+ printf " 2. Stable\n"
+ printf " 3. Choose specific version by tag\n"
+ printf "Enter the number corresponding to your choice (1/2/3): "
+ read update_choice
+
+ case $update_choice in
+ 1)
+ version="Nightly"
+ nightly_version
+ valid_choice=true
+ ;;
+ 2)
+ version="Stable"
+ stable_version
+ valid_choice=true
+ ;;
+ 3)
+ # Ask user for specific version
+ read -p "Enter the specific version (e.g., v0.1.0): " version
+ # Normalize version
+ if [[ $version != v* ]]; then
+ version="v$version"
+ fi
+ # Check if the specific version exists on GitHub releases
+ if version_exists "$version"; then
+ # Install specific version
+ specific_version "$version" # Pass the normalized version to the function
+ valid_choice=true
+ else
+ printf "${RED}The specified version $version does not exist.${NC}\n"
+ fi
+ ;;
+
+ *)
+ handle_error "Invalid choice. Please enter a valid option (1, 2 or 3)."
+ ;;
+ esac
+ done
+
+}
+
+# Install Neovim
+install_neovim() {
+ local url="$1"
+ local install_action="$3"
+
+ if [ "$install_action" = "installed" ]; then
+ printf "Downloading and installing Neovim $version...\n"
+ else
+ printf "${GREEN}Updating Neovim to the latest version ($version)...${NC}\n"
+ fi
+
+ # Determine the platform-specific installation steps
+ case "$(uname -s)" in
+ Linux)
+ printf "Detected Linux OS.\n"
+ if [ -x "$(command -v fusermount)" ]; then
+ printf "FUSE is available. Downloading and running the AppImage...\n"
+ download_file "$url" "nvim.appimage"
+ chmod u+x nvim.appimage
+ "$PRIVILEGE_TOOL" cp nvim.appimage /usr/local/bin/nvim
+ "$PRIVILEGE_TOOL" mv nvim.appimage /usr/bin/nvim
+ else
+ printf "FUSE is not available. Downloading and extracting the AppImage...\n"
+ download_file "$url" "nvim.appimage"
+ chmod u+x nvim.appimage
+ ./nvim.appimage --appimage-extract
+ "$PRIVILEGE_TOOL" cp squashfs-root/usr/bin/nvim /usr/local/bin
+ "$PRIVILEGE_TOOL" mv squashfs-root/usr/bin/nvim /usr/bin
+ fi
+ ;;
+
+ Darwin)
+ printf "Detected macOS.\n"
+ download_file "$url" "nvim-macos.tar.gz"
+ xattr -c ./nvim-macos.tar.gz
+ tar xzvf nvim-macos.tar.gz
+ "$PRIVILEGE_TOOL" cp nvim-macos/bin/nvim /usr/local/bin
+ "$PRIVILEGE_TOOL" mv nvim-macos/bin/nvim /usr/bin/nvim
+ ;;
+
+ MINGW*)
+ printf "Detected Windows.\n"
+ download_file "$url" "nvim.appimage"
+ chmod +x nvim.appimage
+ if [ "$PRIVILEGE_TOOL" = "sudo" ]; then
+ "$PRIVILEGE_TOOL" cp nvim.appimage /usr/local/bin/nvim
+ "$PRIVILEGE_TOOL" mv /usr/local/bin/nvim /usr/bin
+ elif [ "$PRIVILEGE_TOOL" = "" ]; then
+ cp nvim.appimage /usr/local/bin/nvim
+ mv /usr/local/bin/nvim /usr/bin
+ else
+ printf "No privilege escalation tool found. Cannot install Neovim on Windows.\n"
+ fi
+ ;;
+
+ *)
+ printf "Unsupported operating system.\n"
+ exit 1
+ ;;
+ esac
+ # Check if the installation was successful
+ if [ $? -eq 0 ]; then
+ if [ "$install_action" = "installed" ]; then
+ printf "${GREEN}Neovim $version has been installed successfully!${NC}\n"
+ else
+ printf "${GREEN}Neovim has been updated successfully to $version!${NC}\n"
+ fi
+ else
+ printf "${RED}Error: Neovim installation/update failed.${NC}\n"
+ exit 1
+ fi
+}
+
+# Uninstall Neovim
+uninstall_neovim() {
+ printf "${RED}Uninstalling Neovim...${NC}\n"
+
+ # Detect the operating system to determine the appropriate uninstallation method
+ case "$(uname -s)" in
+ Linux)
+ printf "Detected Linux OS.\n"
+ "$PRIVILEGE_TOOL" rm /usr/local/bin/nvim
+ "$PRIVILEGE_TOOL" rm /usr/bin/nvim
+ ;;
+
+ Darwin)
+ printf "Detected macOS.\n"
+ "$PRIVILEGE_TOOL" rm /usr/local/bin/nvim
+ "$PRIVILEGE_TOOL" rm /usr/bin/nvim
+ ;;
+
+ MINGW*)
+ printf "Detected Windows.\n"
+ if [ "$PRIVILEGE_TOOL" = "sudo" ]; then
+ "$PRIVILEGE_TOOL" rm /usr/local/bin/nvim
+ "$PRIVILEGE_TOOL" rm /usr/bin/nvim
+ else
+ [ "$PRIVILEGE_TOOL" = "" ]
+ rm /usr/local/bin/nvim
+ rm /usr/bin/nvim
+ fi
+ ;;
+ *)
+ printf "Unsupported operating system.\n"
+ ;;
+ esac
+
+ printf "${GREEN}Neovim has been uninstalled successfully!${NC}\n"
+}
+
+# Check if Neovim is running
+check_neovim_running() {
+ if pgrep nvim >/dev/null; then
+ printf "${RED}Error: Neovim is currently running. Please close Neovim before proceeding.${NC}\n"
+ read -p "Do you want to forcefully terminate Neovim and continue? [yes/no] " terminate_choice
+
+ case $terminate_choice in
+ [Yy] | [Yy][Ee][Ss])
+ pkill nvim # Forcefully terminate Neovim
+ ;;
+ [Nn] | [Nn][Oo])
+ echo "Exiting..."
+ exit 1
+ ;;
+ *)
+ handle_error "Invalid choice."
+ ;;
+ esac
+ fi
+}
+
+check_neovim_running
+
+# Define the variable to control the prompt
+SHOW_PROMPT=1
+
+# Check if necessary dependencies are installed
+check_dependencies
+
+# Check for privilege escalation tools
+check_privilege_tools
+
+# Check if Neovim is already installed and ask the user if want to install it
+if check_neovim_installed; then
+ printf "${GREEN}Neovim is already installed!${NC}\n"
+else
+ printf "${RED}Neovim is not installed.${NC}\n"
+ read -p "Install Neovim? (y/n): " install_choice
+
+ case $install_choice in
+ [Yy])
+ update_version
+ ;;
+ [Nn])
+ echo "Exiting..."
+ exit
+ ;;
+ *)
+ handle_error "Invalid choice. Please enter 'y' for yes or 'n' for no."
+ ;;
+ esac
+fi
+
+# Function to check for updates and display breaking changes
+check_version_updates() {
+ local latest_version_url="https://api.github.com/repos/neovim/neovim/releases/latest"
+ local latest_version=""
+
+ if [ -x "$(command -v curl)" ]; then
+ latest_version=$(curl -sSL "$latest_version_url" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+ elif [ -x "$(command -v wget)" ]; then
+ latest_version=$(wget -qO - "$latest_version_url" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+ else
+ printf "${RED}Error: Neither curl nor wget found. Please install one of them to continue!${NC}\n"
+ exit 1
+ fi
+
+ if version_exists "$latest_version"; then
+ printf "${GREEN}An update is available!${NC}\n"
+ display_breaking_changes "$latest_version"
+ else
+ printf "You have the latest version of Neovim.\n"
+ fi
+}
+
+# Function to display breaking changes for a specific version
+display_breaking_changes() {
+ local version="$1"
+ local changelog_url="https://github.com/neovim/neovim/releases/tag/$version"
+ local changelog=""
+
+ if [ -x "$(command -v curl)" ]; then
+ changelog=$(curl -sSL "$changelog_url" | grep -oE '<h1>Breaking Changes.*?</ul>' | sed 's/<[^>]*>//g')
+ elif [ -x "$(command -v wget)" ]; then
+ changelog=$(wget -qO - "$changelog_url" | grep -oE '<h1>Breaking Changes.*?</ul>' | sed 's/<[^>]*>//g')
+ else
+ printf "${RED}Error: Neither curl nor wget found. Please install one of them to continue!${NC}\n"
+ exit 1
+ fi
+
+ printf "\nBreaking Changes in Neovim $version:\n"
+ printf "$changelog\n"
+}
+
+# Main loop
+while [ "$SHOW_PROMPT" -gt 0 ]; do
+ printf "Select an option:\n"
+ printf " 1. Install/update Neovim\n"
+ printf " 2. Check for updates\n"
+ printf " 3. Uninstall Neovim\n"
+ printf " 4. Run Neovim\n"
+ printf " 5. Quit\n"
+ read -p "Enter a number or press 'q' to quit: " choice
+
+ case $choice in
+ 1)
+ update_version
+ ;;
+ 2)
+ check_version_updates
+ ;;
+ 3)
+ uninstall_neovim
+ ;;
+ 4)
+ nvim
+ ;;
+ 5 | [Qq])
+ echo "Exiting..."
+ exit
+ ;;
+ *)
+ handle_error "Invalid choice. Please choose a valid option by entering the corresponding number or press 'q' to 'quit'."
+ ;;
+ esac
+done
diff --git a/linux/home/.scripts/opacity-change.sh b/linux/home/.scripts/opacity-change.sh
new file mode 100755
index 0000000..b509936
--- /dev/null
+++ b/linux/home/.scripts/opacity-change.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+set -eu
+[[ -n ${DEBUG:-} ]] && set -x
+
+#### Example alacritty.yml usage
+#key_bindings:
+# - { key: N, mods: Control|Shift, action: SpawnNewInstance }
+# - { key: O, mods: Control|Shift, command: { program: "opacity-change.sh", args: ["-"] } }
+# - { key: P, mods: Control|Shift, command: { program: "opacity-change.sh", args: ["+"] } }
+
+
+operation="${1:-}${2:-}" # Arg #1 & #2 (in case the user misinterpreted a space in the usage), Default ''
+step="${operation:1}" # Substring from char index 1
+step="${step:-1}" # Default '1'
+operation="${operation:0:1}" # Substring from char index 0 length of 1
+config_file="$HOME/.config/alacritty/alacritty.yml"
+config_field="opacity"
+tmp_file="/tmp/$(basename $config_file).$(date +%s)"
+current_value=$(sed 's/#.*//g; /\b'"$config_field"':/!d; s/.*: \?//' < "$config_file")
+
+case $operation in
+"-")
+ verb="Decreasing" ;;
+"+")
+ verb="Increasing" ;;
+*)
+ echo "Usage: ${BASH_SOURCE[0]} (-|+)[int]"; exit 255 ;;
+esac
+
+new_value="$(awk '{n=$1+$2/10; print (n<0 ? 0 : n>1 ? 1 : n)}' <<<"$current_value $operation$step")"
+echo "$verb $config_field from $current_value to $new_value" >&2
+cp "$config_file" "$tmp_file"
+sed "s/\b$config_field:.*/$config_field: $new_value/" "$tmp_file" > "$config_file"
+
diff --git a/linux/home/.scripts/powermenu b/linux/home/.scripts/powermenu
new file mode 100755
index 0000000..7bd913e
--- /dev/null
+++ b/linux/home/.scripts/powermenu
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# display a power menu to: shutdown, reboot,
+# lock, logout, and suspend. This script can be
+# executed by clicking on the polybar powermenu module
+# or with a keyboard shortcut
+
+# options to be displayed
+shutdown=" Shutdown"
+reboot=" Reboot"
+lock=" Lock"
+logout=" Logout"
+suspend=" Suspend"
+
+uptime=$(uptime -p | sed -e 's/up //g')
+
+# options passed into variable
+options="$shutdown\n$reboot\n$lock\n$logout\n$suspend"
+
+chosen="$(echo -e "$options" | rofi -theme ~/.config/rofi/styles/powermenu.rasi -lines 5 -dmenu -p "$uptime")"
+
+case $chosen in
+$shutdown)
+ systemctl poweroff
+ ;;
+$reboot)
+ systemctl reboot
+ ;;
+$lock)
+ betterlockscreen --lock dimblur
+ ;;
+$logout)
+ bspc quit
+ ;;
+$suspend)
+ systemctl suspend
+ ;;
+esac
diff --git a/linux/home/.scripts/qemu-helper.sh b/linux/home/.scripts/qemu-helper.sh
new file mode 100755
index 0000000..0d38aba
--- /dev/null
+++ b/linux/home/.scripts/qemu-helper.sh
@@ -0,0 +1,172 @@
+#!/bin/bash
+
+# Created By: srdusr
+# Created On: Wed 02 Aug 2023 16:16:21 PM CAT
+# Project: QEMU setup/opener helper wrapper script
+
+# Set global variables for VM parameters
+ram_size="4G"
+
+# Function to prompt user for VM parameters
+function get_vm_parameters() {
+ read -p "Enter VM name (default: vm): " vm_name
+ vm_name=${vm_name:-vm}
+
+ # Set the default ISO file path to ~/machines/images
+ default_iso_path="$HOME/machines/images"
+
+ # Generate completions for ISO and IMG files in the images directory
+ COMPREPLY=()
+ local files=$(compgen -G "$default_iso_path/*.{iso,img}" -o plusdirs)
+ for file in $files; do
+ COMPREPLY+=("$file")
+ done
+
+ # Use read with -i and -e options for tab-completion
+ read -ep "Enter ISO file path (default: $default_iso_path): " -i "$default_iso_path" iso_path
+
+ # Manually expand the ~ to the user's home directory
+ iso_path=$(eval echo "$iso_path")
+
+ # Validate the user input
+ while [ ! -f "$iso_path" ]; do
+ read -ep "Invalid file path. Enter a valid ISO file path: " iso_path
+ done
+
+ # Check if the selected file is an IMG file
+ if [[ "$iso_path" == *.img ]]; then
+ guest_os="windows"
+ else
+ guest_os="linux"
+ fi
+
+ # Show available disk space before asking for disk image size
+ echo "Available disk space:"
+ df -h "$vm_images_path"
+
+ read -p "Enter disk image size in GB (default: 10G): " disk_size
+ disk_size=${disk_size:-10G}
+
+ read -p "Enter RAM size in GB (default: 4G): " ram_size
+ ram_size=${ram_size:-4G}
+
+ # Check if the RAM size is in the correct format (e.g., "4G")
+ while ! [[ $ram_size =~ ^[0-9]+[kKmMgGtTpPeE]$ ]]; do
+ read -p "Invalid RAM size format. Enter RAM size in GB (e.g., 4G): " ram_size
+ done
+
+ read -p "Enter number of CPU cores (default: 2): " cpu_cores
+ cpu_cores=${cpu_cores:-2}
+}
+
+
+# Function to list available VMs
+function list_vms() {
+ echo "Available VMs:"
+ for vm_file in "$vm_images_path"/*.qcow2; do
+ vm=$(basename "$vm_file" .qcow2)
+ echo " - $vm"
+ done
+}
+
+# Function to list available ISO and IMG files in the images directory
+function list_iso_img_files() {
+ echo "Available ISO and IMG files in $iso_images_path:"
+ iso_img_files=()
+ while IFS= read -r -d $'\0' file; do
+ iso_img_files+=("$file")
+ done < <(find "$iso_images_path" -type f \( -iname \*.iso -o -iname \*.img \) -print0)
+
+ for ((i = 0; i < ${#iso_img_files[@]}; i++)); do
+ echo " $((i + 1)). ${iso_img_files[i]##*/}"
+ done
+}
+
+# Function to check if VM is already running
+function is_vm_running() {
+ vm_name=$1
+ if ps aux | grep -v grep | grep -q "[q]emu-system-x86_64.*$vm_name"; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# Function to start VM
+function start_vm() {
+ vm_name=$1
+ is_vm_running "$vm_name"
+ if [ $? -eq 0 ]; then
+ echo "VM '$vm_name' is already running."
+ return
+ fi
+
+ # VM parameters
+ qemu_cmd="qemu-system-x86_64 -enable-kvm -machine type=q35 -m $ram_size -cpu host -smp 2 -vga virtio"
+ qemu_cmd+=" -device qemu-xhci -device usb-tablet -device usb-kbd -device virtio-net,netdev=user0 -netdev user,id=user0,hostfwd=tcp::5555-:22"
+ qemu_cmd+=" -cdrom \"$iso_path\" -drive file=\"$vm_images_path/$vm_name.qcow2\",index=0,media=disk,if=virtio"
+
+ if [[ $guest_os == "windows" ]]; then
+ qemu_cmd+=" -drive file=\"$iso_images_path/virtio-win.iso\",index=3,media=cdrom"
+ fi
+
+ qemu_cmd+=" -boot menu=on"
+ qemu_cmd+=" -net nic -net user,hostname=$vm_name -name \"$vm_name\""
+
+ echo "Starting VM: $vm_name"
+ eval "$qemu_cmd"
+}
+
+# Main script starts here
+vm_images_path="$HOME/machines/vm"
+iso_images_path="$HOME/machines/images"
+
+# Check if directories exist
+mkdir -p "$vm_images_path"
+mkdir -p "$iso_images_path"
+
+# List available VMs
+list_vms
+
+# List available ISO and IMG files in the images directory
+list_iso_img_files
+
+# Ask the user if they want to use an existing VM or create a new one
+read -p "Do you want to use an existing VM? (y/n): " use_existing_vm
+if [[ $use_existing_vm =~ ^[Yy]$ ]]; then
+ read -p "Enter the name of the existing VM: " existing_vm_name
+ while [ ! -f "$vm_images_path/$existing_vm_name.qcow2" ]; do
+ echo "VM '$existing_vm_name' does not exist."
+ read -p "Enter a valid existing VM name: " existing_vm_name
+ done
+ vm_name=$existing_vm_name
+else
+ # Prompt user for VM parameters
+ get_vm_parameters
+
+ # Check if VM already exists
+ if [ -f "$vm_images_path/$vm_name.qcow2" ]; then
+ read -p "VM '$vm_name' already exists. Do you want to start it? (y/n): " start_vm_choice
+ if [[ $start_vm_choice =~ ^[Yy]$ ]]; then
+ start_vm "$vm_name"
+ exit 0
+ fi
+ else
+ # Create new VM
+ echo "Creating new VM: $vm_name"
+ qemu-img create -f qcow2 "$vm_images_path/$vm_name.qcow2" "$disk_size"
+ start_vm "$vm_name"
+ exit 0
+ fi
+fi
+
+# If an existing VM is selected, ask if the user wants to modify its parameters
+read -p "Do you want to modify the VM parameters? (y/n): " modify_vm_params
+if [[ $modify_vm_params =~ ^[Yy]$ ]]; then
+ get_vm_parameters
+fi
+
+# Start the VM
+start_vm "$vm_name"
+
+echo "Script execution completed."
diff --git a/linux/home/.scripts/random_data.py b/linux/home/.scripts/random_data.py
new file mode 100755
index 0000000..071ab7c
--- /dev/null
+++ b/linux/home/.scripts/random_data.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python3
+
+import os
+import random
+import string
+import json
+import datetime
+import csv
+
+
+def generate_random_string(length, charset=string.ascii_letters):
+ """Generate a random string of given length and character set."""
+ return ''.join(random.choice(charset) for _ in range(length))
+
+
+def generate_random_number(min_value, max_value):
+ """Generate a random number within the specified range."""
+ return random.randint(min_value, max_value)
+
+
+def generate_random_date(start_date, end_date):
+ """Generate a random date within the specified range."""
+ time_between_dates = end_date - start_date
+ days_between_dates = time_between_dates.days
+ random_number_of_days = random.randrange(days_between_dates)
+ random_date = start_date + datetime.timedelta(days=random_number_of_days)
+ return random_date.strftime("%Y-%m-%d")
+
+
+def generate_sql_insert(table_name, columns, num_records):
+ """Generate SQL INSERT statements for populating a table."""
+ sql_statements = []
+ for _ in range(num_records):
+ values = [f"'{generate_random_string(int(input(f'Enter length for {column}: ')))}'" for column in columns]
+ sql = f"INSERT INTO {table_name} ({', '.join(columns)}) VALUES ({', '.join(values)});"
+ sql_statements.append(sql)
+ return sql_statements
+
+
+def generate_placeholder_data(num_records, data_format):
+ """Generate placeholder data based on user-defined format."""
+ placeholder_data = []
+ for _ in range(num_records):
+ record = {}
+ for field, field_data in data_format.items():
+ data_type = field_data['type']
+ if data_type == 'string':
+ length = field_data['length']
+ charset = field_data['charset']
+ record[field] = generate_random_string(length, charset)
+ elif data_type == 'number':
+ min_value = field_data['min']
+ max_value = field_data['max']
+ record[field] = generate_random_number(min_value, max_value)
+ elif data_type == 'date':
+ start_date = datetime.datetime.strptime(field_data['start_date'], "%Y-%m-%d")
+ end_date = datetime.datetime.strptime(field_data['end_date'], "%Y-%m-%d")
+ record[field] = generate_random_date(start_date, end_date)
+ elif data_type == 'boolean':
+ record[field] = random.choice([True, False])
+ placeholder_data.append(record)
+ return placeholder_data
+
+
+def get_data_format_from_user():
+ """Prompt the user for data format preferences."""
+ data_format = {}
+ while True:
+ field = input("Enter field name (or 'done' to finish): ").strip()
+ if field.lower() == 'done':
+ break
+
+ data_type = input(f"Enter data type for '{field}' (string/number/date/boolean): ").strip()
+ if data_type not in ['string', 'number', 'date', 'boolean']:
+ print("Invalid data type. Please enter 'string', 'number', 'date', or 'boolean'.")
+ continue
+
+ if data_type == 'string':
+ length = int(input(f"Enter length for '{field}' (integer): "))
+ charset = input(f"Enter character set for '{field}' (optional, press Enter for default): ").strip()
+ if not charset:
+ charset = string.ascii_letters
+ data_format[field] = {'type': 'string', 'length': length, 'charset': charset}
+ elif data_type == 'number':
+ min_value = int(input(f"Enter minimum value for '{field}' (integer): "))
+ max_value = int(input(f"Enter maximum value for '{field}' (integer): "))
+ data_format[field] = {'type': 'number', 'min': min_value, 'max': max_value}
+ elif data_type == 'date':
+ start_date = input(f"Enter start date for '{field}' (YYYY-MM-DD): ")
+ end_date = input(f"Enter end date for '{field}' (YYYY-MM-DD): ")
+ data_format[field] = {'type': 'date', 'start_date': start_date, 'end_date': end_date}
+ elif data_type == 'boolean':
+ data_format[field] = {'type': 'boolean'}
+
+ return data_format
+
+
+def get_file_type_from_user():
+ """Prompt the user for the desired file type (e.g., JSON, CSV, SQL, TXT, MD, HTML)."""
+ while True:
+ file_type = input("Enter the desired file type for saving the data (json/csv/sql/txt/md/html): ").strip().lower()
+ if file_type in ['json', 'csv', 'sql', 'txt', 'md', 'html']:
+ return file_type
+ else:
+ print("Invalid file type. Please enter 'json', 'csv', 'sql', 'txt', 'md', or 'html'.")
+
+
+def save_data_to_file(data, file_type):
+ """Save the generated data to a file of the specified type."""
+ if file_type == 'json':
+ with open('placeholder_data.json', 'w') as json_file:
+ json.dump(data, json_file, indent=4)
+ elif file_type == 'csv':
+ with open('placeholder_data.csv', 'w', newline='') as csv_file:
+ fieldnames = data[0].keys()
+ writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
+ writer.writeheader()
+ for record in data:
+ writer.writerow(record)
+ elif file_type == 'sql':
+ table_name = input("Enter the SQL table name: ")
+ columns = input("Enter column names separated by commas: ").split(',')
+ sql_statements = generate_sql_insert(table_name, columns, len(data))
+ with open('generated_data.sql', 'w') as sql_file:
+ sql_file.write('\n'.join(sql_statements))
+ elif file_type == 'txt':
+ with open('placeholder_data.txt', 'w') as txt_file:
+ for record in data:
+ txt_file.write(str(record) + '\n')
+ elif file_type == 'md':
+ with open('placeholder_data.md', 'w') as md_file:
+ for record in data:
+ md_file.write('- ' + ', '.join([f"{key}: {value}" for key, value in record.items()]) + '\n')
+ elif file_type == 'html':
+ with open('placeholder_data.html', 'w') as html_file:
+ html_file.write('<html>\n<head>\n<title>Placeholder Data</title>\n</head>\n<body>\n')
+ for record in data:
+ html_file.write('<ul>\n')
+ for key, value in record.items():
+ html_file.write(f'<li>{key}: {value}</li>\n')
+ html_file.write('</ul>\n')
+
+
+if __name__ == "__main__":
+ num_records = int(input("Enter the number of records to generate: "))
+ data_format = get_data_format_from_user()
+ file_type = get_file_type_from_user()
+
+ placeholder_data = generate_placeholder_data(num_records, data_format)
+
+ save_data_to_file(placeholder_data, file_type)
+
+ print(f"Data will be saved to: {os.path.abspath('generated_data.sql')}")
diff --git a/linux/home/.scripts/scratchpad b/linux/home/.scripts/scratchpad
new file mode 100755
index 0000000..8a1aea0
--- /dev/null
+++ b/linux/home/.scripts/scratchpad
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+# Created By: srdusr
+# Created On: Tue 07 Mar 2023 15:06:47 PM CAT
+# Project: Agnostic scratchpad/dropdown terminal that works on most window managers
+
+# Dependencies: wmctrl, xprop, xdo, xdotool
+# NOTE: Ensure script is included in system's path and can therefore be invoked with the command 'scratchpad'.
+# Furthermore make sure the terminal is using x11 as a backend in wayland to allow this to work.
+# Example: wezterm.lua: enable_wayland = false,
+# kitty.conf: linux_display_server x11
+
+# Set the environment variables to x11 to allow working in Wayland
+export GDK_BACKEND=x11
+export QT_QPA_PLATFORM=xcb
+export WAYLAND_DISPLAY=""
+export WINIT_UNIX_BACKEND=x11
+
+# Supported terminals and dropdown class
+supported_terminals=("wezterm" "kitty" "alacritty")
+
+# Check if any supported terminal with scratchpad class is running
+for term in "${supported_terminals[@]}"; do
+ if pgrep -f "$term.*--class scratchpad" >/dev/null; then
+ my_term="$term"
+ break
+ fi
+done
+
+# If no supported terminal is running, start the first available one
+if [ "$my_term" = "" ]; then
+ for term in "${supported_terminals[@]}"; do
+ if command -v "$term" >/dev/null 2>&1; then
+ my_term="$term"
+ break
+ fi
+ done
+ if [ "$my_term" = "" ]; then
+ echo "No supported terminal found." && exit 1
+ fi
+
+ # Start terminal with scratchpad class
+ case "$my_term" in
+ "wezterm") wezterm start --class scratchpad -e tmux new-session -A -s tmux -e bash & ;;
+ "kitty") kitty --class scratchpad tmux new-session -A -s tmux -e bash & ;;
+ "alacritty") alacritty --class scratchpad -e tmux new-session -A -s tmux -e bash & ;;
+ esac
+fi
+
+# Get the window ID of the scratchpad terminal
+id="$(xdo id -N scratchpad)"
+
+# Toggle scratchpad terminal visibility
+if [ "$id" != "" ]; then
+ if xwininfo -id "$id" | grep "Map State: IsViewable" >/dev/null; then
+ # Scratchpad is visible, hide it
+ dimensions="$(xwininfo -id "$id" | awk '/Width:|Height:/ { printf("%s=%s;", tolower($1), $2) }')"
+ xdo hide "$id" 2>/dev/null
+ else
+ # Scratchpad is hidden, show it and restore dimensions
+ xdo show "$id"
+ xdotool windowsize "$id" "$(echo "$dimensions" | tr ';' ' ')" 2>/dev/null
+ fi
+fi
diff --git a/linux/home/.scripts/spec b/linux/home/.scripts/spec
new file mode 100755
index 0000000..19810fc
--- /dev/null
+++ b/linux/home/.scripts/spec
@@ -0,0 +1,73 @@
+#!/usr/bin/env bash
+
+# Created By: srdusr
+# Created On: Wed 18 Oct 2023 20:19:03 CAT
+# Project: Create Spectrograms of audio files
+
+# Dependencies: sox
+
+# Define the timestamp function
+timestamp() {
+ date +%Y%m%d%H%M%S
+}
+
+spec() {
+
+ if [[ $# -eq 0 ]]; then
+ echo "No audio files provided."
+ return
+ fi
+
+ local outdir
+
+ if [[ -d "$HOME/pictures" ]]; then
+ outdir="$HOME/pictures/spectrograms"
+ elif [[ -d "$HOME/Pictures" ]]; then
+ outdir="$HOME/Pictures/Spectrograms"
+ elif [[ -d "$HOME/Images" ]]; then
+ outdir="$HOME/Images/Spectrograms"
+ else
+ outdir="./spectrograms" # Save to the current directory if none of the expected directories exist
+ fi
+
+ for file in "$@"; do
+ if [[ -f "$file" ]]; then
+ local filename
+ filename=$(basename "$file")
+ local filename_no_extension="${filename%.*}"
+ local target_dir="$outdir"
+ local outname="$target_dir/sox-spec-$(timestamp)-${filename_no_extension}.png"
+
+ if [[ ! -d "$target_dir" ]]; then
+ mkdir -p "$target_dir" # Create the directory if it doesn't exist
+ fi
+
+ sox "$file" -S -n spectrogram -o "$outname"
+
+ # Add the generated spectrogram file name to the array
+ spectrogram_files+=("$outname")
+ else
+ echo "File not found: $file"
+ fi
+ done
+
+ if [[ ${#} -gt 0 ]]; then
+ read -p "Do you want to open the spectrogram(s)? (y/n): " open_choice
+ case "$open_choice" in
+ [Yy])
+ for file in "${spectrogram_files[@]}"; do
+ xdg-open "$file" # Open the spectrogram images generated from the provided audio files
+ done
+ ;;
+ [Nn])
+ echo "Not opening the spectrogram(s)."
+ ;;
+ *)
+ echo "Invalid choice. Not opening the spectrogram(s)."
+ ;;
+ esac
+ fi
+}
+
+# Call the spec function with provided arguments
+spec "$@"
diff --git a/linux/home/.scripts/track-books.sh b/linux/home/.scripts/track-books.sh
new file mode 100755
index 0000000..f13add8
--- /dev/null
+++ b/linux/home/.scripts/track-books.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Created By: srdusr
+# Created On: Wed 25 Oct 2023 13:45:52 CAT
+# Project: Simple script to track most recent books opened, mainly for neovim usage.
+
+# Dependencies: inotify-tools
+
+books_directory="$HOME/documents/books"
+recent_books_file="$HOME/.config/nvim/tmp/recent_books.txt"
+
+inotifywait -m -e CREATE -e OPEN -r "$books_directory" |
+ while read -r path action file; do
+ if [[ $file == *.pdf || $file == *.epub ]]; then
+ echo "$path/$file" >>"$recent_books_file"
+ # Remove duplicates and overwrite the recent_books_file
+ sort -u -o "$recent_books_file" "$recent_books_file"
+ fi
+ done
diff --git a/linux/home/.scripts/win-nvim.bat b/linux/home/.scripts/win-nvim.bat
new file mode 100644
index 0000000..c99374d
--- /dev/null
+++ b/linux/home/.scripts/win-nvim.bat
@@ -0,0 +1,37 @@
+@echo off
+
+REM Install NeoVim with winget, if not already present on the system
+where nvim >nul 2>nul
+if %errorlevel% neq 0 (
+ winget install Neovim.Neovim -q
+)
+
+REM Clone my dotfiles repo
+set dotFilesRoot=%USERPROFILE%\dotfiles
+if not exist "%dotFilesRoot%\." (
+ git clone git@github.com:srdusr/dotfiles.git "%dotFilesRoot%"
+)
+
+REM Link NeoVim configuration
+set localConfiguration=%LOCALAPPDATA%\nvim
+set dotfilesConfiguration=%dotFilesRoot%\.config\nvim
+
+if not exist "%localConfiguration%\." (
+ mklink /D "%localConfiguration%" "%dotfilesConfiguration%"
+)
+
+REM Clone Packer.nvim, if not already present on the system
+set localPacker=%LOCALAPPDATA%\nvim-data\site\pack\packer\start\packer.nvim
+
+if not exist "%localPacker%\." (
+ git clone https://github.com/wbthomason/packer.nvim "%localPacker%"
+)
+
+REM Run the script by using this command in the same existing directory: win-nvim.bat
+
+@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://aka.ms/install-winget'))"
+iex ((new-object net.webclient).DownloadString('https://aka.ms/install-winget'))
+curl -o winget-cli.appxbundle https://aka.ms/winget-cli-appxbundle
+
+powershell Add-AppxPackage -Path "winget-cli.appxbundle"
+
diff --git a/linux/home/.scripts/win-nvim.ps1 b/linux/home/.scripts/win-nvim.ps1
new file mode 100644
index 0000000..ca67755
--- /dev/null
+++ b/linux/home/.scripts/win-nvim.ps1
@@ -0,0 +1,39 @@
+# Install NeoVim with winget, if not already present on the system
+if (!(Get-Command nvim -ErrorAction SilentlyContinue)) {
+ winget install Neovim.Neovim
+}
+
+# Clone my dotfiles repo
+$dotFilesRoot = Join-Path $HOME "dotfiles"
+
+if (!(Test-Path $dotFilesRoot -PathType Container)) {
+ git clone https://github.com/srdusr/dotfiles.git $dotFilesRoot
+}
+
+# Link NeoVim configuration
+$localConfiguration = Join-Path $env:LOCALAPPDATA "nvim"
+$dotfilesConfiguration = Join-Path $dotFilesRoot ".config" "nvim"
+
+if (!(Test-Path $localConfiguration -PathType Container)) {
+ Start-Process -FilePath "cmd.exe" -ArgumentList "/c mklink /D $localConfiguration $dotfilesConfiguration" -Verb runas
+}
+
+# Clone Packer.nvim, if not already present on the system
+$localPacker = Join-Path $env:LOCALAPPDATA "nvim-data" "site" "pack" "packer" "start" "packer.nvim"
+
+if (!(Test-Path $localPacker -PathType Container)) {
+ git clone https://github.com/wbthomason/packer.nvim $localPacker
+}
+
+# To allow script execution, run the following command in PowerShell as an administrator:
+# Set-ExecutionPolicy RemoteSigned
+# Then run the script by using this command in the same existing directory:
+# ./win-nvim.ps1
+#curl -o winget-cli.appxbundle https://aka.ms/winget-cli-appxbundle
+#powershell Add-AppxPackage -Path "winget-cli.appxbundle"
+#Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
+#use `-y` or consider: choco feature enable -n allowGlobalConfirmation
+#choco install git
+#- Refresh the environment
+#Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1
+#refreshenv
diff --git a/linux/home/.vim/autoload/autoload/statusline.vim b/linux/home/.vim/autoload/autoload/statusline.vim
new file mode 100644
index 0000000..bf5f972
--- /dev/null
+++ b/linux/home/.vim/autoload/autoload/statusline.vim
@@ -0,0 +1,267 @@
+" statusline.vim
+
+if exists('g:loaded_statusline') | finish | endif
+let g:loaded_statusline = 1
+
+" --- Detect Nerd Fonts ---
+function! s:HasNerdFonts()
+ if exists('g:statusline_nerd_fonts')
+ return g:statusline_nerd_fonts
+ endif
+
+ if executable('fc-list')
+ let l:output = system('fc-list | grep -i nerd')
+ if len(split(l:output, '\n')) > 0
+ return 1
+ endif
+ endif
+
+ return 0
+endfunction
+
+let g:statusline_has_nerd_fonts = s:HasNerdFonts()
+
+" --- Color Palette ---
+let g:StslineColorGreen = '#2BBB4F'
+let g:StslineColorBlue = '#4799EB'
+let g:StslineColorViolet = '#986FEC'
+let g:StslineColorYellow = '#D7A542'
+let g:StslineColorOrange = '#EB754D'
+let g:StslineColorLight = '#C0C0C0'
+let g:StslineColorDark = '#080808'
+let g:StslineColorDark1 = '#181818'
+let g:StslineColorDark2 = 'NONE'
+let g:StslineColorDark3 = '#303030'
+
+let g:StslineBackColor = g:StslineColorDark2
+let g:StslineOnBackColor = g:StslineColorLight
+let g:StslinePriColor = g:StslineColorGreen
+let g:StslineOnPriColor = g:StslineColorDark
+let g:StslineSecColor = g:StslineColorDark3
+let g:StslineOnSecColor = g:StslineColorLight
+
+" --- Highlight Groups ---
+" Initial setup of highlight groups (will be updated by UpdateStslineColors)
+execute 'highlight StslinePriColorBG guifg=' . g:StslineOnPriColor . ' guibg=' . g:StslinePriColor
+execute 'highlight StslineSecColorFG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+execute 'highlight StslineSecColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineBackColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineBackColor
+execute 'highlight StslineBackColorFGSecColorBG guifg=' . g:StslineBackColor . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineSecColorFGBackColorBG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+execute 'highlight StslineModColorFG guifg=' . g:StslineColorYellow . ' guibg=' . g:StslineBackColor
+execute 'highlight StslinePriColorBG_SecColorBG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineModeSep guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineGitSep guifg=' . g:StslineSecColor . ' guibg=' . g:StslineColorDark2
+
+" --- Statusline Settings ---
+if has('nvim')
+ set laststatus=3
+else
+ set laststatus=2
+endif
+
+"set noshowmode
+"set termguicolors
+
+let space = ''
+
+" Get Statusline mode & also set primary color for that mode
+function! autoload#statusline#StslineMode() abort
+ let l:CurrentMode = mode()
+
+ if l:CurrentMode ==# 'n'
+ let g:StslinePriColor = g:StslineColorGreen
+ let b:CurrentMode = 'NORMAL '
+ elseif l:CurrentMode ==# 'i'
+ let g:StslinePriColor = g:StslineColorViolet
+ let b:CurrentMode = 'INSERT '
+ elseif l:CurrentMode ==# 'c'
+ let g:StslinePriColor = g:StslineColorYellow
+ let b:CurrentMode = 'COMMAND'
+ elseif l:CurrentMode ==# 'v'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'VISUAL '
+ elseif l:CurrentMode ==# '\<C-v>'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'V-BLOCK'
+ elseif l:CurrentMode ==# 'V'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'V-LINE '
+ elseif l:CurrentMode ==# 'R'
+ let g:StslinePriColor = g:StslineColorViolet
+ let b:CurrentMode = 'REPLACE'
+ elseif l:CurrentMode ==# 's'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'SELECT '
+ elseif l:CurrentMode ==# 't'
+ let g:StslinePriColor = g:StslineColorYellow
+ let b:CurrentMode = 'TERM '
+ elseif l:CurrentMode ==# '!'
+ let g:StslinePriColor = g:StslineColorYellow
+ let b:CurrentMode = 'SHELL '
+ else
+ let g:StslinePriColor = g:StslineColorGreen
+ endif
+
+ call autoload#statusline#UpdateStslineColors()
+
+ return b:CurrentMode
+endfunction
+
+function! autoload#statusline#UpdateStslineColors() abort
+ execute 'highlight StslinePriColorBG guifg=' . g:StslineOnPriColor . ' guibg=' . g:StslinePriColor
+ execute 'highlight StslinePriColorBGBold guifg=' . g:StslineOnPriColor . ' guibg=' . g:StslinePriColor . ' gui=bold'
+ execute 'highlight StslinePriColorFG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslinePriColorFGSecColorBG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineModeSep guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineGitSep guifg=' . g:StslineSecColor . ' guibg=' . g:StslineColorDark2
+ execute 'highlight StslineSecColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineBackColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslineBackColorFGSecColorBG guifg=' . g:StslineBackColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineSecColorFGBackColorBG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslineModColorFG guifg=' . g:StslineColorYellow . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslinePriColorBG_SecColorBG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineSecColorFG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+endfunction
+
+function! autoload#statusline#GetGitBranch() abort
+ let b:GitBranch = ''
+ try
+ let l:dir = expand('%:p:h')
+ let l:gitrevparse = system("git -C ".l:dir." rev-parse --abbrev-ref HEAD")
+ if !v:shell_error
+ let icon = g:statusline_has_nerd_fonts ? '  ' : ' [git] '
+ let b:GitBranch = icon . substitute(l:gitrevparse, '\n', '', 'g') . ' '
+ endif
+ catch
+ endtry
+endfunction
+
+function! autoload#statusline#GetFileType() abort
+ if !g:statusline_has_nerd_fonts
+ let b:FiletypeIcon = ''
+ return
+ endif
+ if &filetype ==# 'typescript' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'html' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'scss' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'css' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'javascript' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'javascriptreact' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'markdown' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'sh' || &filetype ==# 'zsh' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'vim' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'rust' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'ruby' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'cpp' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'c' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'go' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'lua' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'haskell' | let b:FiletypeIcon = ' '
+ else | let b:FiletypeIcon = ' '
+ endif
+endfunction
+
+function! autoload#statusline#ActivateStatusline() abort
+ call autoload#statusline#GetFileType()
+ call autoload#statusline#GetGitBranch() " Ensure git branch is updated
+
+ let current_mode_str = autoload#statusline#StslineMode()
+ call autoload#statusline#UpdateStslineColors()
+
+ let readonly_icon = g:statusline_has_nerd_fonts ? ' ' : '[RO] '
+ let modified_icon = g:statusline_has_nerd_fonts ? ' ' : '[+] '
+ let git_sep = g:statusline_has_nerd_fonts ? '' : ' '
+ let file_sep1 = g:statusline_has_nerd_fonts ? ' ' : ' '
+ let file_sep2 = g:statusline_has_nerd_fonts ? '' : ''
+
+ " Get dynamic parts as simple strings
+ let git_status_str = get(b:, "coc_git_status", get(b:, "GitBranch", ""))
+ let git_blame_str = get(b:, "coc_git_blame", "")
+ let filetype_icon_str = get(b:, "FiletypeIcon", "")
+ let file_encoding_str = ''
+ if &fenc != "utf-8"
+ let file_encoding_str = &fenc . ' '
+ endif
+
+ " Build the statusline as a static string
+ let l:statusline = ''
+
+ let l:statusline .= '%#StslinePriColorBG# ' . current_mode_str . ''
+ let l:statusline .= '%#StslineModeSep#' . git_sep
+ let l:statusline .= '%#StslineSecColorBG#' . git_status_str . git_blame_str
+ let l:statusline .= '%#StslineGitSep#' . git_sep
+
+ " File info (Readonly, Modified, Filename)
+ let l:statusline .= '%#StslinePriColorFG#'
+ if &readonly
+ let l:statusline .= readonly_icon
+ endif
+ let l:statusline .= ' %F '
+ if &modified
+ let l:statusline .= modified_icon
+ endif
+
+ " Right align everything after this
+ let l:statusline .= '%='
+
+ " Right side (Filetype, Encoding, Position)
+ let l:statusline .= '%#StslinePriColorFG# ' . filetype_icon_str . '%y'
+ let l:statusline .= '%#StslineSecColorFG#' . file_sep1
+ "let l:statusline .= '%#StslineSecColorBG# ' . file_encoding_str
+ let l:statusline .= '%#StslinePriColorFGSecColorBG#' . file_sep2
+ let l:statusline .= '%#StslinePriColorBG# %p%% %#StslinePriColorBGBold#%l%#StslinePriColorBG#/%L :%c '
+ let l:statusline .= '%#StslineBackColorBG#'
+
+ " Set the statusline for the current buffer
+ let &l:statusline = l:statusline
+endfunction
+
+function! autoload#statusline#DeactivateStatusline() abort
+ let git_sep = g:statusline_has_nerd_fonts ? '' : ''
+ let readonly_icon = g:statusline_has_nerd_fonts ? ' ' : '[RO] '
+ let modified_icon = g:statusline_has_nerd_fonts ? ' ' : '[+] '
+
+ " NOTE: This DeactivateStatusline function still uses %{} for dynamic parts.
+ " If you encounter general E518 or other issues related to %{} expressions,
+ " you will need to refactor this function to build a static string
+ " similar to how ActivateStatusline now does it.
+ if !exists("b:GitBranch") || b:GitBranch == ''
+ let statusline =
+ \ '%#StslineSecColorBG# INACTIVE ' .
+ \ '%{get(b:,"coc_git_statusline",b:GitBranch)}%{get(b:,"coc_git_blame","")}' .
+ \ '%#StslineBackColorFGSecColorBG#' . git_sep .
+ \ '%#StslineBackColorBG# %{&readonly?"' . readonly_icon . '":""}%F ' .
+ \ '%#StslineModColorFG#%{&modified?"' . modified_icon . '":""}' .
+ \ '%=%#StslineBackColorBG# %{b:FiletypeIcon}%{&filetype}' .
+ \ '%#StslineSecColorFGBackColorBG# | %p%% %l/%L :%c'
+ else
+ let statusline =
+ \ '%#StslineSecColorBG# %{get(b:,"coc_git_statusline",b:GitBranch)}%{get(b:,"coc_git_blame","")}' .
+ \ '%#StslineBackColorFGSecColorBG#' . git_sep .
+ \ '%#StslineBackColorBG# %{&readonly?"' . readonly_icon . '":""}%F ' .
+ \ '%#StslineModColorFG#%{&modified?"' . modified_icon . '":""}' .
+ \ '%=%#StslineBackColorBG# %{b:FiletypeIcon}%{&filetype}' .
+ \ '%#StslineSecColorFGBackColorBG# | %p%% %l/%L :%c'
+ endif
+
+ execute 'setlocal statusline=' . substitute(statusline, '"', '\\"', 'g')
+endfunction
+
+augroup StatuslineGit
+ autocmd!
+ autocmd BufEnter * call autoload#statusline#GetGitBranch()
+augroup END
+
+augroup SetStsline
+ autocmd!
+ autocmd BufEnter,WinEnter * call autoload#statusline#ActivateStatusline()
+ autocmd ModeChanged * call autoload#statusline#ActivateStatusline()
+augroup END
+
+augroup StatuslineAutoReload
+ autocmd!
+ autocmd BufWritePost statusline.vim source <afile> | call autoload#statusline#ActivateStatusline()
+augroup END
+
+"call autoload#statusline#ActivateStatusline()
diff --git a/linux/home/.vim/autoload/statusline.vim b/linux/home/.vim/autoload/statusline.vim
new file mode 100644
index 0000000..bf5f972
--- /dev/null
+++ b/linux/home/.vim/autoload/statusline.vim
@@ -0,0 +1,267 @@
+" statusline.vim
+
+if exists('g:loaded_statusline') | finish | endif
+let g:loaded_statusline = 1
+
+" --- Detect Nerd Fonts ---
+function! s:HasNerdFonts()
+ if exists('g:statusline_nerd_fonts')
+ return g:statusline_nerd_fonts
+ endif
+
+ if executable('fc-list')
+ let l:output = system('fc-list | grep -i nerd')
+ if len(split(l:output, '\n')) > 0
+ return 1
+ endif
+ endif
+
+ return 0
+endfunction
+
+let g:statusline_has_nerd_fonts = s:HasNerdFonts()
+
+" --- Color Palette ---
+let g:StslineColorGreen = '#2BBB4F'
+let g:StslineColorBlue = '#4799EB'
+let g:StslineColorViolet = '#986FEC'
+let g:StslineColorYellow = '#D7A542'
+let g:StslineColorOrange = '#EB754D'
+let g:StslineColorLight = '#C0C0C0'
+let g:StslineColorDark = '#080808'
+let g:StslineColorDark1 = '#181818'
+let g:StslineColorDark2 = 'NONE'
+let g:StslineColorDark3 = '#303030'
+
+let g:StslineBackColor = g:StslineColorDark2
+let g:StslineOnBackColor = g:StslineColorLight
+let g:StslinePriColor = g:StslineColorGreen
+let g:StslineOnPriColor = g:StslineColorDark
+let g:StslineSecColor = g:StslineColorDark3
+let g:StslineOnSecColor = g:StslineColorLight
+
+" --- Highlight Groups ---
+" Initial setup of highlight groups (will be updated by UpdateStslineColors)
+execute 'highlight StslinePriColorBG guifg=' . g:StslineOnPriColor . ' guibg=' . g:StslinePriColor
+execute 'highlight StslineSecColorFG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+execute 'highlight StslineSecColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineBackColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineBackColor
+execute 'highlight StslineBackColorFGSecColorBG guifg=' . g:StslineBackColor . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineSecColorFGBackColorBG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+execute 'highlight StslineModColorFG guifg=' . g:StslineColorYellow . ' guibg=' . g:StslineBackColor
+execute 'highlight StslinePriColorBG_SecColorBG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineModeSep guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineGitSep guifg=' . g:StslineSecColor . ' guibg=' . g:StslineColorDark2
+
+" --- Statusline Settings ---
+if has('nvim')
+ set laststatus=3
+else
+ set laststatus=2
+endif
+
+"set noshowmode
+"set termguicolors
+
+let space = ''
+
+" Get Statusline mode & also set primary color for that mode
+function! autoload#statusline#StslineMode() abort
+ let l:CurrentMode = mode()
+
+ if l:CurrentMode ==# 'n'
+ let g:StslinePriColor = g:StslineColorGreen
+ let b:CurrentMode = 'NORMAL '
+ elseif l:CurrentMode ==# 'i'
+ let g:StslinePriColor = g:StslineColorViolet
+ let b:CurrentMode = 'INSERT '
+ elseif l:CurrentMode ==# 'c'
+ let g:StslinePriColor = g:StslineColorYellow
+ let b:CurrentMode = 'COMMAND'
+ elseif l:CurrentMode ==# 'v'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'VISUAL '
+ elseif l:CurrentMode ==# '\<C-v>'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'V-BLOCK'
+ elseif l:CurrentMode ==# 'V'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'V-LINE '
+ elseif l:CurrentMode ==# 'R'
+ let g:StslinePriColor = g:StslineColorViolet
+ let b:CurrentMode = 'REPLACE'
+ elseif l:CurrentMode ==# 's'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'SELECT '
+ elseif l:CurrentMode ==# 't'
+ let g:StslinePriColor = g:StslineColorYellow
+ let b:CurrentMode = 'TERM '
+ elseif l:CurrentMode ==# '!'
+ let g:StslinePriColor = g:StslineColorYellow
+ let b:CurrentMode = 'SHELL '
+ else
+ let g:StslinePriColor = g:StslineColorGreen
+ endif
+
+ call autoload#statusline#UpdateStslineColors()
+
+ return b:CurrentMode
+endfunction
+
+function! autoload#statusline#UpdateStslineColors() abort
+ execute 'highlight StslinePriColorBG guifg=' . g:StslineOnPriColor . ' guibg=' . g:StslinePriColor
+ execute 'highlight StslinePriColorBGBold guifg=' . g:StslineOnPriColor . ' guibg=' . g:StslinePriColor . ' gui=bold'
+ execute 'highlight StslinePriColorFG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslinePriColorFGSecColorBG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineModeSep guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineGitSep guifg=' . g:StslineSecColor . ' guibg=' . g:StslineColorDark2
+ execute 'highlight StslineSecColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineBackColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslineBackColorFGSecColorBG guifg=' . g:StslineBackColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineSecColorFGBackColorBG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslineModColorFG guifg=' . g:StslineColorYellow . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslinePriColorBG_SecColorBG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineSecColorFG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+endfunction
+
+function! autoload#statusline#GetGitBranch() abort
+ let b:GitBranch = ''
+ try
+ let l:dir = expand('%:p:h')
+ let l:gitrevparse = system("git -C ".l:dir." rev-parse --abbrev-ref HEAD")
+ if !v:shell_error
+ let icon = g:statusline_has_nerd_fonts ? '  ' : ' [git] '
+ let b:GitBranch = icon . substitute(l:gitrevparse, '\n', '', 'g') . ' '
+ endif
+ catch
+ endtry
+endfunction
+
+function! autoload#statusline#GetFileType() abort
+ if !g:statusline_has_nerd_fonts
+ let b:FiletypeIcon = ''
+ return
+ endif
+ if &filetype ==# 'typescript' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'html' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'scss' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'css' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'javascript' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'javascriptreact' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'markdown' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'sh' || &filetype ==# 'zsh' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'vim' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'rust' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'ruby' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'cpp' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'c' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'go' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'lua' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'haskell' | let b:FiletypeIcon = ' '
+ else | let b:FiletypeIcon = ' '
+ endif
+endfunction
+
+function! autoload#statusline#ActivateStatusline() abort
+ call autoload#statusline#GetFileType()
+ call autoload#statusline#GetGitBranch() " Ensure git branch is updated
+
+ let current_mode_str = autoload#statusline#StslineMode()
+ call autoload#statusline#UpdateStslineColors()
+
+ let readonly_icon = g:statusline_has_nerd_fonts ? ' ' : '[RO] '
+ let modified_icon = g:statusline_has_nerd_fonts ? ' ' : '[+] '
+ let git_sep = g:statusline_has_nerd_fonts ? '' : ' '
+ let file_sep1 = g:statusline_has_nerd_fonts ? ' ' : ' '
+ let file_sep2 = g:statusline_has_nerd_fonts ? '' : ''
+
+ " Get dynamic parts as simple strings
+ let git_status_str = get(b:, "coc_git_status", get(b:, "GitBranch", ""))
+ let git_blame_str = get(b:, "coc_git_blame", "")
+ let filetype_icon_str = get(b:, "FiletypeIcon", "")
+ let file_encoding_str = ''
+ if &fenc != "utf-8"
+ let file_encoding_str = &fenc . ' '
+ endif
+
+ " Build the statusline as a static string
+ let l:statusline = ''
+
+ let l:statusline .= '%#StslinePriColorBG# ' . current_mode_str . ''
+ let l:statusline .= '%#StslineModeSep#' . git_sep
+ let l:statusline .= '%#StslineSecColorBG#' . git_status_str . git_blame_str
+ let l:statusline .= '%#StslineGitSep#' . git_sep
+
+ " File info (Readonly, Modified, Filename)
+ let l:statusline .= '%#StslinePriColorFG#'
+ if &readonly
+ let l:statusline .= readonly_icon
+ endif
+ let l:statusline .= ' %F '
+ if &modified
+ let l:statusline .= modified_icon
+ endif
+
+ " Right align everything after this
+ let l:statusline .= '%='
+
+ " Right side (Filetype, Encoding, Position)
+ let l:statusline .= '%#StslinePriColorFG# ' . filetype_icon_str . '%y'
+ let l:statusline .= '%#StslineSecColorFG#' . file_sep1
+ "let l:statusline .= '%#StslineSecColorBG# ' . file_encoding_str
+ let l:statusline .= '%#StslinePriColorFGSecColorBG#' . file_sep2
+ let l:statusline .= '%#StslinePriColorBG# %p%% %#StslinePriColorBGBold#%l%#StslinePriColorBG#/%L :%c '
+ let l:statusline .= '%#StslineBackColorBG#'
+
+ " Set the statusline for the current buffer
+ let &l:statusline = l:statusline
+endfunction
+
+function! autoload#statusline#DeactivateStatusline() abort
+ let git_sep = g:statusline_has_nerd_fonts ? '' : ''
+ let readonly_icon = g:statusline_has_nerd_fonts ? ' ' : '[RO] '
+ let modified_icon = g:statusline_has_nerd_fonts ? ' ' : '[+] '
+
+ " NOTE: This DeactivateStatusline function still uses %{} for dynamic parts.
+ " If you encounter general E518 or other issues related to %{} expressions,
+ " you will need to refactor this function to build a static string
+ " similar to how ActivateStatusline now does it.
+ if !exists("b:GitBranch") || b:GitBranch == ''
+ let statusline =
+ \ '%#StslineSecColorBG# INACTIVE ' .
+ \ '%{get(b:,"coc_git_statusline",b:GitBranch)}%{get(b:,"coc_git_blame","")}' .
+ \ '%#StslineBackColorFGSecColorBG#' . git_sep .
+ \ '%#StslineBackColorBG# %{&readonly?"' . readonly_icon . '":""}%F ' .
+ \ '%#StslineModColorFG#%{&modified?"' . modified_icon . '":""}' .
+ \ '%=%#StslineBackColorBG# %{b:FiletypeIcon}%{&filetype}' .
+ \ '%#StslineSecColorFGBackColorBG# | %p%% %l/%L :%c'
+ else
+ let statusline =
+ \ '%#StslineSecColorBG# %{get(b:,"coc_git_statusline",b:GitBranch)}%{get(b:,"coc_git_blame","")}' .
+ \ '%#StslineBackColorFGSecColorBG#' . git_sep .
+ \ '%#StslineBackColorBG# %{&readonly?"' . readonly_icon . '":""}%F ' .
+ \ '%#StslineModColorFG#%{&modified?"' . modified_icon . '":""}' .
+ \ '%=%#StslineBackColorBG# %{b:FiletypeIcon}%{&filetype}' .
+ \ '%#StslineSecColorFGBackColorBG# | %p%% %l/%L :%c'
+ endif
+
+ execute 'setlocal statusline=' . substitute(statusline, '"', '\\"', 'g')
+endfunction
+
+augroup StatuslineGit
+ autocmd!
+ autocmd BufEnter * call autoload#statusline#GetGitBranch()
+augroup END
+
+augroup SetStsline
+ autocmd!
+ autocmd BufEnter,WinEnter * call autoload#statusline#ActivateStatusline()
+ autocmd ModeChanged * call autoload#statusline#ActivateStatusline()
+augroup END
+
+augroup StatuslineAutoReload
+ autocmd!
+ autocmd BufWritePost statusline.vim source <afile> | call autoload#statusline#ActivateStatusline()
+augroup END
+
+"call autoload#statusline#ActivateStatusline()
diff --git a/linux/home/.vim/colors/colors/colorscheme.vim b/linux/home/.vim/colors/colors/colorscheme.vim
new file mode 100644
index 0000000..ce0526e
--- /dev/null
+++ b/linux/home/.vim/colors/colors/colorscheme.vim
@@ -0,0 +1,247 @@
+" Vim Colorscheme
+" Name: cherryblossom.vim
+" Author: Luo Boming
+" Version: 0.3
+" License: The MIT Licence
+
+"{{{ Pre-setting
+let g:colors_name = expand('<sfile>:t:r')
+
+"hi clear
+"if exists("syntax_on")
+" syntax reset
+"endif
+
+if ! exists("g:terminal_italics")
+ let g:terminal_italics = 0
+endif
+
+"if ! exists("g:switch_statusline_bg_in_insert")
+" let g:switch_statusline_bg_in_insert = 0
+"endif
+
+if ! exists("g:spell_undercurl")
+ let g:spell_undercurl = 0
+endif
+
+"}}}
+"{{{ Color Palette
+" Color Entity
+let s:black = { "gui": "#171717", "cterm": "16" }
+let s:white = { "gui": "#EAE8E7", "cterm": "231" }
+
+let s:gray = { "gui": "#3a3f52", "cterm": "247" }
+
+let s:green = { "gui": "#30B536", "cterm": "34" }
+let s:pink = { "gui": "#D36DD3", "cterm": "170" }
+let s:orange = { "gui": "#FC923F", "cterm": "208" }
+let s:purple = { "gui": "#B586E7", "cterm": "141" }
+let s:light_cyan = { "gui": "#D7FFFF", "cterm": "195" }
+let s:dark_cyan = { "gui": "#00AF87", "cterm": "36" }
+let s:ultramarine = { "gui": "#229EC0", "cterm": "38" }
+let s:skyblue = { "gui": "#9BE7F8", "cterm": "195" }
+
+let s:white_pink = { "gui": "#FEF7FE", "cterm": "231" }
+let s:white_pink_deep = { "gui": "#FEF0FE", "cterm": "255" }
+let s:black_green = { "gui": "#053703", "cterm": "235" }
+let s:black_green_bright = { "gui": "#074005", "cterm": "239" }
+let s:middle_gray = { "gui": "#8a8a8a", "cterm": "245" }
+
+let s:light_gray = { "gui": "#E1DCDA", "cterm": "253" }
+let s:light_green = { "gui": "#B7EFA5", "cterm": "157" }
+let s:light_pink = { "gui": "#FEDCFE", "cterm": "225" }
+let s:light_yellow = { "gui": "#EDE682", "cterm": "228" }
+let s:light_red = { "gui": "#EB5A7C", "cterm": "204" }
+
+let s:dark_gray = { "gui": "#4D4A48", "cterm": "241" }
+let s:dark_green = { "gui": "#09570A", "cterm": "22" }
+let s:dark_yellow = { "gui": "#BC922B", "cterm": "3" }
+let s:dark_pink = { "gui": "#B365A2", "cterm": "133" }
+let s:dark_red = { "gui": "#D9372D", "cterm": "160" }
+let s:NONE = { "gui": "NONE", "cterm": "NONE" }
+
+" Color Alias
+if &background == "light"
+ let s:norm = s:black
+ let s:bg = s:NONE
+ let s:bg_subtle = s:white_pink_deep
+ let s:gray_fg = s:middle_gray
+ let s:green_fg = s:green
+ let s:yellow_fg = s:dark_yellow
+ let s:pink_fg = s:dark_pink
+ let s:cyan_fg = s:dark_cyan
+ let s:blue_fg = s:ultramarine
+ let s:red_fg = s:dark_red
+ let s:gray_bg = s:light_gray
+ let s:green_bg = s:light_green
+ let s:yellow_bg = s:light_yellow
+ let s:pink_bg = s:light_pink
+ let s:cyan_bg = s:light_cyan
+ let s:blue_bg = s:skyblue
+ let s:red_bg = s:light_red
+endif
+
+if &background == "dark"
+ let s:norm = s:white
+ let s:bg = s:NONE
+ let s:bg_subtle = s:gray
+ let s:gray_fg = s:middle_gray
+ let s:green_fg = s:light_green
+ let s:yellow_fg = s:light_yellow
+ let s:pink_fg = s:light_pink
+ let s:cyan_fg = s:light_cyan
+ let s:blue_fg = s:skyblue
+ let s:red_fg = s:light_red
+ let s:gray_bg = s:dark_gray
+ let s:green_bg = s:green
+ let s:yellow_bg = s:dark_yellow
+ let s:pink_bg = s:pink
+ let s:cyan_bg = s:dark_cyan
+ let s:blue_bg = s:ultramarine
+ let s:red_bg = s:dark_red
+endif
+"}}}
+"{{{ Highlight Function
+" shamelessly stolen from pencil: https://github.com/reedes/vim-colors-pencil
+function! s:hi(group, style)
+ if g:terminal_italics == 0
+ if has_key(a:style, "cterm") && a:style["cterm"] == "italic"
+ unlet a:style.cterm
+ endif
+ if has_key(a:style, "term") && a:style["term"] == "italic"
+ unlet a:style.term
+ endif
+ endif
+ execute "highlight" a:group
+ \ "guifg=" (has_key(a:style, "fg") ? a:style.fg.gui : "NONE")
+ \ "guibg=" (has_key(a:style, "bg") ? a:style.bg.gui : "NONE")
+ \ "guisp=" (has_key(a:style, "sp") ? a:style.sp.gui : "NONE")
+ \ "gui=" (has_key(a:style, "gui") ? a:style.gui : "NONE")
+ \ "ctermfg=" (has_key(a:style, "fg") ? a:style.fg.cterm : "NONE")
+ \ "ctermbg=" (has_key(a:style, "bg") ? a:style.bg.cterm : "NONE")
+ \ "cterm=" (has_key(a:style, "cterm") ? a:style.cterm : "NONE")
+ \ "term=" (has_key(a:style, "term") ? a:style.term : "NONE")
+endfunction
+
+if g:spell_undercurl == 1
+ let s:attr_un = 'undercurl'
+else
+ let s:attr_un = 'underline'
+endif
+
+"}}}
+"{{{ Common Highlighting
+call s:hi("Normal", {"fg": s:norm, "bg": s:bg})
+call s:hi("Cursor", {})
+call s:hi("Comment", {"fg": s:gray_fg, "gui": "italic", "cterm": "italic", "term": "italic"})
+
+call s:hi("Constant", {"fg": s:pink_fg})
+hi! link String Constant
+hi! link Character Constant
+hi! link Number Constant
+hi! link Boolean Constant
+hi! link Float Constant
+
+call s:hi("Identifier", {"fg": s:red_fg})
+hi! link Function Identifier
+
+call s:hi("Statement", {"fg": s:green_fg})
+hi! link Conditonal Statement
+hi! link Repeat Statement
+hi! link Label Statement
+hi! link Operator Statement
+hi! link Keyword Statement
+hi! link Exception Statement
+
+call s:hi("PreProc", {"fg": s:blue_fg})
+hi! link Include PreProc
+hi! link Define PreProc
+hi! link Macro PreProc
+hi! link PreCondit PreProc
+
+call s:hi("Type", {"fg": s:yellow_fg})
+hi! link StorageClass Type
+hi! link Structure Type
+hi! link Typedef Type
+
+call s:hi("Special", {"fg": s:orange})
+hi! link SpecialChar Special
+hi! link Tag Special
+hi! link Delimiter Special
+hi! link SpecialComment Special
+hi! link Debug Special
+
+call s:hi("Underlined", {"gui": "underline", "cterm": "underline"})
+call s:hi("Ignore", {"fg": s:bg_subtle})
+call s:hi("Error", {"fg": s:white, "bg": s:red_fg , "gui": "bold", "cterm": "bold"})
+call s:hi("Todo", {"bg": s:yellow_bg, "gui": "bold", "cterm": "bold"})
+
+"}}}
+"{{{ Semi-Common Highlighting
+call s:hi("SpecialKey", {"fg": s:purple, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("NonText", {"fg": s:cyan_bg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("Directory", {"fg": s:blue_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("ErrorMsg", {"fg": s:red_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("IncSearch", {"gui": "reverse", "cterm": "reverse", "term": "reverse"})
+call s:hi("Search", {"fg": s:norm, "bg": s:pink_bg})
+call s:hi("MoreMsg", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("ModeMsg", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("LineNr", {"fg": s:gray})
+call s:hi("CursorLineNr", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("Question", {"fg": s:purple, "gui": "bold", "cterm": "bold", "term": "bold"})
+"call s:hi("StatusLine", {"fg": s:norm, "bg": s:green_bg, "gui": "bold", "cterm": "bold", "term": "bold"})
+"call s:hi("StatusLineNC", {"fg": s:norm, "bg": s:gray_bg})
+call s:hi("Conceal", {"fg": s:yellow_fg})
+call s:hi("VertSplit", {"gui": "reverse", "cterm": "reverse", "term": "reverse"})
+call s:hi("Title", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("Visual", {"gui": "reverse", "cterm": "reverse", "term": "reverse"})
+call s:hi("VisualNOS", {"gui": "bold,underline", "cterm": "bold,underline", "term": "bold,underline"})
+call s:hi("WarningMsg", {"fg": s:orange, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("WildMenu", {"fg": s:norm, "bg": s:blue_bg})
+call s:hi("Folded", {"fg": s:green_fg, "bg": s:gray_bg})
+call s:hi("FoldColumn", {"fg": s:green_fg, "bg": s:gray_bg})
+call s:hi("DiffAdd", {"bg": s:green_bg})
+call s:hi("DiffChange", {"bg": s:yellow_bg})
+call s:hi("DiffDelete", {"bg": s:red_bg})
+call s:hi("DiffText", {"bg": s:blue_bg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("SignColumn", {"fg": s:green_fg, "bg": s:gray})
+if has("gui_running")
+ call s:hi("SpellBad", {"gui": s:attr_un, "sp": s:red_bg})
+ call s:hi("SpellCap", {"gui": s:attr_un, "sp": s:yellow_bg})
+ call s:hi("SpellRare", {"gui": s:attr_un, "sp": s:blue_bg})
+ call s:hi("SpellLocal", {"gui": s:attr_un, "sp": s:green_bg})
+else
+ call s:hi("SpellBad", {"cterm": s:attr_un, "fg": s:red_fg})
+ call s:hi("SpellCap", {"cterm": s:attr_un, "fg": s:yellow_fg})
+ call s:hi("SpellRare", {"cterm": s:attr_un, "fg": s:blue_fg})
+ call s:hi("SpellLocal", {"cterm": s:attr_un, "fg": s:green_fg})
+endif
+call s:hi("Pmenu", {"bg": s:gray_bg})
+call s:hi("PmenuSel", {"bg": s:pink_bg})
+call s:hi("PmenuSbar", {"bg": s:gray_bg})
+call s:hi("PmenuThumb", {"bg": s:gray_bg})
+call s:hi("TabLine", {"bg": s:bg_subtle})
+call s:hi("TabLineSel", {"bg": s:pink_bg})
+call s:hi("TabLineFill", {"bg": s:bg_subtle})
+call s:hi("CursorColumn", {"bg": s:yellow_fg})
+call s:hi("CursorLine", {"bg": s:bg_subtle})
+call s:hi("ColorColumn", {"bg": s:bg_subtle})
+call s:hi("MatchParen", {"fg": s:pink_fg, "gui": "underline", "cterm": "underline"})
+call s:hi("qfLineNr", {"fg": s:gray})
+
+"}}}
+""{{{ Switching StatusLine bg
+"function! s:changebg(group, color)
+" execute "highlight" a:group "guibg=" a:color.gui "ctermbg=" a:color.cterm
+"endfunction
+"
+"if g:switch_statusline_bg_in_insert == 1
+" "" Change Color when entering Insert Mode
+" autocmd InsertEnter * call s:changebg("StatusLine", s:pink_bg)
+" "" Revert Color to default when leaving Insert Mode
+" autocmd InsertLeave * call s:changebg("StatusLine", s:green_bg)
+"endif
+
+"}}}
+" vim: set foldmethod=marker:
+
diff --git a/linux/home/.vim/colors/colors/default.vim b/linux/home/.vim/colors/colors/default.vim
new file mode 100644
index 0000000..ebc66e8
--- /dev/null
+++ b/linux/home/.vim/colors/colors/default.vim
@@ -0,0 +1,175 @@
+"{{{ Pre-setting
+let g:colors_name = expand('<sfile>:t:r')
+
+if ! exists("g:terminal_italics")
+ let g:terminal_italics = 0
+endif
+
+if ! exists("g:spell_undercurl")
+ let g:spell_undercurl = 0
+endif
+"}}}
+
+"{{{ Color Palette (updated to match official Vim default colors)
+" Note: Hex colors chosen to reflect official Vim default colorscheme
+
+let s:black = { "gui": "#171717", "cterm": "16" }
+let s:white = { "gui": "#EAE8E7", "cterm": "231" }
+let s:gray = { "gui": "#808080", "cterm": "244" }
+
+" Reds
+let s:red_fg = { "gui": "#FFFFFF", "cterm": "231" } " White fg on red bg for errors
+let s:red_bg = { "gui": "#A40000", "cterm": "52" } " DarkRed bg (ErrorMsg bg)
+
+" Blues and Cyan
+let s:blue_fg = { "gui": "#6A5ACD", "cterm": "60" } " SlateBlue
+let s:dark_cyan = { "gui": "#008B8B", "cterm": "36" } " DarkCyan
+let s:cyan_bg = { "gui": "#00CED1", "cterm": "38" } " DarkTurquoise
+
+" Greens
+let s:green_fg = { "gui": "#008000", "cterm": "22" } " DarkGreen
+let s:green_bg = { "gui": "#90EE90", "cterm": "120" } " LightGreen
+
+" Yellows and Oranges
+let s:yellow_fg = { "gui": "#A52A2A", "cterm": "94" } " Brown (used in Vim default)
+let s:yellow_bg = { "gui": "#FFFF00", "cterm": "226" } " Yellow bg
+
+let s:orange = { "gui": "#FFA500", "cterm": "214" } " Orange
+
+" Purples
+let s:purple = { "gui": "#6A0DAD", "cterm": "90" } " DarkMagenta
+
+" Grays
+let s:light_gray = { "gui": "#D3D3D3", "cterm": "252" }
+let s:dark_gray = { "gui": "#4D4D4D", "cterm": "240" }
+
+" No color
+let s:NONE = { "gui": "NONE", "cterm": "NONE" }
+
+" Alias for Normal fg and background depending on background setting
+if &background == "light"
+ let s:norm = s:black
+ let s:bg = s:NONE
+ let s:bg_subtle = s:light_gray
+ let s:gray_fg = s:gray
+ let s:green_fg = s:green_fg
+ let s:yellow_fg = s:yellow_fg
+ let s:pink_fg = s:purple
+ let s:cyan_fg = s:dark_cyan
+ let s:blue_fg = s:blue_fg
+ let s:red_fg = s:red_bg
+ let s:gray_bg = s:light_gray
+ let s:green_bg = s:green_bg
+ let s:yellow_bg = s:yellow_bg
+ let s:pink_bg = s:orange
+ let s:cyan_bg = s:cyan_bg
+ let s:blue_bg = s:blue_fg
+ let s:red_bg = s:red_bg
+else
+ let s:norm = s:white
+ let s:bg = s:NONE
+ let s:bg_subtle = s:dark_gray
+ let s:gray_fg = s:gray
+ let s:green_fg = s:green_bg
+ let s:yellow_fg = s:yellow_bg
+ let s:pink_fg = s:orange
+ let s:cyan_fg = s:cyan_bg
+ let s:blue_fg = s:blue_bg
+ let s:red_fg = s:red_bg
+ let s:gray_bg = s:dark_gray
+ let s:green_bg = s:green_fg
+ let s:yellow_bg = s:yellow_fg
+ let s:pink_bg = s:purple
+ let s:cyan_bg = s:dark_cyan
+ let s:blue_bg = s:blue_fg
+ let s:red_bg = s:red_bg
+endif
+"}}}
+
+"{{{ Highlight Function (keep your existing function)
+function! s:hi(group, style)
+ if g:terminal_italics == 0
+ if has_key(a:style, "cterm") && a:style["cterm"] == "italic"
+ unlet a:style.cterm
+ endif
+ if has_key(a:style, "term") && a:style["term"] == "italic"
+ unlet a:style.term
+ endif
+ endif
+ execute "highlight" a:group
+ \ "guifg=" (has_key(a:style, "fg") ? a:style.fg.gui : "NONE")
+ \ "guibg=" (has_key(a:style, "bg") ? a:style.bg.gui : "NONE")
+ \ "guisp=" (has_key(a:style, "sp") ? a:style.sp.gui : "NONE")
+ \ "gui=" (has_key(a:style, "gui") ? a:style.gui : "NONE")
+ \ "ctermfg=" (has_key(a:style, "fg") ? a:style.fg.cterm : "NONE")
+ \ "ctermbg=" (has_key(a:style, "bg") ? a:style.bg.cterm : "NONE")
+ \ "cterm=" (has_key(a:style, "cterm") ? a:style.cterm : "NONE")
+ \ "term=" (has_key(a:style, "term") ? a:style.term : "NONE")
+endfunction
+
+if g:spell_undercurl == 1
+ let s:attr_un = 'undercurl'
+else
+ let s:attr_un = 'underline'
+endif
+"}}}
+
+"{{{ Common Highlighting updated to match official Vim default colorscheme
+
+call s:hi("Normal", {"fg": s:norm, "bg": s:bg})
+call s:hi("Cursor", {})
+call s:hi("Conceal", {"fg": s:yellow_fg})
+call s:hi("ErrorMsg", {"fg": s:red_fg, "bg": s:red_bg, "gui": "bold", "cterm": "bold"})
+call s:hi("IncSearch", {"gui": "reverse", "cterm": "reverse"})
+call s:hi("ModeMsg", {"gui": "bold", "cterm": "bold"})
+call s:hi("NonText", {"fg": s:blue_fg, "gui": "bold", "cterm": "bold"})
+call s:hi("PmenuSbar", {"bg": s:gray_bg})
+call s:hi("StatusLine", {"gui": "reverse,bold", "cterm": "reverse,bold"})
+call s:hi("StatusLineNC", {"gui": "reverse", "cterm": "reverse"})
+call s:hi("TabLineFill", {"gui": "reverse", "cterm": "reverse"})
+call s:hi("TabLineSel", {"gui": "bold", "cterm": "bold"})
+call s:hi("TermCursor", {"gui": "reverse", "cterm": "reverse"})
+call s:hi("WinBar", {"gui": "bold", "cterm": "bold"})
+call s:hi("WildMenu", {"fg": s:black, "bg": s:yellow_bg})
+
+call s:hi("VertSplit", {"link": "Normal"})
+call s:hi("WinSeparator", {"link": "VertSplit"})
+call s:hi("WinBarNC", {"link": "WinBar"})
+call s:hi("DiffTextAdd", {"link": "DiffText"})
+call s:hi("EndOfBuffer", {"link": "NonText"})
+call s:hi("LineNrAbove", {"link": "LineNr"})
+call s:hi("LineNrBelow", {"link": "LineNr"})
+call s:hi("QuickFixLine", {"link": "Search"})
+call s:hi("CursorLineSign", {"link": "SignColumn"})
+call s:hi("CursorLineFold", {"link": "FoldColumn"})
+call s:hi("CurSearch", {"link": "Search"})
+call s:hi("PmenuKind", {"link": "Pmenu"})
+call s:hi("PmenuKindSel", {"link": "PmenuSel"})
+call s:hi("PmenuMatch", {"link": "Pmenu"})
+call s:hi("PmenuMatchSel", {"link": "PmenuSel"})
+call s:hi("PmenuExtra", {"link": "Pmenu"})
+call s:hi("PmenuExtraSel", {"link": "PmenuSel"})
+call s:hi("ComplMatchIns", {})
+call s:hi("Substitute", {"link": "Search"})
+call s:hi("Whitespace", {"link": "NonText"})
+call s:hi("MsgSeparator", {"link": "StatusLine"})
+call s:hi("NormalFloat", {"link": "Pmenu"})
+call s:hi("FloatBorder", {"link": "WinSeparator"})
+call s:hi("FloatTitle", {"link": "Title"})
+call s:hi("FloatFooter", {"link": "Title"})
+
+call s:hi("Error", {"fg": s:red_fg, "bg": s:red_bg, "gui": "bold", "cterm": "bold"})
+call s:hi("Todo", {"fg": s:black, "bg": s:yellow_bg, "gui": "bold", "cterm": "bold"})
+
+call s:hi("String", {"link": "Constant"})
+call s:hi("Character", {"link": "Constant"})
+call s:hi("Number", {"link": "Constant"})
+call s:hi("Boolean", {"link": "Constant"})
+call s:hi("Float", {"link": "Number"})
+call s:hi("Function", {"link": "Identifier"})
+call s:hi("Conditional", {"link": "Statement"})
+call s:hi("Repeat", {"link": "Statement"})
+call s:hi("Label", {"link": "Statement"})
+call s:hi("Operator", {"link": "Statement"})
+call s:hi("Keyword", {"link": "Statement"})
+call s:hi("Exception",
diff --git a/linux/home/.vim/colors/colorscheme.vim b/linux/home/.vim/colors/colorscheme.vim
new file mode 100644
index 0000000..ce0526e
--- /dev/null
+++ b/linux/home/.vim/colors/colorscheme.vim
@@ -0,0 +1,247 @@
+" Vim Colorscheme
+" Name: cherryblossom.vim
+" Author: Luo Boming
+" Version: 0.3
+" License: The MIT Licence
+
+"{{{ Pre-setting
+let g:colors_name = expand('<sfile>:t:r')
+
+"hi clear
+"if exists("syntax_on")
+" syntax reset
+"endif
+
+if ! exists("g:terminal_italics")
+ let g:terminal_italics = 0
+endif
+
+"if ! exists("g:switch_statusline_bg_in_insert")
+" let g:switch_statusline_bg_in_insert = 0
+"endif
+
+if ! exists("g:spell_undercurl")
+ let g:spell_undercurl = 0
+endif
+
+"}}}
+"{{{ Color Palette
+" Color Entity
+let s:black = { "gui": "#171717", "cterm": "16" }
+let s:white = { "gui": "#EAE8E7", "cterm": "231" }
+
+let s:gray = { "gui": "#3a3f52", "cterm": "247" }
+
+let s:green = { "gui": "#30B536", "cterm": "34" }
+let s:pink = { "gui": "#D36DD3", "cterm": "170" }
+let s:orange = { "gui": "#FC923F", "cterm": "208" }
+let s:purple = { "gui": "#B586E7", "cterm": "141" }
+let s:light_cyan = { "gui": "#D7FFFF", "cterm": "195" }
+let s:dark_cyan = { "gui": "#00AF87", "cterm": "36" }
+let s:ultramarine = { "gui": "#229EC0", "cterm": "38" }
+let s:skyblue = { "gui": "#9BE7F8", "cterm": "195" }
+
+let s:white_pink = { "gui": "#FEF7FE", "cterm": "231" }
+let s:white_pink_deep = { "gui": "#FEF0FE", "cterm": "255" }
+let s:black_green = { "gui": "#053703", "cterm": "235" }
+let s:black_green_bright = { "gui": "#074005", "cterm": "239" }
+let s:middle_gray = { "gui": "#8a8a8a", "cterm": "245" }
+
+let s:light_gray = { "gui": "#E1DCDA", "cterm": "253" }
+let s:light_green = { "gui": "#B7EFA5", "cterm": "157" }
+let s:light_pink = { "gui": "#FEDCFE", "cterm": "225" }
+let s:light_yellow = { "gui": "#EDE682", "cterm": "228" }
+let s:light_red = { "gui": "#EB5A7C", "cterm": "204" }
+
+let s:dark_gray = { "gui": "#4D4A48", "cterm": "241" }
+let s:dark_green = { "gui": "#09570A", "cterm": "22" }
+let s:dark_yellow = { "gui": "#BC922B", "cterm": "3" }
+let s:dark_pink = { "gui": "#B365A2", "cterm": "133" }
+let s:dark_red = { "gui": "#D9372D", "cterm": "160" }
+let s:NONE = { "gui": "NONE", "cterm": "NONE" }
+
+" Color Alias
+if &background == "light"
+ let s:norm = s:black
+ let s:bg = s:NONE
+ let s:bg_subtle = s:white_pink_deep
+ let s:gray_fg = s:middle_gray
+ let s:green_fg = s:green
+ let s:yellow_fg = s:dark_yellow
+ let s:pink_fg = s:dark_pink
+ let s:cyan_fg = s:dark_cyan
+ let s:blue_fg = s:ultramarine
+ let s:red_fg = s:dark_red
+ let s:gray_bg = s:light_gray
+ let s:green_bg = s:light_green
+ let s:yellow_bg = s:light_yellow
+ let s:pink_bg = s:light_pink
+ let s:cyan_bg = s:light_cyan
+ let s:blue_bg = s:skyblue
+ let s:red_bg = s:light_red
+endif
+
+if &background == "dark"
+ let s:norm = s:white
+ let s:bg = s:NONE
+ let s:bg_subtle = s:gray
+ let s:gray_fg = s:middle_gray
+ let s:green_fg = s:light_green
+ let s:yellow_fg = s:light_yellow
+ let s:pink_fg = s:light_pink
+ let s:cyan_fg = s:light_cyan
+ let s:blue_fg = s:skyblue
+ let s:red_fg = s:light_red
+ let s:gray_bg = s:dark_gray
+ let s:green_bg = s:green
+ let s:yellow_bg = s:dark_yellow
+ let s:pink_bg = s:pink
+ let s:cyan_bg = s:dark_cyan
+ let s:blue_bg = s:ultramarine
+ let s:red_bg = s:dark_red
+endif
+"}}}
+"{{{ Highlight Function
+" shamelessly stolen from pencil: https://github.com/reedes/vim-colors-pencil
+function! s:hi(group, style)
+ if g:terminal_italics == 0
+ if has_key(a:style, "cterm") && a:style["cterm"] == "italic"
+ unlet a:style.cterm
+ endif
+ if has_key(a:style, "term") && a:style["term"] == "italic"
+ unlet a:style.term
+ endif
+ endif
+ execute "highlight" a:group
+ \ "guifg=" (has_key(a:style, "fg") ? a:style.fg.gui : "NONE")
+ \ "guibg=" (has_key(a:style, "bg") ? a:style.bg.gui : "NONE")
+ \ "guisp=" (has_key(a:style, "sp") ? a:style.sp.gui : "NONE")
+ \ "gui=" (has_key(a:style, "gui") ? a:style.gui : "NONE")
+ \ "ctermfg=" (has_key(a:style, "fg") ? a:style.fg.cterm : "NONE")
+ \ "ctermbg=" (has_key(a:style, "bg") ? a:style.bg.cterm : "NONE")
+ \ "cterm=" (has_key(a:style, "cterm") ? a:style.cterm : "NONE")
+ \ "term=" (has_key(a:style, "term") ? a:style.term : "NONE")
+endfunction
+
+if g:spell_undercurl == 1
+ let s:attr_un = 'undercurl'
+else
+ let s:attr_un = 'underline'
+endif
+
+"}}}
+"{{{ Common Highlighting
+call s:hi("Normal", {"fg": s:norm, "bg": s:bg})
+call s:hi("Cursor", {})
+call s:hi("Comment", {"fg": s:gray_fg, "gui": "italic", "cterm": "italic", "term": "italic"})
+
+call s:hi("Constant", {"fg": s:pink_fg})
+hi! link String Constant
+hi! link Character Constant
+hi! link Number Constant
+hi! link Boolean Constant
+hi! link Float Constant
+
+call s:hi("Identifier", {"fg": s:red_fg})
+hi! link Function Identifier
+
+call s:hi("Statement", {"fg": s:green_fg})
+hi! link Conditonal Statement
+hi! link Repeat Statement
+hi! link Label Statement
+hi! link Operator Statement
+hi! link Keyword Statement
+hi! link Exception Statement
+
+call s:hi("PreProc", {"fg": s:blue_fg})
+hi! link Include PreProc
+hi! link Define PreProc
+hi! link Macro PreProc
+hi! link PreCondit PreProc
+
+call s:hi("Type", {"fg": s:yellow_fg})
+hi! link StorageClass Type
+hi! link Structure Type
+hi! link Typedef Type
+
+call s:hi("Special", {"fg": s:orange})
+hi! link SpecialChar Special
+hi! link Tag Special
+hi! link Delimiter Special
+hi! link SpecialComment Special
+hi! link Debug Special
+
+call s:hi("Underlined", {"gui": "underline", "cterm": "underline"})
+call s:hi("Ignore", {"fg": s:bg_subtle})
+call s:hi("Error", {"fg": s:white, "bg": s:red_fg , "gui": "bold", "cterm": "bold"})
+call s:hi("Todo", {"bg": s:yellow_bg, "gui": "bold", "cterm": "bold"})
+
+"}}}
+"{{{ Semi-Common Highlighting
+call s:hi("SpecialKey", {"fg": s:purple, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("NonText", {"fg": s:cyan_bg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("Directory", {"fg": s:blue_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("ErrorMsg", {"fg": s:red_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("IncSearch", {"gui": "reverse", "cterm": "reverse", "term": "reverse"})
+call s:hi("Search", {"fg": s:norm, "bg": s:pink_bg})
+call s:hi("MoreMsg", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("ModeMsg", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("LineNr", {"fg": s:gray})
+call s:hi("CursorLineNr", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("Question", {"fg": s:purple, "gui": "bold", "cterm": "bold", "term": "bold"})
+"call s:hi("StatusLine", {"fg": s:norm, "bg": s:green_bg, "gui": "bold", "cterm": "bold", "term": "bold"})
+"call s:hi("StatusLineNC", {"fg": s:norm, "bg": s:gray_bg})
+call s:hi("Conceal", {"fg": s:yellow_fg})
+call s:hi("VertSplit", {"gui": "reverse", "cterm": "reverse", "term": "reverse"})
+call s:hi("Title", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("Visual", {"gui": "reverse", "cterm": "reverse", "term": "reverse"})
+call s:hi("VisualNOS", {"gui": "bold,underline", "cterm": "bold,underline", "term": "bold,underline"})
+call s:hi("WarningMsg", {"fg": s:orange, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("WildMenu", {"fg": s:norm, "bg": s:blue_bg})
+call s:hi("Folded", {"fg": s:green_fg, "bg": s:gray_bg})
+call s:hi("FoldColumn", {"fg": s:green_fg, "bg": s:gray_bg})
+call s:hi("DiffAdd", {"bg": s:green_bg})
+call s:hi("DiffChange", {"bg": s:yellow_bg})
+call s:hi("DiffDelete", {"bg": s:red_bg})
+call s:hi("DiffText", {"bg": s:blue_bg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("SignColumn", {"fg": s:green_fg, "bg": s:gray})
+if has("gui_running")
+ call s:hi("SpellBad", {"gui": s:attr_un, "sp": s:red_bg})
+ call s:hi("SpellCap", {"gui": s:attr_un, "sp": s:yellow_bg})
+ call s:hi("SpellRare", {"gui": s:attr_un, "sp": s:blue_bg})
+ call s:hi("SpellLocal", {"gui": s:attr_un, "sp": s:green_bg})
+else
+ call s:hi("SpellBad", {"cterm": s:attr_un, "fg": s:red_fg})
+ call s:hi("SpellCap", {"cterm": s:attr_un, "fg": s:yellow_fg})
+ call s:hi("SpellRare", {"cterm": s:attr_un, "fg": s:blue_fg})
+ call s:hi("SpellLocal", {"cterm": s:attr_un, "fg": s:green_fg})
+endif
+call s:hi("Pmenu", {"bg": s:gray_bg})
+call s:hi("PmenuSel", {"bg": s:pink_bg})
+call s:hi("PmenuSbar", {"bg": s:gray_bg})
+call s:hi("PmenuThumb", {"bg": s:gray_bg})
+call s:hi("TabLine", {"bg": s:bg_subtle})
+call s:hi("TabLineSel", {"bg": s:pink_bg})
+call s:hi("TabLineFill", {"bg": s:bg_subtle})
+call s:hi("CursorColumn", {"bg": s:yellow_fg})
+call s:hi("CursorLine", {"bg": s:bg_subtle})
+call s:hi("ColorColumn", {"bg": s:bg_subtle})
+call s:hi("MatchParen", {"fg": s:pink_fg, "gui": "underline", "cterm": "underline"})
+call s:hi("qfLineNr", {"fg": s:gray})
+
+"}}}
+""{{{ Switching StatusLine bg
+"function! s:changebg(group, color)
+" execute "highlight" a:group "guibg=" a:color.gui "ctermbg=" a:color.cterm
+"endfunction
+"
+"if g:switch_statusline_bg_in_insert == 1
+" "" Change Color when entering Insert Mode
+" autocmd InsertEnter * call s:changebg("StatusLine", s:pink_bg)
+" "" Revert Color to default when leaving Insert Mode
+" autocmd InsertLeave * call s:changebg("StatusLine", s:green_bg)
+"endif
+
+"}}}
+" vim: set foldmethod=marker:
+
diff --git a/linux/home/.vim/colors/default.vim b/linux/home/.vim/colors/default.vim
new file mode 100644
index 0000000..ebc66e8
--- /dev/null
+++ b/linux/home/.vim/colors/default.vim
@@ -0,0 +1,175 @@
+"{{{ Pre-setting
+let g:colors_name = expand('<sfile>:t:r')
+
+if ! exists("g:terminal_italics")
+ let g:terminal_italics = 0
+endif
+
+if ! exists("g:spell_undercurl")
+ let g:spell_undercurl = 0
+endif
+"}}}
+
+"{{{ Color Palette (updated to match official Vim default colors)
+" Note: Hex colors chosen to reflect official Vim default colorscheme
+
+let s:black = { "gui": "#171717", "cterm": "16" }
+let s:white = { "gui": "#EAE8E7", "cterm": "231" }
+let s:gray = { "gui": "#808080", "cterm": "244" }
+
+" Reds
+let s:red_fg = { "gui": "#FFFFFF", "cterm": "231" } " White fg on red bg for errors
+let s:red_bg = { "gui": "#A40000", "cterm": "52" } " DarkRed bg (ErrorMsg bg)
+
+" Blues and Cyan
+let s:blue_fg = { "gui": "#6A5ACD", "cterm": "60" } " SlateBlue
+let s:dark_cyan = { "gui": "#008B8B", "cterm": "36" } " DarkCyan
+let s:cyan_bg = { "gui": "#00CED1", "cterm": "38" } " DarkTurquoise
+
+" Greens
+let s:green_fg = { "gui": "#008000", "cterm": "22" } " DarkGreen
+let s:green_bg = { "gui": "#90EE90", "cterm": "120" } " LightGreen
+
+" Yellows and Oranges
+let s:yellow_fg = { "gui": "#A52A2A", "cterm": "94" } " Brown (used in Vim default)
+let s:yellow_bg = { "gui": "#FFFF00", "cterm": "226" } " Yellow bg
+
+let s:orange = { "gui": "#FFA500", "cterm": "214" } " Orange
+
+" Purples
+let s:purple = { "gui": "#6A0DAD", "cterm": "90" } " DarkMagenta
+
+" Grays
+let s:light_gray = { "gui": "#D3D3D3", "cterm": "252" }
+let s:dark_gray = { "gui": "#4D4D4D", "cterm": "240" }
+
+" No color
+let s:NONE = { "gui": "NONE", "cterm": "NONE" }
+
+" Alias for Normal fg and background depending on background setting
+if &background == "light"
+ let s:norm = s:black
+ let s:bg = s:NONE
+ let s:bg_subtle = s:light_gray
+ let s:gray_fg = s:gray
+ let s:green_fg = s:green_fg
+ let s:yellow_fg = s:yellow_fg
+ let s:pink_fg = s:purple
+ let s:cyan_fg = s:dark_cyan
+ let s:blue_fg = s:blue_fg
+ let s:red_fg = s:red_bg
+ let s:gray_bg = s:light_gray
+ let s:green_bg = s:green_bg
+ let s:yellow_bg = s:yellow_bg
+ let s:pink_bg = s:orange
+ let s:cyan_bg = s:cyan_bg
+ let s:blue_bg = s:blue_fg
+ let s:red_bg = s:red_bg
+else
+ let s:norm = s:white
+ let s:bg = s:NONE
+ let s:bg_subtle = s:dark_gray
+ let s:gray_fg = s:gray
+ let s:green_fg = s:green_bg
+ let s:yellow_fg = s:yellow_bg
+ let s:pink_fg = s:orange
+ let s:cyan_fg = s:cyan_bg
+ let s:blue_fg = s:blue_bg
+ let s:red_fg = s:red_bg
+ let s:gray_bg = s:dark_gray
+ let s:green_bg = s:green_fg
+ let s:yellow_bg = s:yellow_fg
+ let s:pink_bg = s:purple
+ let s:cyan_bg = s:dark_cyan
+ let s:blue_bg = s:blue_fg
+ let s:red_bg = s:red_bg
+endif
+"}}}
+
+"{{{ Highlight Function (keep your existing function)
+function! s:hi(group, style)
+ if g:terminal_italics == 0
+ if has_key(a:style, "cterm") && a:style["cterm"] == "italic"
+ unlet a:style.cterm
+ endif
+ if has_key(a:style, "term") && a:style["term"] == "italic"
+ unlet a:style.term
+ endif
+ endif
+ execute "highlight" a:group
+ \ "guifg=" (has_key(a:style, "fg") ? a:style.fg.gui : "NONE")
+ \ "guibg=" (has_key(a:style, "bg") ? a:style.bg.gui : "NONE")
+ \ "guisp=" (has_key(a:style, "sp") ? a:style.sp.gui : "NONE")
+ \ "gui=" (has_key(a:style, "gui") ? a:style.gui : "NONE")
+ \ "ctermfg=" (has_key(a:style, "fg") ? a:style.fg.cterm : "NONE")
+ \ "ctermbg=" (has_key(a:style, "bg") ? a:style.bg.cterm : "NONE")
+ \ "cterm=" (has_key(a:style, "cterm") ? a:style.cterm : "NONE")
+ \ "term=" (has_key(a:style, "term") ? a:style.term : "NONE")
+endfunction
+
+if g:spell_undercurl == 1
+ let s:attr_un = 'undercurl'
+else
+ let s:attr_un = 'underline'
+endif
+"}}}
+
+"{{{ Common Highlighting updated to match official Vim default colorscheme
+
+call s:hi("Normal", {"fg": s:norm, "bg": s:bg})
+call s:hi("Cursor", {})
+call s:hi("Conceal", {"fg": s:yellow_fg})
+call s:hi("ErrorMsg", {"fg": s:red_fg, "bg": s:red_bg, "gui": "bold", "cterm": "bold"})
+call s:hi("IncSearch", {"gui": "reverse", "cterm": "reverse"})
+call s:hi("ModeMsg", {"gui": "bold", "cterm": "bold"})
+call s:hi("NonText", {"fg": s:blue_fg, "gui": "bold", "cterm": "bold"})
+call s:hi("PmenuSbar", {"bg": s:gray_bg})
+call s:hi("StatusLine", {"gui": "reverse,bold", "cterm": "reverse,bold"})
+call s:hi("StatusLineNC", {"gui": "reverse", "cterm": "reverse"})
+call s:hi("TabLineFill", {"gui": "reverse", "cterm": "reverse"})
+call s:hi("TabLineSel", {"gui": "bold", "cterm": "bold"})
+call s:hi("TermCursor", {"gui": "reverse", "cterm": "reverse"})
+call s:hi("WinBar", {"gui": "bold", "cterm": "bold"})
+call s:hi("WildMenu", {"fg": s:black, "bg": s:yellow_bg})
+
+call s:hi("VertSplit", {"link": "Normal"})
+call s:hi("WinSeparator", {"link": "VertSplit"})
+call s:hi("WinBarNC", {"link": "WinBar"})
+call s:hi("DiffTextAdd", {"link": "DiffText"})
+call s:hi("EndOfBuffer", {"link": "NonText"})
+call s:hi("LineNrAbove", {"link": "LineNr"})
+call s:hi("LineNrBelow", {"link": "LineNr"})
+call s:hi("QuickFixLine", {"link": "Search"})
+call s:hi("CursorLineSign", {"link": "SignColumn"})
+call s:hi("CursorLineFold", {"link": "FoldColumn"})
+call s:hi("CurSearch", {"link": "Search"})
+call s:hi("PmenuKind", {"link": "Pmenu"})
+call s:hi("PmenuKindSel", {"link": "PmenuSel"})
+call s:hi("PmenuMatch", {"link": "Pmenu"})
+call s:hi("PmenuMatchSel", {"link": "PmenuSel"})
+call s:hi("PmenuExtra", {"link": "Pmenu"})
+call s:hi("PmenuExtraSel", {"link": "PmenuSel"})
+call s:hi("ComplMatchIns", {})
+call s:hi("Substitute", {"link": "Search"})
+call s:hi("Whitespace", {"link": "NonText"})
+call s:hi("MsgSeparator", {"link": "StatusLine"})
+call s:hi("NormalFloat", {"link": "Pmenu"})
+call s:hi("FloatBorder", {"link": "WinSeparator"})
+call s:hi("FloatTitle", {"link": "Title"})
+call s:hi("FloatFooter", {"link": "Title"})
+
+call s:hi("Error", {"fg": s:red_fg, "bg": s:red_bg, "gui": "bold", "cterm": "bold"})
+call s:hi("Todo", {"fg": s:black, "bg": s:yellow_bg, "gui": "bold", "cterm": "bold"})
+
+call s:hi("String", {"link": "Constant"})
+call s:hi("Character", {"link": "Constant"})
+call s:hi("Number", {"link": "Constant"})
+call s:hi("Boolean", {"link": "Constant"})
+call s:hi("Float", {"link": "Number"})
+call s:hi("Function", {"link": "Identifier"})
+call s:hi("Conditional", {"link": "Statement"})
+call s:hi("Repeat", {"link": "Statement"})
+call s:hi("Label", {"link": "Statement"})
+call s:hi("Operator", {"link": "Statement"})
+call s:hi("Keyword", {"link": "Statement"})
+call s:hi("Exception",
diff --git a/linux/home/.vim/ftplugin/after/vim.vim b/linux/home/.vim/ftplugin/after/vim.vim
new file mode 100644
index 0000000..3548816
--- /dev/null
+++ b/linux/home/.vim/ftplugin/after/vim.vim
@@ -0,0 +1,12 @@
+" Enable Vim-specific options
+setlocal tabstop=4
+setlocal shiftwidth=4
+setlocal softtabstop=4
+setlocal expandtab
+setlocal foldmethod=marker
+setlocal keywordprg=:help
+setlocal iskeyword+=:
+
+" Spell-check off by default
+setlocal nospell
+
diff --git a/linux/home/.vim/ftplugin/ftplugin/after/vim.vim b/linux/home/.vim/ftplugin/ftplugin/after/vim.vim
new file mode 100644
index 0000000..3548816
--- /dev/null
+++ b/linux/home/.vim/ftplugin/ftplugin/after/vim.vim
@@ -0,0 +1,12 @@
+" Enable Vim-specific options
+setlocal tabstop=4
+setlocal shiftwidth=4
+setlocal softtabstop=4
+setlocal expandtab
+setlocal foldmethod=marker
+setlocal keywordprg=:help
+setlocal iskeyword+=:
+
+" Spell-check off by default
+setlocal nospell
+
diff --git a/linux/home/.vim/pack/plugins/start/vim-tmux-navigator b/linux/home/.vim/pack/plugins/start/vim-tmux-navigator
new file mode 160000
+Subproject 38b1d0402c4600543281dc85b3f51884205674b
diff --git a/linux/home/.vim/vimrc b/linux/home/.vim/vimrc
new file mode 100644
index 0000000..5b0f444
--- /dev/null
+++ b/linux/home/.vim/vimrc
@@ -0,0 +1,700 @@
+" ============================================================================
+" Vim Configuration
+" Maintainer: srdusr
+" ============================================================================
+
+" ============================================================================
+" Core Settings
+" ============================================================================
+set nocompatible
+set encoding=utf-8
+set fileencoding=utf-8
+scriptencoding utf-8
+set termguicolors
+set mouse=a
+set clipboard=unnamedplus
+set hidden
+set updatetime=300
+set timeoutlen=500
+set ttimeoutlen=10
+
+" Performance
+set ttyfast
+set lazyredraw
+set ttyscroll=3
+
+" ============================================================================
+" Display and UI Settings
+" ============================================================================
+set number
+set relativenumber
+set cursorline
+set signcolumn=no
+set showcmd
+set showmode
+set showmatch
+set matchtime=2
+set laststatus=2
+set cmdheight=1
+set scrolloff=8
+set sidescrolloff=8
+set sidescroll=1
+set display=lastline
+set nowrap
+set linebreak
+set showbreak=↪\
+
+" Window behavior
+set splitright
+set splitbelow
+set winminwidth=1
+set winwidth=5
+
+" ============================================================================
+" Formatting and Indentation Settings
+" ============================================================================
+set autoindent
+set smartindent
+set expandtab
+set tabstop=2
+set shiftwidth=2
+set softtabstop=2
+set shiftround
+set textwidth=80
+set formatoptions+=j
+" set colorcolumn=+1
+
+" File-specific formatting
+setlocal tabstop=4
+setlocal shiftwidth=4
+setlocal softtabstop=4
+setlocal foldmethod=marker
+
+" ============================================================================
+" Search and Matching Settings
+" ============================================================================
+set hlsearch
+set incsearch
+set ignorecase
+set smartcase
+set showmatch
+" set inccommand=split
+
+" ============================================================================
+" File Handling and Backup Settings
+" ============================================================================
+set autoread
+set autowrite
+set autochdir
+set confirm
+set fileformats=unix,dos,mac
+
+" Backup and undo configuration
+set backup
+set writebackup
+set backupcopy=yes
+set undofile
+set noswapfile
+set backupdir=~/.cache/vim/backup//
+set directory=~/.cache/vim/swap//
+set undodir=~/.cache/vim/undo//
+set undolevels=10000
+set undoreload=10000
+
+" Create necessary directories
+let s:config_dir = expand('~/.vim')
+let s:cache_dir = expand('~/.cache/vim')
+let s:dirs = [
+ \ s:cache_dir . '/backup',
+ \ s:cache_dir . '/swap',
+ \ s:cache_dir . '/undo',
+ \ s:cache_dir . '/shada',
+ \ s:config_dir . '/sessions',
+ \ ]
+
+for s:dir in s:dirs
+ if !isdirectory(s:dir)
+ call mkdir(s:dir, 'p', 0700)
+ endif
+endfor
+
+" ============================================================================
+" Completion and Command Line Settings
+" ============================================================================
+set omnifunc=syntaxcomplete#Complete
+set complete=.,w,b,u,t,i,kspell
+set completeopt=menu,menuone,noselect
+
+" Wildmenu configuration
+set wildmenu
+set wildmode=longest:full,full
+set wildignorecase
+set wildignore+=*.o,*.obj,.git,*.rbc,*.pyc,__pycache__
+set wildignore+=.git,.hg,.svn
+set wildignore+=*.swp,*.swo
+set wildignore+=*.DS_Store
+set wildignore+=*.class
+
+" ============================================================================
+" Leader Keys
+" ============================================================================
+let mapleader = ";"
+let maplocalleader = "\\"
+
+" ============================================================================
+" Navigation and Movement Mappings
+" ============================================================================
+
+" Tmux/Vim Navigation Function
+function! SmartMove(direction, tmux_flag)
+ let curwin = win_getid()
+ execute 'wincmd ' . a:direction
+ if win_getid() == curwin
+ call system('tmux select-pane ' . a:tmux_flag)
+ endif
+endfunction
+
+" Window navigation
+nnoremap <silent> <C-h> :call SmartMove('h', '-L')<CR>
+nnoremap <silent> <C-j> :call SmartMove('j', '-D')<CR>
+nnoremap <silent> <C-k> :call SmartMove('k', '-U')<CR>
+nnoremap <silent> <C-l> :call SmartMove('l', '-R')<CR>
+
+" Split window horizontally
+nnoremap <leader>- :split<CR>
+
+" Split window vertically
+nnoremap <leader>\ :vsplit<CR>
+
+" Close the current window
+nnoremap <leader>c <C-w>c
+
+" Buffer navigation
+nnoremap <silent> <leader>bn :bnext<CR>
+nnoremap <silent> <leader>bp :bprevious<CR>
+nnoremap <silent> <leader>bd :bdelete<CR>
+nnoremap <silent> <leader>ba :%bdelete<CR>
+nnoremap <silent> <leader>bl :ls<CR>:b<Space>
+
+" Quickfix and location list
+nnoremap ]q :cnext<CR>zz
+nnoremap [q :cprev<CR>zz
+nnoremap ]l :lnext<CR>zz
+nnoremap [l :lprev<CR>zz
+
+" ============================================================================
+" File and Buffer Management Mappings
+" ============================================================================
+nnoremap <silent> <leader>w :w<CR>
+nnoremap <silent> <leader>q :q<CR>
+nnoremap <silent> <leader>wq :wq<CR>
+nnoremap <silent> <leader>Q :qa!<CR>
+
+" File operations
+nnoremap <leader>f :Lex<CR>
+noremap <leader>o :Explore<CR>
+nnoremap <leader>rf :browse old<cr>
+
+" Utility mappings
+nnoremap <silent> <leader>r :registers<CR>
+nnoremap <silent> <leader>m :redir @+<CR>:silent messages<CR>:redir END<CR>
+nnoremap <silent> <leader>hx :call HexState()<CR>
+nnoremap <buffer> <leader>h :help <C-R><C-W><CR>
+
+" Format disable
+cnoremap F! :noautocmd w<CR>
+
+" ============================================================================
+" Window and Terminal Mappings
+" ============================================================================
+
+" Window resizing
+nnoremap <M-Up> :resize -2<CR>
+noremap <M-Down> :resize +2<CR>
+noremap <M-Left> :vertical resize -2<CR>
+noremap <M-Right> :vertical resize +2<CR>
+
+" Terminal mode
+nnoremap <silent> <C-t> :terminal<CR>
+tnoremap <C-t> <C-\><C-n>:q!<CR>
+tnoremap <Esc> <C-\><C-n>
+tnoremap <C-h> <C-\><C-n><C-w>h
+tnoremap <C-j> <C-\><C-n><C-w>j
+tnoremap <C-k> <C-\><C-n><C-w>k
+tnoremap <C-l> <C-\><C-n><C-w>l
+
+" ============================================================================
+" Text Editing and Visual Mode Mappings
+" ============================================================================
+
+" Insert mode shortcuts
+inoremap jk <ESC>
+
+" Visual mode operations
+vnoremap J :m '>+1<CR>gv=gv
+vnoremap K :m '>-2<CR>gv=gv
+vnoremap < <gv
+vnoremap > >gv
+
+" Text formatting
+nnoremap Q gqap
+
+" Auto-closing pairs
+inoremap [ []<left>
+inoremap ( ()<left>
+inoremap { {}<left>
+inoremap /* /**/<left><left>
+
+" ============================================================================
+" Tab and Completion Functions
+" ============================================================================
+
+" Smart Tab Function
+function! SmartIndentTab()
+ let line = getline('.')
+ let col = col('.') - 1
+
+ if col == 0 || line[col - 1] =~ '^\s*$'
+ return "\<C-T>"
+ else
+ return "\<tab>"
+ endif
+endfunction
+
+" Tab mappings
+silent! iunmap <tab>
+inoremap <silent><expr> <tab> SmartIndentTab()
+inoremap <expr> <CR> pumvisible() ? "\<C-Y>" : "\<CR>"
+
+" Auto-completion function
+function! AutoComplete()
+ let skip_filetypes = ['netrw', 'help', 'startify', 'qf', 'man', 'gitcommit']
+
+ if index(skip_filetypes, &filetype) >= 0
+ return
+ endif
+
+ let col = col('.')
+ if col < 4
+ return
+ endif
+
+ let line = getline('.')
+ let prev3 = line[col - 4]
+ let prev2 = line[col - 3]
+ let prev1 = line[col - 2]
+ let curr = v:char
+
+ if curr =~ '\k' && prev3 !~ '\k' && prev2 =~ '\k' && prev1 =~ '\k'
+ call feedkeys("\<C-n>", 'n')
+ endif
+endfunction
+
+" ============================================================================
+" Spell Checking
+" ============================================================================
+noremap <silent> <C-z> :setlocal spell!<CR>
+setlocal nospell
+
+function! SpellCheckStatus()
+ return &spell ? ' [SPELL]' : ''
+endfunction
+
+" ============================================================================
+" File Explorer Configuration
+" ============================================================================
+let g:netrw_banner=0
+let g:netrw_browse_split=4
+let g:netrw_altv=1
+let g:netrw_liststyle=3
+let g:netrw_fastbrowse = 0
+let g:netrw_winsize=20
+
+" ============================================================================
+" Search Tools Configuration
+" ============================================================================
+if executable("rg")
+ set grepprg=rg\ --vimgrep\ --no-heading
+ set grepformat=%f:%l:%c:%m,%f:%l:%m
+endif
+
+" ============================================================================
+" Clipboard Configuration
+" ============================================================================
+
+" OS Detection
+let s:uname = substitute(system('uname'), '\n', '', '')
+let g:is_mac = has('mac')
+let g:is_linux = s:uname ==# 'Linux'
+let g:is_windows = has('win32') || has('win64') || s:uname =~? 'Windows'
+let g:is_wsl = has('wsl') || (g:is_linux && !empty($WSL_DISTRO_NAME))
+let g:is_termux = has('termux') || (!empty($PREFIX) && $PREFIX =~# 'com.termux')
+let g:is_wayland = !empty($WAYLAND_DISPLAY)
+let g:is_x11 = !empty($DISPLAY) && empty($WAYLAND_DISPLAY)
+
+" Clipboard configuration
+if has('unnamedplus')
+ set clipboard=unnamed,unnamedplus
+else
+ set clipboard=unnamed
+endif
+
+if has('clipboard')
+ " Linux: Wayland/X11 providers
+ if g:is_wayland && executable('wl-copy') && executable('wl-paste')
+ let g:clipboard = {
+ \ 'name': 'wl-clipboard',
+ \ 'copy': {
+ \ '+': ['wl-copy', '--trim-newline'],
+ \ '*': ['wl-copy', '--trim-newline', '--primary'],
+ \ },
+ \ 'paste': {
+ \ '+': ['wl-paste', '--no-newline'],
+ \ '*': ['wl-paste', '--no-newline', '--primary'],
+ \ },
+ \ 'cache_enabled': 1,
+ \ }
+ elseif g:is_x11 && executable('xclip')
+ let g:clipboard = {
+ \ 'name': 'xclip',
+ \ 'copy': {
+ \ '+': ['xclip', '-selection', 'clipboard'],
+ \ '*': ['xclip', '-selection', 'primary'],
+ \ },
+ \ 'paste': {
+ \ '+': ['xclip', '-selection', 'clipboard', '-o'],
+ \ '*': ['xclip', '-selection', 'primary', '-o'],
+ \ },
+ \ 'cache_enabled': 1,
+ \ }
+ elseif g:is_x11 && executable('xsel')
+ let g:clipboard = {
+ \ 'name': 'xsel',
+ \ 'copy': {
+ \ '+': ['xsel', '--clipboard', '--input'],
+ \ '*': ['xsel', '--primary', '--input'],
+ \ },
+ \ 'paste': {
+ \ '+': ['xsel', '--clipboard', '--output'],
+ \ '*': ['xsel', '--primary', '--output'],
+ \ },
+ \ 'cache_enabled': 1,
+ \ }
+ " macOS: pbcopy/pbpaste
+ elseif g:is_mac && executable('pbcopy') && executable('pbpaste')
+ let g:clipboard = {
+ \ 'name': 'macOS-clipboard',
+ \ 'copy': { '+': ['pbcopy'], '*': ['pbcopy'] },
+ \ 'paste': { '+': ['pbpaste'], '*': ['pbpaste'] },
+ \ 'cache_enabled': 1,
+ \ }
+ " WSL: prefer win32yank, fallback to clip.exe + PowerShell
+ elseif g:is_wsl && executable('win32yank.exe')
+ let g:clipboard = {
+ \ 'name': 'win32yank-wsl',
+ \ 'copy': { '+': ['win32yank.exe', '-i', '--crlf'],
+ \ '*': ['win32yank.exe', '-i', '--crlf'] },
+ \ 'paste': { '+': ['win32yank.exe', '-o', '--lf'],
+ \ '*': ['win32yank.exe', '-o', '--lf'] },
+ \ 'cache_enabled': 1,
+ \ }
+ elseif g:is_wsl && executable('clip.exe') && executable('powershell.exe')
+ let g:clipboard = {
+ \ 'name': 'wsl-clip',
+ \ 'copy': {
+ \ '+': ['clip.exe'],
+ \ '*': ['clip.exe'],
+ \ },
+ \ 'paste': {
+ \ '+': ['powershell.exe', '-NoProfile', '-Command',
+ \ '[Console]::Out.Write((Get-Clipboard -Raw) -replace "`r","")'],
+ \ '*': ['powershell.exe', '-NoProfile', '-Command',
+ \ '[Console]::Out.Write((Get-Clipboard -Raw) -replace "`r","")'],
+ \ },
+ \ 'cache_enabled': 0,
+ \ }
+ " Native Windows
+ elseif g:is_windows && !g:is_wsl && executable('win32yank.exe')
+ let g:clipboard = {
+ \ 'name': 'win32yank',
+ \ 'copy': { '+': ['win32yank.exe', '-i', '--crlf'],
+ \ '*': ['win32yank.exe', '-i', '--crlf'] },
+ \ 'paste': { '+': ['win32yank.exe', '-o', '--lf'],
+ \ '*': ['win32yank.exe', '-o', '--lf'] },
+ \ 'cache_enabled': 1,
+ \ }
+ elseif g:is_windows && !g:is_wsl && executable('powershell.exe')
+ let g:clipboard = {
+ \ 'name': 'powershell-clipboard',
+ \ 'copy': {
+ \ '+': ['powershell.exe', '-NoProfile', '-Command',
+ \ 'Set-Clipboard ([Console]::In.ReadToEnd())'],
+ \ '*': ['powershell.exe', '-NoProfile', '-Command',
+ \ 'Set-Clipboard ([Console]::In.ReadToEnd())'],
+ \ },
+ \ 'paste': {
+ \ '+': ['powershell.exe', '-NoProfile', '-Command',
+ \ '[Console]::Out.Write((Get-Clipboard -Raw) -replace "`r","")'],
+ \ '*': ['powershell.exe', '-NoProfile', '-Command',
+ \ '[Console]::Out.Write((Get-Clipboard -Raw) -replace "`r","")'],
+ \ },
+ \ 'cache_enabled': 0,
+ \ }
+ endif
+endif
+
+" ============================================================================
+" Colorscheme and Appearance
+" ============================================================================
+syntax enable
+set termguicolors
+set guicursor=
+let &t_SI = "\e[6 q"
+let &t_EI = "\e[2 q"
+set background=dark
+
+" Window title
+let progname = substitute($VIM, '.*[/\\]', '', '')
+set title titlestring=%{progname}\ %f\ +%l\ #%{tabpagenr()}.%{winnr()}
+if &term =~ '^screen' && !has('vim')
+ exe "set t_ts=\e]2; t_fs=\7"
+endif
+
+colorscheme colorscheme
+
+" ============================================================================
+" Custom Functions
+" ============================================================================
+
+" Clear Terminal Buffer
+function! ClearTerminal()
+ if &buftype == 'terminal'
+ let s:scroll_value = &scrollback
+ set scrollback=1
+ call feedkeys("\<C-\\>\<C-n>i")
+ call feedkeys("clear\<CR>")
+ call timer_start(100, {-> execute('let &scrollback=' . s:scroll_value)})
+ endif
+endfunction
+
+" Rename Current File
+function! RenameFile()
+ let old_name = expand('%')
+ let new_name = input('New file name: ', old_name, 'file')
+ if new_name != '' && new_name != old_name
+ try
+ execute 'saveas ' . fnameescape(new_name)
+ if filereadable(old_name) && !filereadable(new_name)
+ call delete(old_name)
+ endif
+ redraw!
+ catch /^Vim\%(\w\+\).*/
+ echohl ErrorMsg | echo 'Error renaming file: ' . v:exception | echohl None
+ endtry
+ endif
+endfunction
+
+" Format with cursor preservation
+function! PreserveCursorFormat()
+ let l:pos = getpos(".")
+ silent! normal! gg=G
+ call setpos('.', l:pos)
+endfunction
+
+" File/URL opener
+function! OpenFileOrUrl(path) abort
+ if !exists('g:os_name')
+ echohl WarningMsg | echom 'OS detection not available' | echohl None
+ return
+ endif
+
+ let cmd = ''
+ if g:os_name ==# 'mac'
+ let cmd = 'open ' . shellescape(a:path)
+ elseif g:os_name ==# 'linux'
+ let cmd = 'xdg-open ' . shellescape(a:path) . ' >/dev/null 2>&1 &'
+ elseif g:os_name ==# 'wsl'
+ let cmd = 'wslview ' . shellescape(a:path) . ' >/dev/null 2>&1 &'
+ elseif g:os_name ==# 'windows'
+ let cmd = 'start "" ' . shellescape(a:path)
+ elseif g:os_name ==# 'termux'
+ let cmd = 'am start -a android.intent.action.VIEW -d ' . shellescape(a:path)
+ else
+ echohl WarningMsg | echom 'No file opener for OS: ' . g:os_name | echohl None
+ return
+ endif
+
+ call system(cmd)
+ if v:shell_error
+ echohl ErrorMsg | echom 'Failed to open: ' . a:path | echohl None
+ endif
+endfunction
+
+" ============================================================================
+" Hex Editing Functions
+" ============================================================================
+
+" Convert to hex
+function! DoHex() abort
+ let current_file = expand('%:p')
+ if empty(current_file)
+ echohl ErrorMsg | echo 'No file name' | echohl None
+ return
+ endif
+
+ let new_file = current_file . '.hex'
+ try
+ execute 'w !xxd > ' . fnameescape(new_file)
+ echohl Directory | echo 'Hex file created: ' . new_file | echohl None
+ catch /^Vim\%(\w\+\):/
+ echohl ErrorMsg | echo 'Error creating hex file: ' . v:exception | echohl None
+ endtry
+endfunction
+
+" Convert from hex
+function! UndoHex() abort
+ let current_file = expand('%:p')
+ if empty(current_file)
+ echohl ErrorMsg | echo 'No file name' | echohl None
+ return
+ endif
+
+ let new_file = substitute(current_file, '\.hex$', '', '')
+ if new_file ==# current_file
+ echohl WarningMsg | echo 'Not a .hex file' | echohl None
+ return
+ endif
+
+ try
+ execute '%!xxd -r'
+ execute 'w ' . fnameescape(new_file)
+ echohl Directory | echo 'Binary file restored: ' . new_file | echohl None
+ catch /^Vim\%(\w\+\):/
+ echohl ErrorMsg | echo 'Error converting hex file: ' . v:exception | echohl None
+ endtry
+endfunction
+
+" Hex state switcher
+function! HexState() abort
+ let choices = [
+ \ '1. Convert to hex',
+ \ '2. Convert from hex',
+ \ '3. Cancel'
+ \ ]
+
+ let choice = inputlist(choices)
+ if choice == 1
+ call DoHex()
+ elseif choice == 2
+ call UndoHex()
+ else
+ echo 'Operation cancelled.'
+ endif
+endfunction
+
+" ============================================================================
+" Commands
+" ============================================================================
+command! -nargs=0 OpenFile call OpenFileOrUrl(expand('%:p'))
+
+" ============================================================================
+" Auto Commands
+" ============================================================================
+
+" Return to last position
+augroup line_return
+ au!
+ au BufReadPost *
+ \ if line("'\"") > 0 && line("'\"") <= line("$") |
+ \ execute 'normal! g`"zvzz' |
+ \ endif
+augroup END
+
+" Auto-completion
+augroup AutoCompleteWordStart
+ autocmd!
+ autocmd InsertCharPre * call AutoComplete()
+augroup END
+
+" Relative numbers in insert mode
+autocmd InsertEnter * set norelativenumber
+autocmd InsertLeave * set relativenumber
+
+" Netrw cleanup
+autocmd FileType netrw setl bufhidden=wipe
+
+" Reload config
+augroup ReloadVimrc
+ autocmd!
+ autocmd BufWritePost $MYVIMRC nested source $MYVIMRC |
+ \ doautocmd ColorScheme |
+ \ call autoload#statusline#ActivateStatusline() |
+ \ echon "Reloaded .vimrc" |
+ \ redraw!
+augroup END
+
+" Auto format
+augroup FormatOnSave
+ autocmd!
+ autocmd BufWritePre * call PreserveCursorFormat()
+augroup END
+
+" ============================================================================
+" Statusline Configuration
+" ============================================================================
+let s:statusline_file = expand('~/.vim/autoload/statusline.vim')
+if filereadable(s:statusline_file)
+ execute 'source ' . fnameescape(s:statusline_file)
+
+ augroup StatuslineConfig
+ autocmd!
+ autocmd VimEnter * call autoload#statusline#ActivateStatusline()
+ autocmd ColorScheme * call autoload#statusline#UpdateStslineColors()
+ autocmd VimEnter * redrawstatus!
+ augroup END
+
+ set laststatus=2
+ set statusline=%!StatusLine()
+
+ augroup StatuslineOverride
+ autocmd!
+ autocmd FileType help,gitcommit,netrw setlocal statusline=%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P
+ augroup END
+endif
+
+" ============================================================================
+" GUI Configuration
+" ============================================================================
+if has('gui_running')
+ color slate
+
+ if has('mac')
+ set guifont=Menlo\ Regular:h14
+ elseif has('win32')
+ set guifont=Consolas:h12
+ else
+ set guifont=Monospace\ 11
+ endif
+
+ set guioptions-=T
+ set guioptions-=r
+ set guioptions-=L
+ set guioptions-=m
+
+ autocmd VimEnter * :Lexplore | wincmd p
+endif
+
+" ============================================================================
+" Final Setup
+" ============================================================================
+syntax on
+filetype plugin indent on
+setlocal keywordprg=:help
+setlocal iskeyword+=:
+
+" Load matchit for better % matching
+if !exists('g:loaded_matchit') && findfile('plugin/matchit.vim', &rtp) ==# ''
+ runtime! macros/matchit.vim
+endif
+
+" ============================================================================
diff --git a/linux/home/.vnc/config b/linux/home/.vnc/config
new file mode 100644
index 0000000..5c1462c
--- /dev/null
+++ b/linux/home/.vnc/config
@@ -0,0 +1,4 @@
+session=bspwm
+geometry=1920x1080
+localhost
+alwaysshared
diff --git a/linux/home/.vnc/xstartup b/linux/home/.vnc/xstartup
new file mode 100755
index 0000000..bbbc0db
--- /dev/null
+++ b/linux/home/.vnc/xstartup
@@ -0,0 +1,16 @@
+#!/data/data/com.termux/files/usr/bin/sh
+
+## file is executed during VNC server
+## startup.
+
+# Launch terminal emulator Aterm.
+# Requires package 'aterm'.
+
+unset SESSION_MANAGER
+unset DBUS_SESSION_BUS_ADDRESS
+
+export PULSE_SERVER=127.0.0.1 && pulseaudio --start --disable-shm=1 --exit-idle-time=-1
+
+# Launch Window Manager/Desktop Environment
+bspwm &
+