commit ad6d893cb030cc849119b21bea4998633ad16c50 Author: Nico Date: Thu Oct 31 10:33:46 2024 +0100 init diff --git a/configuration.nix b/configuration.nix new file mode 100644 index 0000000..887f3a2 --- /dev/null +++ b/configuration.nix @@ -0,0 +1,217 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page +# and in the NixOS manual (accessible by running ‘nixos-help’). + +{ config, pkgs, inputs, ... }: + +{ + imports = + [ # Include the results of the hardware scan. + ./hardware-configuration.nix + inputs.home-manager.nixosModules.default + ]; + + + # home manager + home-manager = { + extraSpecialArgs = { inherit inputs; }; + users = { + "n" = import ./home-nico.nix; + }; + }; + + # hyprland +# programs.hyprland = { +# enable = true; +# xwayland.enable = true; +# }; + + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + + security.polkit.enable = true; + + + # Bootloader. + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + networking.hostName = "nico-nixos-laptop"; # Define your hostname. + # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. + + # Configure network proxy if necessary + # networking.proxy.default = "http://user:password@proxy:port/"; + # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; + + # Enable networking + networking.networkmanager.enable = true; + + # Set your time zone. + time.timeZone = "Europe/Berlin"; + + # Select internationalisation properties. + i18n.defaultLocale = "en_US.UTF-8"; + + i18n.extraLocaleSettings = { + LC_ADDRESS = "de_DE.UTF-8"; + LC_IDENTIFICATION = "de_DE.UTF-8"; + LC_MEASUREMENT = "de_DE.UTF-8"; + LC_MONETARY = "de_DE.UTF-8"; + LC_NAME = "de_DE.UTF-8"; + LC_NUMERIC = "de_DE.UTF-8"; + LC_PAPER = "de_DE.UTF-8"; + LC_TELEPHONE = "de_DE.UTF-8"; + LC_TIME = "de_DE.UTF-8"; + }; + + # Enable the X11 windowing system. + services.xserver.enable = true; + + # Enable the XFCE Desktop Environment. + services.displayManager.sddm.enable = true; + services.xserver.desktopManager.xfce.enable = true; + + + # Configure keymap in X11 + services.xserver = { + xkb = { + variant = ""; + layout = "de"; + }; + }; + + # Configure console keymap + console.keyMap = "de"; + + # Enable CUPS to print documents. + services.printing.enable = true; + + # Enable sound with pipewire. + hardware.pulseaudio.enable = false; + security.rtkit.enable = true; + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + # If you want to use JACK applications, uncomment this + #jack.enable = true; + + # use the example session manager (no others are packaged yet so this is enabled by default, + # no need to redefine it in your config for now) + #media-session.enable = true; + }; + + # Enable touchpad support (enabled default in most desktopManager). + services.libinput.enable = true; + + programs.light.enable = true; + + programs.zsh.enable = true; + + # Define a user account. Don't forget to set a password with ‘passwd’. + users.users.n = { + isNormalUser = true; + description = "n"; + extraGroups = [ "networkmanager" "wheel" "video" ]; + packages = with pkgs; [ + # thunderbird + ]; + shell = pkgs.zsh; + }; + + + + # Install firefox. + programs.firefox.enable = true; + + # Allow unfree packages + nixpkgs.config.allowUnfree = true; + + # List packages installed in system profile. To search, run: + # $ nix search wget + environment.systemPackages = with pkgs; [ + vim + wget + git + kitty + tree + nodejs + musescore + ranger + flutterPackages-source.stable + + # nvim + lunarvim + gnumake + python312Packages.pip + python312 + cargo + ripgrep + gcc + + # sway + grim # screenshot functionality + slurp # screenshot functionality + wl-clipboard # wl-copy and wl-paste for copy/paste from stdin / stdout + mako # notification system developed by swaywm maintainer + kickoff # application launcher + waybar # bar + pulseaudio # audiocontrols + brightnessctl # brightness controls + wpaperd # wallpaper manager + ]; + + services.gnome.gnome-keyring.enable = true; + + programs.sway = { + enable = true; + wrapperFeatures.gtk = true; + }; + + + #fonts +fonts.packages = with pkgs; [ + iosevka + (nerdfonts.override { fonts = [ "JetBrainsMono" "UbuntuMono" ]; }) +]; + + + # Some programs need SUID wrappers, can be configured further or are + # started in user sessions. + # programs.mtr.enable = true; + # programs.gnupg.agent = { + # enable = true; + # enableSSHSupport = true; + # }; + + # List services that you want to enable: + + # Enable the OpenSSH daemon. + # services.openssh.enable = true; + + # Open ports in the firewall. + # networking.firewall.allowedTCPPorts = [ ... ]; + # networking.firewall.allowedUDPPorts = [ ... ]; + # Or disable the firewall altogether. + # networking.firewall.enable = false; + + networking.firewall = { + enable = true; + allowedTCPPortRanges = [ + { from = 1714; to = 1764; } # KDE Connect + ]; + allowedUDPPortRanges = [ + { from = 1714; to = 1764; } # KDE Connect + ]; + }; + + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "24.05"; # Did you read the comment? + +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..b75c899 --- /dev/null +++ b/flake.lock @@ -0,0 +1,99 @@ +{ + "nodes": { + "ags": { + "inputs": { + "nixpkgs": "nixpkgs", + "systems": "systems" + }, + "locked": { + "lastModified": 1728326430, + "narHash": "sha256-tV1ABHuA1HItMdCTuNdA8fMB+qw7LpjvI945VwMSABI=", + "owner": "Aylur", + "repo": "ags", + "rev": "60180a184cfb32b61a1d871c058b31a3b9b0743d", + "type": "github" + }, + "original": { + "owner": "Aylur", + "repo": "ags", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1728903686, + "narHash": "sha256-ZHFrGNWDDriZ4m8CA/5kDa250SG1LiiLPApv1p/JF0o=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "e1aec543f5caf643ca0d94b6a633101942fd065f", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1725634671, + "narHash": "sha256-v3rIhsJBOMLR8e/RNWxr828tB+WywYIoajrZKFM+0Gg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "574d1eac1c200690e27b8eb4e24887f8df7ac27c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1728888510, + "narHash": "sha256-nsNdSldaAyu6PE3YUA+YQLqUDJh+gRbBooMMekZJwvI=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a3c0b3b21515f74fd2665903d4ce6bc4dc81c77c", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "ags": "ags", + "home-manager": "home-manager", + "nixpkgs": "nixpkgs_2" + } + }, + "systems": { + "locked": { + "lastModified": 1689347949, + "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", + "owner": "nix-systems", + "repo": "default-linux", + "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default-linux", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..deaab76 --- /dev/null +++ b/flake.nix @@ -0,0 +1,29 @@ +{ + description = "Nixos config flake"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + ags.url = "github:Aylur/ags"; + }; + + outputs = { self, nixpkgs, ... }@inputs: + let system = "x86_64-linux"; in + { + nixosConfigurations.my-nixos = nixpkgs.lib.nixosSystem { + inherit system; + specialArgs = { + inherit inputs; + }; + modules = [ + ./configuration.nix + inputs.home-manager.nixosModules.default + ]; + }; + }; +} diff --git a/hardware-configuration.nix b/hardware-configuration.nix new file mode 100644 index 0000000..d294671 --- /dev/null +++ b/hardware-configuration.nix @@ -0,0 +1,39 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/1d64861a-3d30-4b76-8a19-9a68618ddf3a"; + fsType = "ext4"; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/580D-111D"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; + }; + + swapDevices = [ ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.enp0s31f6.useDHCP = lib.mkDefault true; + # networking.interfaces.wlp4s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/home-nico.nix b/home-nico.nix new file mode 100644 index 0000000..9fb24cc --- /dev/null +++ b/home-nico.nix @@ -0,0 +1,229 @@ +{ config, pkgs, inputs, ... }: + +{ + home.username = "n"; + home.homeDirectory = "/home/n"; + + imports = [ + ./packages + ]; + + nixpkgs = { + config = { + allowUnfree = true; + allowUnfreePredicate = (_: true); + }; + }; + + home.packages = with pkgs; [ + brave + + ]; + +#fonts.fonts = with pkgs; [ +# (nerdfonts.override { fonts = [ "JetBrainsMono" "UbuntuMono"]; }) +#]; + +programs.kitty = { + enable = true; + + extraConfig = '' + +# vim:ft=kitty + +## name: Catppuccin Kitty Mocha +## author: Catppuccin Org +## license: MIT +## upstream: https://github.com/catppuccin/kitty/blob/main/themes/mocha.conf +## blurb: Soothing pastel theme for the high-spirited! + + + +# The basic colors +foreground #cdd6f4 +background #1e1e2e +selection_foreground #1e1e2e +selection_background #f5e0dc + +# Cursor colors +cursor #f5e0dc +cursor_text_color #1e1e2e + +# URL underline color when hovering with mouse +url_color #f5e0dc + +# Kitty window border colors +active_border_color #b4befe +inactive_border_color #6c7086 +bell_border_color #f9e2af + +# OS Window titlebar colors +wayland_titlebar_color system +macos_titlebar_color system + +# Tab bar colors +active_tab_foreground #11111b +active_tab_background #cba6f7 +inactive_tab_foreground #cdd6f4 +inactive_tab_background #181825 +tab_bar_background #11111b + +# Colors for marks (marked text in the terminal) +mark1_foreground #1e1e2e +mark1_background #b4befe +mark2_foreground #1e1e2e +mark2_background #cba6f7 +mark3_foreground #1e1e2e +mark3_background #74c7ec + +# The 16 terminal colors + +# black +color0 #45475a +color8 #585b70 + +# red +color1 #f38ba8 +color9 #f38ba8 + +# green +color2 #a6e3a1 +color10 #a6e3a1 + +# yellow +color3 #f9e2af +color11 #f9e2af + +# blue +color4 #89b4fa +color12 #89b4fa + +# magenta +color5 #f5c2e7 +color13 #f5c2e7 + +# cyan +color6 #94e2d5 +color14 #94e2d5 + +# white +color7 #bac2de +color15 #a6adc8 + ''; +}; + + wayland.windowManager.sway = { + enable = true; + config = rec { + modifier = "Mod4"; + # Use kitty as default terminal + terminal = "kitty"; + bars = [ + {command = "waybar";} + ]; + + startup = [ + # Launch Firefox on start + {command = "wpaperd";} + ]; + }; + + + extraConfig = '' + ## Input configuration + input "type:keyboard" { + xkb_layout de + xkb_variant "nodeadkeys" + } + +input "type:touchpad" { + tap enabled # enables click-on-tap + tap_button_map lrm # tap with 1 finger = left click, 2 fingers = right click, 3 fingers = middle click + dwt disabled # disable (touchpad) while typing + dwtp disabled # disable (touchpad) while track pointing +} + + # Brightness + bindsym XF86MonBrightnessDown exec light -U 10 + bindsym XF86MonBrightnessUp exec light -A 10 + + # Volume + bindsym XF86AudioRaiseVolume exec 'pactl set-sink-volume @DEFAULT_SINK@ +1%' + bindsym XF86AudioLowerVolume exec 'pactl set-sink-volume @DEFAULT_SINK@ -1%' + bindsym XF86AudioMute exec 'pactl set-sink-mute @DEFAULT_SINK@ toggle' + + # app launcher + bindsym Mod4+m exec 'kickoff' + bindsym Mod4+BackSpace kill + + # disable window border +default_border pixel 2 +font pango:monospace 0.001 +titlebar_padding 1 +titlebar_border_thickness 0 + ''; + }; + +programs.starship = { + enable = true; + + settings = { + + }; +}; + +programs.git = { + enable = true; + userName = "Nico"; + userEmail = "nicovessen@gmail.com"; + extraConfig = { + safe.directory = "/etc/nixos"; + }; +}; + +services.kdeconnect = { + enable = true; + indicator = true; +}; + +programs.neovim = { + enable = true; + vimAlias = true; +}; + +programs.zsh = { + enable = true; + enableCompletion = true; + autosuggestion.enable = true; + syntaxHighlighting.enable = true; + initExtra = '' + eval "$(starship init zsh)" + ''; + + + shellAliases = { + update = "sudo nixos-rebuild switch --flake /etc/nixos/#my-nixos"; + }; + zplug = { + enable = true; + plugins = [ + { name = "zsh-users/zsh-autosuggestions"; } # Simple plugin installation + ]; + }; + + oh-my-zsh = { + enable = true; + plugins = [ "git" ]; + theme = "agnoster"; + }; + + + +}; + + + home.stateVersion = "24.05"; + + programs.home-manager.enable = true; + +} diff --git a/packages/ags-backup/config.js.bk b/packages/ags-backup/config.js.bk new file mode 100644 index 0000000..4b1948f --- /dev/null +++ b/packages/ags-backup/config.js.bk @@ -0,0 +1,26 @@ +const time = Variable('', { + poll: [1000, function() { + return Date().toString() + }], +}) + +const Bar = (/** @type {number} */ monitor) => Widget.Window({ + monitor, + name: `bar${monitor}`, + anchor: ['top', 'left', 'right'], + exclusivity: 'exclusive', + child: Widget.CenterBox({ + start_widget: Widget.Label({ + hpack: 'center', + label: 'Welcome to AGS!', + }), + end_widget: Widget.Label({ + hpack: 'center', + label: time.bind(), + }), + }), +}) + +App.config({ + windows: [Bar(0)], +}) diff --git a/packages/ags-backup/default.nix.bk b/packages/ags-backup/default.nix.bk new file mode 100755 index 0000000..b0ccb79 --- /dev/null +++ b/packages/ags-backup/default.nix.bk @@ -0,0 +1,21 @@ +{ inputs, pkgs, lib, config, ... }: +{ + imports = [ inputs.ags.homeManagerModules.default ]; + + programs.ags = { + enable = true; + + extraPackages = with pkgs; [ + gtksourceview + webkitgtk + accountsservice + ]; + }; + + home.file = + lib.mkMerge [ + { + ".config/ags/config.js".source = ./config.js; + } + ]; +} diff --git a/packages/ags-backup/tsconfig.json.bk b/packages/ags-backup/tsconfig.json.bk new file mode 100644 index 0000000..f03f2d1 --- /dev/null +++ b/packages/ags-backup/tsconfig.json.bk @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "lib": [ + "ES2022" + ], + "allowJs": true, + "checkJs": true, + "strict": true, + "noImplicitAny": false, + "baseUrl": ".", + "typeRoots": [ + "./types" + ], + "skipLibCheck": true + } +} \ No newline at end of file diff --git a/packages/ags-backup/types.bk b/packages/ags-backup/types.bk new file mode 120000 index 0000000..70169f5 --- /dev/null +++ b/packages/ags-backup/types.bk @@ -0,0 +1 @@ +/home/nico/.local/share/com.github.Aylur.ags/types \ No newline at end of file diff --git a/packages/ags/bk/assets/battery-flash-symbolic.svg b/packages/ags/bk/assets/battery-flash-symbolic.svg new file mode 100644 index 0000000..21b5e33 --- /dev/null +++ b/packages/ags/bk/assets/battery-flash-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/bk/assets/chat-bubbles-symbolic.svg b/packages/ags/bk/assets/chat-bubbles-symbolic.svg new file mode 100644 index 0000000..fdee0b3 --- /dev/null +++ b/packages/ags/bk/assets/chat-bubbles-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/ags/bk/assets/controller-symbolic.svg b/packages/ags/bk/assets/controller-symbolic.svg new file mode 100644 index 0000000..98bf5d6 --- /dev/null +++ b/packages/ags/bk/assets/controller-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/bk/assets/controls-symbolic.svg b/packages/ags/bk/assets/controls-symbolic.svg new file mode 100644 index 0000000..7df5663 --- /dev/null +++ b/packages/ags/bk/assets/controls-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/ags/bk/assets/dark-mode-symbolic.svg b/packages/ags/bk/assets/dark-mode-symbolic.svg new file mode 100644 index 0000000..9f2e6b4 --- /dev/null +++ b/packages/ags/bk/assets/dark-mode-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/bk/assets/hourglass-symbolic.svg b/packages/ags/bk/assets/hourglass-symbolic.svg new file mode 100644 index 0000000..aa4f97c --- /dev/null +++ b/packages/ags/bk/assets/hourglass-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/bk/assets/light-mode-symbolic.svg b/packages/ags/bk/assets/light-mode-symbolic.svg new file mode 100644 index 0000000..d5fb271 --- /dev/null +++ b/packages/ags/bk/assets/light-mode-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/bk/assets/mixer-symbolic.svg b/packages/ags/bk/assets/mixer-symbolic.svg new file mode 100644 index 0000000..ad6cfa8 --- /dev/null +++ b/packages/ags/bk/assets/mixer-symbolic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/ags/bk/assets/nixos-symbolic.svg b/packages/ags/bk/assets/nixos-symbolic.svg new file mode 100644 index 0000000..7bb42ed --- /dev/null +++ b/packages/ags/bk/assets/nixos-symbolic.svg @@ -0,0 +1,155 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ags/bk/assets/nixos.svg b/packages/ags/bk/assets/nixos.svg new file mode 100644 index 0000000..1a756ed --- /dev/null +++ b/packages/ags/bk/assets/nixos.svg @@ -0,0 +1,277 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ags/bk/assets/person-symbolic.svg b/packages/ags/bk/assets/person-symbolic.svg new file mode 100644 index 0000000..dd2fe1b --- /dev/null +++ b/packages/ags/bk/assets/person-symbolic.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ags/bk/assets/processor-symbolic.svg b/packages/ags/bk/assets/processor-symbolic.svg new file mode 100644 index 0000000..832dbaf --- /dev/null +++ b/packages/ags/bk/assets/processor-symbolic.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/ags/bk/assets/terminal-symbolic.svg b/packages/ags/bk/assets/terminal-symbolic.svg new file mode 100644 index 0000000..9f82bcf --- /dev/null +++ b/packages/ags/bk/assets/terminal-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/ags/bk/assets/toolbars-symbolic.svg b/packages/ags/bk/assets/toolbars-symbolic.svg new file mode 100644 index 0000000..9f4c564 --- /dev/null +++ b/packages/ags/bk/assets/toolbars-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/bk/config.js b/packages/ags/bk/config.js new file mode 100644 index 0000000..e947d6c --- /dev/null +++ b/packages/ags/bk/config.js @@ -0,0 +1,87 @@ +App.addIcons(`${App.configDir}/assets`) + +import Gdk from 'gi://Gdk'; +import GLib from 'gi://GLib'; +import App from 'resource:///com/github/Aylur/ags/app.js' +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js' + +import options from './options.js'; +import { init } from './lib/init.js'; + +import Lockscreen from './modules/lock/Lockscreen.js'; +import Powermenu from './modules/powermenu/Powermenu.js'; +import OSD from './modules/osd/OSD.js'; +import Verification from './modules/powermenu/Verification.js'; +import Overview from './modules/overview/Overview.js'; +import NotificationPopups from './modules/notifications/NotificationPopups.js'; +import Bar from './modules/bar/Bar.js' +import { setupDateMenu } from './modules/datemenu/DateMenu.js'; +import { setupQuickSettings } from './modules/quicksettings/QuickSettings.js'; + + +const range = (length, start = 1) => Array.from({ length }, (_, i) => i + start); +function forMonitors(widget) { + const n = Gdk.Display.get_default()?.get_n_monitors() || 1; + return range(n, 0).map(widget).flat(1); +} + + + +print(App.configDir) +// SCSS compilation +// Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles +// Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicmaterial.scss'`); // reset music styles +async function applyStyle() { + const COMPILED_STYLE_DIR = `${GLib.get_user_cache_dir()}/ags/user/generated` + if (options.recompileSass) { + Utils.exec(`mkdir -p ${COMPILED_STYLE_DIR}`); + Utils.exec(`sassc ${App.configDir}/scss/main.scss ${COMPILED_STYLE_DIR}/style.css`); + } + App.resetCss(); + App.applyCss(`${COMPILED_STYLE_DIR}/style.css`); + print('[LOG] Styles loaded') + print(`CRITICAL: Reload Sass option is set to: ${options.recompileSass}`) +} +applyStyle().catch(print); + + +const Windows = () => [ + Lockscreen(), + Powermenu(), + // Dock(), + Verification(), + Overview(), + forMonitors(NotificationPopups), + forMonitors(Bar), + forMonitors(OSD), +]; + +// const CLOSE_ANIM_TIME = 210; // Longer than actual anim time to make sure widgets animate fully +export default { + onConfigParsed: () => { + setupQuickSettings() + setupDateMenu() + init() + if (options.monitorCSS) { + Utils.monitorFile( + // directory that contains the scss files + `${App.configDir}/scss`, + function() { + applyStyle() + }, + ) + } + }, + css: `${App.configDir}/style.css`, + stackTraceOnError: true, + closeWindowDelay: { // For animations + "powermenu": options.theme.PopupCloseDuration, + "verification": options.theme.PopupCloseDuration, + "overview": options.theme.PopupCloseDuration, + "quicksettings": options.theme.PopupCloseDuration, + // 'sideright': CLOSE_ANIM_TIME, + // 'sideleft': CLOSE_ANIM_TIME, + // 'osk': CLOSE_ANIM_TIME, + }, + windows: Windows().flat(1), +}; diff --git a/packages/ags/bk/lib/battery.js b/packages/ags/bk/lib/battery.js new file mode 100644 index 0000000..574ba29 --- /dev/null +++ b/packages/ags/bk/lib/battery.js @@ -0,0 +1,17 @@ + +import icons from "./icons.js" + +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/packages/ags/bk/lib/experiments.js b/packages/ags/bk/lib/experiments.js new file mode 100644 index 0000000..fee1c2f --- /dev/null +++ b/packages/ags/bk/lib/experiments.js @@ -0,0 +1,67 @@ + +// these are functionalities that I might include in ags + +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Variable } from "resource:///com/github/Aylur/ags/variable.js" +import { App } from "resource:///com/github/Aylur/ags/app.js" +import GObject from "gi://GObject?version=2.0" + +// eslint-disable-next-line max-len +// export function watch(init, objs, callback) +// export function watch(init, obj, callback) +export function pwatch( + init, + objs, + sigOrFn, + callback, +) { + const v = new Variable(init) + const f = typeof sigOrFn === "function" ? sigOrFn : callback ?? (() => v.value) + const set = () => v.value = f() + + if (Array.isArray(objs)) { + // multiple objects + for (const obj of objs) { + if (Array.isArray(obj)) { + // obj signal pair + const [o, s = "changed"] = obj + o.connect(s, set) + } else { + // obj on changed + obj.connect("changed", set) + } + } + } else { + // watch single object + const signal = typeof sigOrFn === "string" ? sigOrFn : "changed" + objs.connect(signal, set) + } + + return v.bind() +} +export function watch(init, objs, sigOrFn, callback) { + print(objs) + const v = new Variable(init); + const f = typeof sigOrFn === "function" ? sigOrFn : (callback || (() => v.value)); + const set = () => { v.value = f(); }; + + if (Array.isArray(objs)) { + // multiple objects + for (const obj of objs) { + if (Array.isArray(obj)) { + // obj signal pair + const [o, s = "changed"] = obj; + o.connect(s, set); + } else { + // obj on changed + obj.connect("changed", set); + } + } + } else { + // watch single object + const signal = typeof sigOrFn === "string" ? sigOrFn : "changed"; + objs.connect(signal, set); + } + + return v.bind(); +} diff --git a/packages/ags/bk/lib/icons.js b/packages/ags/bk/lib/icons.js new file mode 100644 index 0000000..690a806 --- /dev/null +++ b/packages/ags/bk/lib/icons.js @@ -0,0 +1,133 @@ + +export const substitutes = { + "transmission-gtk": "transmission", + "blueberry.py": "blueberry", + "Caprine": "facebook-messenger", + "com.raggesilver.BlackBox-symbolic": "terminal-symbolic", + "audio-headset-bluetooth": "audio-headphones-symbolic", + "audio-card-analog-usb": "audio-speakers-symbolic", + "audio-card-analog-pci": "audio-card-symbolic", + "preferences-system": "emblem-system-symbolic", + "com.github.Aylur.ags-symbolic": "controls-symbolic", + "com.github.Aylur.ags": "controls-symbolic", +} + +export default { + fallback: { + executable: "application-x-executable-symbolic", + notification: "dialog-information-symbolic", + video: "video-x-generic-symbolic", + audio: "audio-x-generic-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", + person: "person-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-overamplified-symbolic", + }, + type: { + headset: "audio-headphones-symbolic", + speaker: "audio-speakers-symbolic", + card: "audio-card-symbolic", + }, + mixer: "mixer-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", + }, +} diff --git a/packages/ags/bk/lib/init.js b/packages/ags/bk/lib/init.js new file mode 100644 index 0000000..d42da9e --- /dev/null +++ b/packages/ags/bk/lib/init.js @@ -0,0 +1,19 @@ + +// import hyprland from "./hyprland.js" +// import tmux from "./tmux" +// import gtk from "./gtk" +import lowBattery from "./battery.js" +// import swww from "./swww" + +export async function init() { + try { + // gtk() + // tmux() + lowBattery() + // hyprland() + // css() + // swww() + } catch (error) { + logError(error) + } +} diff --git a/packages/ags/bk/lib/utils.js b/packages/ags/bk/lib/utils.js new file mode 100644 index 0000000..d3cf694 --- /dev/null +++ b/packages/ags/bk/lib/utils.js @@ -0,0 +1,120 @@ + +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { substitutes } from "./icons.js" +import Gtk from "gi://Gtk?version=3.0" +import Gdk from "gi://Gdk" +import GLib from "gi://GLib?version=2.0" + +export function config(config) { + return config +} + +/** + * @returns substitute icon || name || fallback icon + */ +export function icon(name, fallback = name) { + 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, ...values) { + 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) { + return Utils.execAsync(cmd).catch(err => { + console.error(typeof cmd === "string" ? cmd : cmd.join(" "), err) + return "" + }) +} + +export function forMonitors(widget) { + 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, start = 1) { + return Array.from({ length }, (_, i) => i + start) +} + +/** + * promisified timeout + */ +export function wait(ms, callback) { + return new Promise(resolve => Utils.timeout(ms, () => { + resolve(callback()) + })) +} + +/** + * @returns true if all of the `bins` are found + */ +export function dependencies(...bins) { + const missing = bins.filter(bin => { + return !Utils.exec(`which ${bin}`) + }) + + if (missing.length > 0) + console.warn("missing dependencies:", missing.join(", ")) + + return missing.length === 0 +} + +/** + * run app detached + */ +export function launchApp(app) { + 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) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const cairo = imports.gi.cairo + 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/packages/ags/bk/lib/variables.js b/packages/ags/bk/lib/variables.js new file mode 100644 index 0000000..383d77e --- /dev/null +++ b/packages/ags/bk/lib/variables.js @@ -0,0 +1,16 @@ + +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, + ], +}) diff --git a/packages/ags/bk/modules/PopupWindow.js b/packages/ags/bk/modules/PopupWindow.js new file mode 100644 index 0000000..15fc73b --- /dev/null +++ b/packages/ags/bk/modules/PopupWindow.js @@ -0,0 +1,158 @@ +import Gtk from "gi://Gtk?version=3.0" +import options from '../options.js'; + +export const Padding = (name, + css = "", + hexpand = true, + vexpand = true, +) => Widget.EventBox({ + hexpand, + vexpand, + can_focus: false, + child: Widget.Box({ css }), + setup: w => w.on("button-press-event", () => App.toggleWindow(name)), +}) + +const PopupRevealer = ( + name, + child, + transition = "slide_down", +) => Widget.Box( + { css: "padding: 1px;", class_name: "popup-borderbox" }, + Widget.Revealer({ + transition, + child: Widget.Box({ + class_name: "window-content", + child, + }), + transitionDuration: options.theme.PopupTransitionDuration, + setup: self => self.hook(App, (_, wname, visible) => { + if (wname === name) + self.reveal_child = visible + }), + }), +) + +const Layout = (name, child, 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), + ), + ), + "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 +}) => Widget.Window({ + name, + class_names: [name, "popup-window"], + popup: true, + visible: false, + keymode: "on-demand", + exclusivity, + layer: "top", + anchor: ["top", "bottom", "right", "left"], + child: Layout(name, child, transition)[layout](), + ...props, + setup: (self => { + self.keybind("Escape", () => App.closeWindow(name)) + }) +}) diff --git a/packages/ags/bk/modules/bar/Bar.js b/packages/ags/bk/modules/bar/Bar.js new file mode 100644 index 0000000..9dea146 --- /dev/null +++ b/packages/ags/bk/modules/bar/Bar.js @@ -0,0 +1,56 @@ +import BatteryBar from "./buttons/BatteryBar.js" +import ColorPicker from "./buttons/ColorPicker.js" +import Date from "./buttons/Date.js" +import Launcher from "./buttons/Launcher.js" +import Media from "./buttons/Media.js" +import PowerMenu from "./buttons/PowerMenu.js" +import SysTray from "./buttons/SysTray.js" +import SystemIndicators from "./buttons/SystemIndicators.js" +import Taskbar from "./buttons/Taskbar.js" +import Workspaces from "./buttons/Workspaces.js" +import ScreenRecord from "./buttons/ScreenRecord.js" +import Messages from "./buttons/Messages.js" +import options from "../../options.js" + +const { start, center, end } = options.bar.layout +const pos = options.bar.position + +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) => Widget.Window({ + monitor, + class_name: "bar", + name: `bar${monitor}`, + exclusivity: "exclusive", + layer: "top", + anchor: [pos, "right", "left"], + child: Widget.CenterBox({ + css: "min-width: 2px; min-height: 2px;", + startWidget: Widget.Box({ + hexpand: true, + children: start.map(w => widget[w]()), + }), + centerWidget: Widget.Box({ + hpack: "center", + children: center.map(w => widget[w]()), + }), + endWidget: Widget.Box({ + hexpand: true, + children: end.map(w => widget[w]()), + }), + }), +}) diff --git a/packages/ags/bk/modules/bar/PanelButton.js b/packages/ags/bk/modules/bar/PanelButton.js new file mode 100644 index 0000000..8c1fc50 --- /dev/null +++ b/packages/ags/bk/modules/bar/PanelButton.js @@ -0,0 +1,38 @@ +import options from "../../options.js" + +export default ({ + window = "", + flat, + child, + setup, + ...rest +}) => Widget.Button({ + child: Widget.Box({ child }), + setup: self => { + let open = false + + self.toggleClassName("panel-button") + self.toggleClassName(window) + + self.toggleClassName("flat", flat ?? options.bar.flatButtons) + + 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/packages/ags/bk/modules/bar/ScreenCorners.js b/packages/ags/bk/modules/bar/ScreenCorners.js new file mode 100644 index 0000000..4e7cba4 --- /dev/null +++ b/packages/ags/bk/modules/bar/ScreenCorners.js @@ -0,0 +1,25 @@ +import options from "../../options.js" + +const { corners } = options.bar + +export default (monitor) => 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.toggleClassName("corners", corners) + }, +}) diff --git a/packages/ags/bk/modules/bar/bar.scss b/packages/ags/bk/modules/bar/bar.scss new file mode 100644 index 0000000..472894c --- /dev/null +++ b/packages/ags/bk/modules/bar/bar.scss @@ -0,0 +1,234 @@ +@use 'sass:color'; + +$bar-spacing: $spacing * .3; +$button-radius: $radius; + +@mixin panel-button($flat: true, $reactive: true) { + @include accs-button($flat, $reactive); + + >* { + border-radius: $button-radius; + margin: $bar-spacing; + } + + label, + image { + font-weight: bold; + } + + >* { + padding: $padding * 0.4 $padding * 0.8; + } +} + +.bar { + background-color: $bg; + + .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*.6; + box-shadow: inset 0 0 0 $border-width $border-color; + margin: 0 $padding * .5; + transition: $transition* .5; + background-color: transparentize($fg, .8); + + &.occupied { + background-color: transparentize($fg, .2); + min-width: 7pt; + min-height: 7pt; + } + + &.active { + // background-color: $primary-bg; + background-image: $active-gradient; + min-width: 20pt; + min-height: 12pt; + } + } + + &.active, + &:active { + label { + background-color: transparentize($primary-fg, .3); + + &.occupied { + background-color: transparentize($primary-fg, .15); + } + + &.active { + background-color: $primary-fg; + } + } + } + } + + .media label { + margin: 0 ($spacing * .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 * .7; + color: $fg; + text-shadow: $text-shadow; + } + } + } + + .battery-bar { + >* { + padding: 0; + } + + &.bar-hidden>box { + padding: 0 $spacing * .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 * .5; + } + + trough { + @include widget; + min-height: 12pt; + min-width: 12pt; + } + + .regular trough { + margin-right: $spacing * .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/packages/ags/bk/modules/bar/buttons/BatteryBar.js b/packages/ags/bk/modules/bar/buttons/BatteryBar.js new file mode 100644 index 0000000..4eada2a --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/BatteryBar.js @@ -0,0 +1,94 @@ +import icons from "../../../lib/icons.js" +import options from "../../../options.js" +import PanelButton from "../PanelButton.js" + +const battery = await Service.import("battery") +let { bar, percentage, blocks, width, low } = options.bar.battery + +percentage = Variable(percentage, {}) + +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({ + mode: 1, + max_value: blocks, + visible: bar !== "hidden", + value: battery.bind("percent").as(p => (p / 100) * blocks), + }) + const update = () => { + level.value = (battery.percent / 100) * blocks + level.css = `block { min-width: ${width / blocks}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 }, + child: Widget.Box({ + expand: true, + visible: battery.bind("available"), + child: bar === "whole" ? WholeButton() : Regular(), + }), + setup: self => { + self.toggleClassName("bar-hidden", bar === "hidden") + self.toggleClassName("charging", battery.charging || battery.charged) + self.toggleClassName("low", battery.percent < low) + } +}) diff --git a/packages/ags/bk/modules/bar/buttons/ColorPicker.js b/packages/ags/bk/modules/bar/buttons/ColorPicker.js new file mode 100644 index 0000000..e4366ae --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/ColorPicker.js @@ -0,0 +1,37 @@ +import PanelButton from "../PanelButton.js" +import colorpicker from "../../../services/colorpicker.js" +import Gdk from "gi://Gdk" + +const css = (color) => ` +* { + 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/packages/ags/bk/modules/bar/buttons/Date.js b/packages/ags/bk/modules/bar/buttons/Date.js new file mode 100644 index 0000000..b9c5f26 --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/Date.js @@ -0,0 +1,19 @@ +import clock from "../../../services/clock.js" +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" + +const { format, action } = options.bar.date +// const time = Utils.derive([clock], (c) => { +// c.format(format) || "" +// }) + + +// const time = Variable('', { +// poll: [1000, `date "+${format}"`], +// }); + +export default () => PanelButton({ + window: "dashboard", + on_clicked: action, + child: Widget.Label({ label: clock.bind('time').as(t => `${t.format(format)}`) }), +}) diff --git a/packages/ags/bk/modules/bar/buttons/Launcher.js b/packages/ags/bk/modules/bar/buttons/Launcher.js new file mode 100644 index 0000000..0288171 --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/Launcher.js @@ -0,0 +1,21 @@ +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" + +const { icon, label, action } = options.bar.launcher + +export default () => PanelButton({ + window: "launcher", + on_clicked: action, + child: Widget.Box([ + Widget.Icon({ + class_name: icon.colored ? "colored" : "", + visible: !!icon.icon, + icon: icon.icon, + }), + Widget.Label({ + class_name: label.colored ? "colored" : "", + visible: !!label.label, + label: label.label, + }), + ]), +}) diff --git a/packages/ags/bk/modules/bar/buttons/Media.js b/packages/ags/bk/modules/bar/buttons/Media.js new file mode 100644 index 0000000..0c13b34 --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/Media.js @@ -0,0 +1,81 @@ +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" +import icons from "../../../lib/icons.js" +import { icon } from "../../../lib/utils.js" + +const mpris = await Service.import("mpris") +const { length, direction, preferred, monochrome } = options.bar.media + +const getPlayer = (name = preferred) => + mpris.getPlayer(name) || mpris.players[0] || null + +const Content = (player) => { + const revealer = Widget.Revealer({ + click_through: true, + visible: (length > 0), + transition: `slide_${direction}`, + 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, + label: player.bind("track_title").as(() => + `${player.track_artists.join(", ")} - ${player.track_title}`), + }), + }) + + const playericon = Widget.Icon({ + icon: player.bind("entry").as(entry => { + const name = `${entry}${monochrome ? "-symbolic" : ""}` + return icon(name, icons.fallback.audio) + }), + }) + + return Widget.Box({ + attribute: { revealer }, + children: direction === "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/packages/ags/bk/modules/bar/buttons/Messages.js b/packages/ags/bk/modules/bar/buttons/Messages.js new file mode 100644 index 0000000..3d90dc7 --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/Messages.js @@ -0,0 +1,16 @@ +import icons from "../../../lib/icons.js" +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" + +const n = await Service.import("notifications") +const notifs = n.bind("notifications") +const action = options.bar.messages.action + +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/packages/ags/bk/modules/bar/buttons/PowerMenu.js b/packages/ags/bk/modules/bar/buttons/PowerMenu.js new file mode 100644 index 0000000..39dd147 --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/PowerMenu.js @@ -0,0 +1,15 @@ +import icons from "../../../lib/icons.js" +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" + +const { monochrome, action } = options.bar.powermenu + +export default () => PanelButton({ + window: "powermenu", + on_clicked: action, + child: Widget.Icon(icons.powermenu.shutdown), + setup: self => { + self.toggleClassName("colored", !monochrome) + self.toggleClassName("box") + }, +}) diff --git a/packages/ags/bk/modules/bar/buttons/ScreenRecord.js b/packages/ags/bk/modules/bar/buttons/ScreenRecord.js new file mode 100644 index 0000000..9171d21 --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/ScreenRecord.js @@ -0,0 +1,21 @@ +import PanelButton from "../PanelButton.js" +import screenrecord from "../../../services/screenrecord.js" +import icons from "../../../lib/icons.js" + +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/packages/ags/bk/modules/bar/buttons/SysTray.js b/packages/ags/bk/modules/bar/buttons/SysTray.js new file mode 100644 index 0000000..e962a6b --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/SysTray.js @@ -0,0 +1,39 @@ +import PanelButton from "../PanelButton.js" +import Gdk from "gi://Gdk" +import options from "../../../options.js" + +const systemtray = await Service.import("systemtray") +const { ignore } = options.bar.systray + +const SysTrayItem = (item) => PanelButton({ + class_name: "tray-item", + child: Widget.Icon({ icon: item.bind("icon") }), + tooltip_markup: item.bind("tooltip_markup"), + setup: self => { + const menu = item.menu + if (!menu) + return + + const id = item.menu?.connect("popped-up", () => { + self.toggleClassName("active") + menu.connect("notify::visible", () => { + self.toggleClassName("active", menu.visible) + }) + menu.disconnect(id) + }) + + if (id) + self.connect("destroy", () => item.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.includes(id)) + .map(SysTrayItem)) diff --git a/packages/ags/bk/modules/bar/buttons/SystemIndicators.js b/packages/ags/bk/modules/bar/buttons/SystemIndicators.js new file mode 100644 index 0000000..2a5c44a --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/SystemIndicators.js @@ -0,0 +1,79 @@ +import PanelButton from "../PanelButton.js" +import icons from "../../../lib/icons.js" +import asusctl from "../../../services/asusctl.js" + +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 ProfileIndicator = () => Widget.Icon() + .bind("visible", asusctl, "profile", p => p !== "Balanced") + .bind("icon", asusctl, "profile", p => icons.asusctl.profile[p]) + +const ModeIndicator = () => Widget.Icon() + .bind("visible", asusctl, "mode", m => m !== "Hybrid") + .bind("icon", asusctl, "mode", m => icons.asusctl.mode[m]) + +const MicrophoneIndicator = () => Widget.Icon() + .hook(audio, self => self.visible = + audio.recorders.length > 0 + || audio.microphone.stream?.is_muted + || audio.microphone.is_muted) + .hook(audio.microphone, self => { + const vol = audio.microphone.stream.is_muted ? 0 : audio.microphone.volume + const { muted, low, medium, high } = icons.audio.mic + const cons = [[67, high], [34, medium], [1, low], [0, muted]] + 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({ + icon: audio.speaker.bind("volume").as(vol => { + const { muted, low, medium, high, overamplified } = icons.audio.volume + const cons = [[101, overamplified], [67, high], [34, medium], [1, low], [0, muted]] + const icon = cons.find(([n]) => n <= vol * 100)?.[1] || "" + return audio.speaker.is_muted ? muted : icon + }), +}) + +export default () => PanelButton({ + class_name: "quicksettings panel-button", + on_clicked: () => App.toggleWindow("quicksettings"), + on_scroll_up: () => audio.speaker.volume += 0.02, + on_scroll_down: () => audio.speaker.volume -= 0.02, + child: Widget.Box([ + asusctl?.available && ProfileIndicator(), + asusctl?.available && ModeIndicator(), + DNDIndicator(), + BluetoothIndicator(), + NetworkIndicator(), + AudioIndicator(), + MicrophoneIndicator(), + ]), +}) diff --git a/packages/ags/bk/modules/bar/buttons/Taskbar.js b/packages/ags/bk/modules/bar/buttons/Taskbar.js new file mode 100644 index 0000000..1c63b4b --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/Taskbar.js @@ -0,0 +1,86 @@ +import { launchApp, icon } from "../../../lib/utils.js" +import icons from "../../../lib/icons.js" +import options from "../../../options.js" +import { watch } from "../../../lib/experiments.js" +import PanelButton from "../PanelButton.js" + +const hyprland = await Service.import("hyprland") +const apps = await Service.import("applications") +const { monochrome, exclusive } = options.bar.taskbar +const { position } = options.bar + +const focus = (address) => hyprland.messageAsync( + `dispatch focuswindow address:${address}`) + +const DummyItem = (address) => Widget.Box({ + attribute: { address }, + visible: false, +}) + +const AppItem = (address) => { + 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: client.title, + on_primary_click: () => focus(address), + on_middle_click: () => app && launchApp(app), + visible: watch(true, [hyprland], () => { + return exclusive + ? hyprland.active.workspace.id === client.workspace.id + : true + }), + child: Widget.Icon({ + icon: icon( + (app?.icon_name || client.class) + (monochrome ? "-symbolic" : ""), + icons.fallback.executable, + ), + }), + }) + + return Widget.Box( + { attribute: { address } }, + Widget.Overlay({ + child: btn, + pass_through: true, + overlay: Widget.Box({ + className: "indicator", + hpack: "center", + vpack: (position === "top" ? "start" : "end"), + setup: w => w.hook(hyprland, () => { + w.toggleClassName("active", hyprland.active.client.address === address) + }), + }), + }), + ) +} + +function sortItems(arr) { + 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) => { + if (typeof address === "string") + w.children = w.children.filter(ch => ch.attribute.address !== address) + }, "client-removed") + .hook(hyprland, (w, address) => { + if (typeof address === "string") + w.children = sortItems([...w.children, AppItem(address)]) + }, "client-added") + .hook(hyprland, (w, event) => { + if (event === "movewindow") + w.children = sortItems(w.children) + }, "event"), +}) diff --git a/packages/ags/bk/modules/bar/buttons/Workspaces.js b/packages/ags/bk/modules/bar/buttons/Workspaces.js new file mode 100644 index 0000000..a3207f0 --- /dev/null +++ b/packages/ags/bk/modules/bar/buttons/Workspaces.js @@ -0,0 +1,38 @@ +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" +import { sh, range } from "../../../lib/utils.js" + +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 => self.hook(hyprland, () => { + self.toggleClassName("active", hyprland.active.workspace.id === i) + self.toggleClassName("occupied", (hyprland.getWorkspace(i)?.windows || 0) > 0) + }), + })), + setup: box => { + if (ws === 0) { + box.hook(hyprland.active.workspace, () => box.children.map(btn => { + btn.visible = hyprland.workspaces.some(ws => ws.id === btn.attribute) + })) + } + }, +}) + +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(workspaces), +}) diff --git a/packages/ags/bk/modules/bar/screencorner.scss b/packages/ags/bk/modules/bar/screencorner.scss new file mode 100644 index 0000000..d3b43a0 --- /dev/null +++ b/packages/ags/bk/modules/bar/screencorner.scss @@ -0,0 +1,50 @@ +$_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 $shadow-color; + } + + @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 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/packages/ags/bk/modules/datemenu/DateColumn.js b/packages/ags/bk/modules/datemenu/DateColumn.js new file mode 100644 index 0000000..1fae685 --- /dev/null +++ b/packages/ags/bk/modules/datemenu/DateColumn.js @@ -0,0 +1,38 @@ +import { uptime } from "../../lib/variables.js" +import clock from "../../services/clock.js" + +function up(up) { + 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('time').as(t => t.format("%H:%M")), + }), + Widget.Label({ + class_name: "uptime", + label: uptime.bind().as(up), + }), + ], + }), + Widget.Box({ + class_name: "calendar", + children: [ + Widget.Calendar({ + hexpand: true, + hpack: "center", + }), + ], + }), + ], +}) diff --git a/packages/ags/bk/modules/datemenu/DateMenu.js b/packages/ags/bk/modules/datemenu/DateMenu.js new file mode 100644 index 0000000..12cdfe2 --- /dev/null +++ b/packages/ags/bk/modules/datemenu/DateMenu.js @@ -0,0 +1,33 @@ +import PopupWindow from "../PopupWindow.js" +import DateColumn from "./DateColumn.js" +import options from "../../options.js" + +const { bar, datemenu } = options +const pos = bar.position +const layout = `${bar.position}-${datemenu.position}` + +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 === "top" ? "slide_down" : "slide_up", + layout: layout, + child: Settings(), +}) + +export function setupDateMenu() { + App.addWindow(DateMenu()) + // layout.connect("changed", () => { + // App.removeWindow("datemenu") + // App.addWindow(DateMenu()) + // }) +} diff --git a/packages/ags/bk/modules/dock/Dock.js b/packages/ags/bk/modules/dock/Dock.js new file mode 100644 index 0000000..7e79fb1 --- /dev/null +++ b/packages/ags/bk/modules/dock/Dock.js @@ -0,0 +1,31 @@ +// import Gtk from 'gi://Gtk'; + +/** @param {string} windowName */ +const Padding = windowName => Widget.EventBox({ + class_name: 'padding', + hexpand: true, + vexpand: true, + setup: w => w.on('button-press-event', () => App.toggleWindow(windowName)), +}); + +const Dock = () => { + return Widget.Box({ + name: 'dock-box', + child: Widget.Label({ + label: "Yo Yo YO YO bfbhgjhdfvbfb", + }) + }) +} + + +export default () => Widget.Window({ + name: 'dock', + anchor: ['bottom'], + exclusivity: 'normal', + keymode: 'on-demand', + layer: 'background', + // popup: true, + // monitor: 0, + visible: true, + child: Dock(), +}) diff --git a/packages/ags/bk/modules/lock/Lockscreen.js b/packages/ags/bk/modules/lock/Lockscreen.js new file mode 100644 index 0000000..e9de717 --- /dev/null +++ b/packages/ags/bk/modules/lock/Lockscreen.js @@ -0,0 +1,156 @@ +import GLib from 'gi://GLib' + +const time = Widget.Label({ + name: 'lockscreen-time', + label: GLib.DateTime.new_now_local().format("%H:%M"), + setup: (self) => self.poll(1000, label => { + label.label = GLib.DateTime.new_now_local().format("%H:%M"); + }), + justification: 'left', + hexpand: true, + hpack: 'start', +}) + +const date = Widget.Label({ + name: 'lockscreen-date', + label: GLib.DateTime.new_now_local().format("%e, %B %A"), + setup: (self) => self.poll(60000, label => { + label.label = GLib.DateTime.new_now_local().format("%e, %B %A"); + }), + justification: 'left', +}) + +const network = await Service.import('network') +const battery = await Service.import('battery') + + +const Lockscreen = () => { + const WifiIndicator = () => Widget.Box({ + child: Widget.Icon({ + icon: network.wifi.bind('icon_name'), + }), + }) + + const WiredIndicator = () => Widget.Icon({ + icon: network.wired.bind('icon_name'), + }) + + const batPercent = Widget.Revealer({ + revealChild: false, + transitionDuration: 600, + transition: 'slide_left', + child: Widget.Label({ + name: "lockscreen-revealer", + label: battery.bind('percent').transform(p => "" + p + "%"), + }), + }) + + const batInfo = Widget.Box({ + "class-name": 'lockscreen-smolbox', + hexpand: false, + hpack: 'end', + children: [ + batPercent, + Widget.EventBox({ + child: Widget.Box({ + child: Widget.Label({ + label: battery.bind('percent').transform(p => ['', '', '', '', ''][Math.floor(p / 20)]), + class_name: 'lockscreen-battery', + hexpand: false, + setup: icon => icon.hook(battery, () => { + icon.toggleClassName('lockscreen-charging', battery.charging); + icon.toggleClassName('lockscreen-charged', battery.charged); + icon.toggleClassName('lockscreen-low', battery.percent < 30); + }), + }), + }), + onHover: () => { + batPercent.reveal_child = true; + print("revealed"); + }, + onHoverLost: () => { + batPercent.reveal_child = false; + } + }) + ] + }) + + const networkName = Widget.Revealer({ + revealChild: false, + transitionDuration: 600, + transition: 'slide_left', + child: Widget.Label({ + name: "lockscreen-revealer", + label: network.wifi.bind('ssid') + .as(ssid => ssid || 'Unknown'), + }), + }) + + const networkStatus = Widget.Box({ + class_name: "lockscreen-smolbox", + hexpand: false, + hpack: 'end', + children: [ + networkName, + Widget.EventBox({ + child: Widget.Stack({ + class_name: "lockscreen-network", + children: { + 'wifi': WifiIndicator(), + 'wired': WiredIndicator() + }, + shown: network.bind('primary').transform(p => p || 'wifi'), + }), + onHover: () => { + networkName.reveal_child = true; + }, + onHoverLost: () => { + networkName.reveal_child = false; + } + }) + ] + }) + + const boxRight = Widget.Box({ + homogeneous: false, + name: "lockscreen-boxRight", + vertical: true, + hexpand: false, + children: [networkStatus, batInfo], + vpack: 'end', + hpack: 'end', + }) + + const boxLeft = Widget.Box({ + homogeneous: false, + vertical: true, + children: [time, date], + vpack: 'end', + hpack: 'start', + }) + + return Widget.Box({ + children: [boxLeft, boxRight], + vertical: false, + }) +} + +function randomImage() { + const a = Utils.exec(`bash -c "ls ${App.configDir + '/modules/lock/images/'} | shuf -n 1"`); + const b = `background-image: url("${App.configDir + '/modules/lock/images/' + a}");`; + print(b); + return b; + +} + +export default () => Widget.Window({ + name: 'lockscreen', + css: randomImage(), + anchor: ['top', 'left', 'right', 'bottom'], + exclusivity: 'normal', + keymode: 'on-demand', + layer: 'top', + visible: false, + monitor: 0, + child: Lockscreen(), +}) diff --git a/packages/ags/bk/modules/lock/images/astronautart.png b/packages/ags/bk/modules/lock/images/astronautart.png new file mode 100644 index 0000000..5b0d5d7 Binary files /dev/null and b/packages/ags/bk/modules/lock/images/astronautart.png differ diff --git a/packages/ags/bk/modules/lock/images/cityevening.png b/packages/ags/bk/modules/lock/images/cityevening.png new file mode 100644 index 0000000..97de3b7 Binary files /dev/null and b/packages/ags/bk/modules/lock/images/cityevening.png differ diff --git a/packages/ags/bk/modules/lock/images/dark-sand.jpg b/packages/ags/bk/modules/lock/images/dark-sand.jpg new file mode 100644 index 0000000..da884e9 Binary files /dev/null and b/packages/ags/bk/modules/lock/images/dark-sand.jpg differ diff --git a/packages/ags/bk/modules/lock/images/something.png b/packages/ags/bk/modules/lock/images/something.png new file mode 100644 index 0000000..ae4c761 Binary files /dev/null and b/packages/ags/bk/modules/lock/images/something.png differ diff --git a/packages/ags/bk/modules/lock/images/space.png b/packages/ags/bk/modules/lock/images/space.png new file mode 100644 index 0000000..cb1d74f Binary files /dev/null and b/packages/ags/bk/modules/lock/images/space.png differ diff --git a/packages/ags/bk/modules/notifications/Notification.js b/packages/ags/bk/modules/notifications/Notification.js new file mode 100644 index 0000000..cca1822 --- /dev/null +++ b/packages/ags/bk/modules/notifications/Notification.js @@ -0,0 +1,139 @@ +import GLib from "gi://GLib" +import icons from "../../lib/icons.js" + +const time = (time, format = "%H:%M") => GLib.DateTime + .new_from_unix_local(time) + .format(format) + +const NotificationIcon = ({ app_entry, app_icon, image }) => { + 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) => { + 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", + hexpand: false, + vexpand: false, + 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/packages/ags/bk/modules/notifications/NotificationPopups.js b/packages/ags/bk/modules/notifications/NotificationPopups.js new file mode 100644 index 0000000..5a77308 --- /dev/null +++ b/packages/ags/bk/modules/notifications/NotificationPopups.js @@ -0,0 +1,95 @@ +import Notification from "./Notification.js" +import options from "../../options.js" + +const notifications = await Service.import("notifications") +const { transitionDuration } = options +const { position, blacklist } = options.notifications +const { timeout, idle } = Utils + +function Animated(id) { + const n = notifications.getNotification(id) + const widget = Notification(n) + + const inner = Widget.Revealer({ + css: "border: 1px solid magenta;", + transition: "slide_left", + transition_duration: transitionDuration, + child: widget, + }) + + const outer = Widget.Revealer({ + css: "border: 1px solid yellow;", + transition: "slide_down", + transition_duration: transitionDuration, + child: inner, + }) + + const box = Widget.Box({ + hpack: "end", + child: outer, + }) + + idle(() => { + outer.reveal_child = true + timeout(transitionDuration, () => { + inner.reveal_child = true + }) + }) + + return Object.assign(box, { + dismiss() { + inner.reveal_child = false + timeout(transitionDuration, () => { + outer.reveal_child = false + timeout(transitionDuration, () => { + box.destroy() + }) + }) + }, + }) +} + +function PopupList() { + const map = new Map + const box = Widget.Box({ + hpack: "end", + vertical: true, + css: `min-width: ${options.notifications.width}px;`, + }) + + function remove(_, id) { + map.get(id)?.dismiss() + map.delete(id) + } + + return box + .hook(notifications, (_, id) => { + if (id !== undefined) { + if (map.has(id)) + remove(null, id) + + if (blacklist.includes(notifications.getNotification(id).app_name)) + return + + 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) => Widget.Window({ + monitor, + name: `notifications${monitor}`, + anchor: position, + class_name: "notifications", + child: Widget.Box({ + css: "padding: 2px;", + child: PopupList(), + }), +}) diff --git a/packages/ags/bk/modules/osd/OSD.js b/packages/ags/bk/modules/osd/OSD.js new file mode 100644 index 0000000..66279f7 --- /dev/null +++ b/packages/ags/bk/modules/osd/OSD.js @@ -0,0 +1,111 @@ +import { icon } from "../../lib/utils.js" +import icons from "../../lib/icons.js" +import Progress from "./Progress.js" +import brightness from "../../services/brightness.js" +import options from "../../options.js" + +const audio = await Service.import("audio") +const { progress, microphone } = options.osd + +const DELAY = 2500 + +function OnScreenProgress(vertical) { + 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, icon) { + 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) => 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, + vpack: progress.pack.v, + child: OnScreenProgress(progress.vertical), + }), + Widget.Box({ + hpack: microphone.pack.h, + vpack: microphone.pack.v, + child: MicrophoneMute(), + }), + ), + }), +}) diff --git a/packages/ags/bk/modules/osd/Progress.js b/packages/ags/bk/modules/osd/Progress.js new file mode 100644 index 0000000..4e15b07 --- /dev/null +++ b/packages/ags/bk/modules/osd/Progress.js @@ -0,0 +1,66 @@ +import GLib from "gi://GLib?version=2.0" +import { range } from "../../lib/utils.js" +import options from "../../options.js" + +export default ({ + height = 18, + width = 180, + vertical = false, + child, +}) => { + 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 = [] + + return Object.assign(container, { + setValue(value) { + 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 / 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/packages/ags/bk/modules/overview/Overview.js b/packages/ags/bk/modules/overview/Overview.js new file mode 100644 index 0000000..823e939 --- /dev/null +++ b/packages/ags/bk/modules/overview/Overview.js @@ -0,0 +1,43 @@ +import PopupWindow from "../PopupWindow.js" +import Workspace from "./Workspace.js" +import options from "../../options.js" +import { range } from "../../lib/utils.js" + +const hyprland = await Service.import("hyprland") + +const Overview = (ws) => Widget.Box({ + class_name: "overview horizontal", + // spacing: 10, + 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) => { + if (id === undefined) + return + + w.children = w.children + .filter(ch => ch.attribute.id !== Number(id)) + }, "workspace-removed") + w.hook(hyprland, (w, id) => { + 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", + transition: "slide_down", + child: Overview(options.overview.workspaces), +}) diff --git a/packages/ags/bk/modules/overview/Window.js b/packages/ags/bk/modules/overview/Window.js new file mode 100644 index 0000000..1c0924b --- /dev/null +++ b/packages/ags/bk/modules/overview/Window.js @@ -0,0 +1,51 @@ +import { createSurfaceFromWidget, icon } from "../../lib/utils.js" +import Gdk from "gi://Gdk" +import Gtk from "gi://Gtk?version=3.0" +import options from "../../options.js" +import icons from "../../lib/icons.js" + +const monochrome = options.overview.monochromeIcon +const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)] +const scale = (size) => (options.overview.scale / 100) * size +const hyprland = await Service.import("hyprland") +const apps = await Service.import("applications") +const dispatch = (args) => hyprland.messageAsync(`dispatch ${args}`) + + +const Icon = (m, c) => { + const app = apps.list.find(app => app.match(c)) + if (!app) + return icons.fallback.executable + + return icon( + app.icon_name + (m ? "-symbolic" : ""), + icons.fallback.executable, + ) +} + +export default ({ address, size: [w, h], class: c, title }) => Widget.Button({ + class_name: "client", + attribute: { address }, + tooltip_text: `${title}`, + child: Widget.Icon({ + class_name: "overview-icon", + css: ` + min-width: ${scale(w)}px; + min-height: ${scale(h)}px; + `, + icon: Icon(monochrome, c), + }), + 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/packages/ags/bk/modules/overview/Workspace.js b/packages/ags/bk/modules/overview/Workspace.js new file mode 100644 index 0000000..27bff76 --- /dev/null +++ b/packages/ags/bk/modules/overview/Workspace.js @@ -0,0 +1,59 @@ +import Window from "./Window.js" +import Gdk from "gi://Gdk" +import Gtk from "gi://Gtk?version=3.0" +import options from "../../options.js" + +const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)] +const scale = (size) => (options.overview.scale / 100) * size +const hyprland = await Service.import("hyprland") + +const dispatch = (args) => hyprland.messageAsync(`dispatch ${args}`) + +const size = (id) => { + 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) => Widget.Box({ + attribute: { id }, + tooltipText: `${id}`, + class_name: "workspace", + vpack: "center", + css: ` + min-width: ${scale(size(id).w)}px; + min-height: ${scale(size(id).h)}px; + `, + setup: box => box.hook(hyprland, () => { + 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: Widget.Fixed().hook(hyprland, fixed => { + fixed.get_children().forEach(ch => ch.destroy()) + hyprland.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() + }, "notify::clients"), + }), +}) diff --git a/packages/ags/bk/modules/powermenu/Powermenu.js b/packages/ags/bk/modules/powermenu/Powermenu.js new file mode 100644 index 0000000..7d7a2b3 --- /dev/null +++ b/packages/ags/bk/modules/powermenu/Powermenu.js @@ -0,0 +1,144 @@ +// import Gtk from 'gi://Gtk'; +import { uptime } from '../../lib/variables.js'; +import powermenu from '../../services/powermenu.js'; +import PopupWindow from "../PopupWindow.js"; + +const options = [ + { + label: "lock", + icon: "" + }, + { + label: "sleep", + icon: "" + }, + { + label: "logout", + icon: "" + }, + { + label: "shutdown", + icon: "" + }, + { + label: "reboot", + icon: "" + } +]; + +function buttonCreator(option, flag = 'none') { + const but = Widget.Button({ + className: 'powermenu-buttonBox', + child: Widget.Box({ + child: Widget.Label({ + className: 'powermenu-buttonLabel', + label: option['icon'], + }), + }), + onClicked: () => powermenu.action(option['label']),//Utils.execAsync(option['cmd']), + }) + if (flag == 'first') { + but.toggleClassName('powermenu-buttonBoxFirst', true); + } else if (flag == 'last') { + but.toggleClassName('powermenu-buttonBoxLast', true); + } + + return but; +} + +const Powermenu = () => { + + let powerbutton = Widget.Box({ + hpack: 'start', + vpack: 'start', + className: 'powermenu-powerbutton', + child: Widget.Label({ + label: "", + hpack: 'start', + }), + }) + + const topBox = Widget.Box({ + vertical: false, + homogeneous: false, + name: "powermenu-topbox", + css: `background-image: url('${App.configDir + '/modules/powermenu/imag2.png'}'); background-position: center;`, + spacing: 0, + children: [ + Widget.EventBox({ + vexpand: false, + vpack: 'start', + child: powerbutton, + onPrimaryClick: () => { + App.toggleWindow('powermenu'); + } + }), + Widget.Box({ + hpack: 'start', + vpack: 'start', + className: 'powermenu-username', + child: Widget.Label({ + hpack: 'start', + label: `${Utils.exec('whoami')}` + }) + }) + ] + }) + + const midBox = Widget.Box({ + hpack: 'fill', + className: 'powermenu-midbox', + child: Widget.Label({ + label: uptime.bind().as(value => 'Uptime : ' + Math.floor(value / 60) + ' hours ' + Math.floor(value % 60) + ' minutes'), + setup: self => self.hook(uptime, () => { + self.label = uptime.value.toString(); + }) + }) + }) + + let bottomBox = Widget.Box({ + vertical: false, + homogeneous: false, + name: 'powermenu-bottombox', + spacing: 20, + children: [ + buttonCreator(options[0], 'first'), + buttonCreator(options[1]), + buttonCreator(options[2]), + buttonCreator(options[3]), + buttonCreator(options[4], 'last'), + ], + }) + + let motherBox = Widget.Box({ + homogeneous: false, + vertical: true, + name: 'powermenu-motherbox', + // hexpand: true, + // vexpand: false, + hpack: 'center', + vpack: 'center', + children: [topBox, midBox, bottomBox], + }) + + return motherBox; +} + +export default () => PopupWindow({ + name: 'powermenu', + transition: "slide_down", + child: Powermenu(), +}) + + +// export default () => Widget.Window({ +// name: 'powermenu', +// anchor: ['top', 'left', 'right', 'bottom'], +// exclusivity: 'normal', +// keymode: 'exclusive', +// layer: 'top', +// popup: true, +// // monitor: 0, +// visible: false, +// child: Powermenu(), +// }) diff --git a/packages/ags/bk/modules/powermenu/Verification.js b/packages/ags/bk/modules/powermenu/Verification.js new file mode 100644 index 0000000..e62acdd --- /dev/null +++ b/packages/ags/bk/modules/powermenu/Verification.js @@ -0,0 +1,70 @@ + +import PopupWindow from '../PopupWindow.js'; +import powermenu from '../../services/powermenu.js'; + +export default () => PopupWindow({ + name: "verification", + transition: "crossfade", + layout: 'center', + child: Widget.Box({ + class_name: "verification", + vertical: true, + homogeneous: false, + children: [ + Widget.Box({ + class_name: "verification-label", + vertical: true, + children: [ + Widget.Label({ + class_name: "desc", + label: powermenu.bind("title"), + }), + ], + }), + Widget.Box({ + vexpand: true, + vpack: "end", + spacing: 20, + homogeneous: false, + children: [ + Widget.Button({ + class_name: "verification-buttonBox verification-buttonBoxFirst", + child: Widget.Label({ + label: "No", + hpack: "center", + vpack: "center", + }), + on_clicked: () => App.toggleWindow("verification"), + setup: self => self.hook(App, (_, name, visible) => { + if (name === "verification" && visible) + self.grab_focus() + }), + }), + Widget.Button({ + class_name: "verification-buttonBox verification-buttonBoxLast", + child: Widget.Label({ + label: "Yes", + hpack: "center", + vpack: "center", + }), + on_clicked: () => Utils.exec(powermenu.cmd), + }), + ], + }), + ], + }), +}) + + +// export default () => Widget.Window({ +// name: 'verification', +// // transition: 'crossfade', +// anchor: ['top', 'left', 'right', 'bottom'], +// exclusivity: 'normal', +// keymode: 'exclusive', +// layer: 'top', +// popup: true, +// // monitor: 0, +// visible: false, +// child: Verification(), +// }) diff --git a/packages/ags/bk/modules/powermenu/imag2.png b/packages/ags/bk/modules/powermenu/imag2.png new file mode 100644 index 0000000..dc030f2 Binary files /dev/null and b/packages/ags/bk/modules/powermenu/imag2.png differ diff --git a/packages/ags/bk/modules/powermenu/image.png b/packages/ags/bk/modules/powermenu/image.png new file mode 100644 index 0000000..65d14f7 Binary files /dev/null and b/packages/ags/bk/modules/powermenu/image.png differ diff --git a/packages/ags/bk/modules/quicksettings/NotificationColumn.js b/packages/ags/bk/modules/quicksettings/NotificationColumn.js new file mode 100644 index 0000000..0812685 --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/NotificationColumn.js @@ -0,0 +1,117 @@ +import Notification from "../notifications/Notification.js" +import options from "../../options.js" +import icons from "../../lib/icons.js" + +const notifications = await Service.import("notifications") +const notifs = notifications.bind("notifications") + +const Animated = (n) => Widget.Revealer({ + transition_duration: options.transition, + transition: "slide_down", + child: Notification(n), + setup: self => Utils.timeout(options.transition, () => { + 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 = 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(_, id) { + const n = map.get(id) + if (n) { + n.reveal_child = false + Utils.timeout(options.transition, () => { + n.destroy() + map.delete(id) + }) + } + } + + return box + .hook(notifications, remove, "closed") + .hook(notifications, (_, id) => { + 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: `min-width: ${options.notifications.width}px`, + // vpack: "fill", + vexpand: true, + vertical: true, + children: [ + Header(), + Widget.Scrollable({ + vexpand: true, + // vpack: "fill", + hscroll: "never", + class_name: "notification-scrollable", + child: Widget.Box({ + class_name: "notification-list vertical", + vertical: true, + vexpand: true, + // vpack: "fill", + // vexpand: true, + children: [ + NotificationList(), + Placeholder(), + ], + }), + }), + ], +}) diff --git a/packages/ags/bk/modules/quicksettings/QuickSettings.js b/packages/ags/bk/modules/quicksettings/QuickSettings.js new file mode 100644 index 0000000..e55755f --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/QuickSettings.js @@ -0,0 +1,87 @@ +import { ProfileSelector, ProfileToggle } from "./widgets/AsusProfile.js" +// import { Header } from "./widgets/Header.js" +import { Volume, Microhone, SinkSelector, AppMixer } from "./widgets/Volume.js" +import { Brightness } from "./widgets/Brightness.js" +import { NetworkToggle, WifiSelection } from "./widgets/Network.js" +import { BluetoothToggle, BluetoothDevices } from "./widgets/Bluetooth.js" +import { DND } from "./widgets/DND.js" +// import { DarkModeToggle } from "./widgets/DarkMode.js" +import { MicMute } from "./widgets/MicMute.js" +import { Media } from "./widgets/Media.js" +import PopupWindow from "../PopupWindow.js" +import NotificationColumn from "./NotificationColumn.js" +import options from "../../options.js" + +const { bar, quicksettings } = options +const media = (await Service.import("mpris")).bind("players") +const layout = `${quicksettings.position}` + + +const Row = ( + toggles = [], + menus = [], +) => 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, + vexpand: true, + vpack: "fill", + class_name: "quicksettings vertical", + css: `min-width: ${quicksettings.width}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], + [ProfileSelector], + ), + Row([MicMute, DND]), + Widget.Box({ + visible: media.as(l => l.length > 0), + child: Media(), + }), + NotificationColumn(), + ], +}) + +const QuickSettings = () => PopupWindow({ + name: "quicksettings", + transition: "slide_left", //bar.position === "top" ? "slide_down" : "slide_up", + // anchor: ["right", "top"], + keymode: 'on-demand', + layout: layout, + child: Settings(), + vpack: "fill", + // hpack: "fill", + vexpand: true, + // hexpand: true, +}) + +export function setupQuickSettings() { + App.addWindow(QuickSettings()) +} diff --git a/packages/ags/bk/modules/quicksettings/ToggleButton.js b/packages/ags/bk/modules/quicksettings/ToggleButton.js new file mode 100644 index 0000000..43cb7c9 --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/ToggleButton.js @@ -0,0 +1,129 @@ +import icons from "../../lib/icons.js" + +export const opened = Variable("") +App.connect("window-toggled", (_, name, visible) => { + if (name === "quicksettings" && !visible) + Utils.timeout(500, () => opened.value = "") +}) + +export const Arrow = (name, activate) => { + 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() + }, + }) +} + +export const ArrowToggleButton = ({ + name, + icon, + label, + activate, + deactivate, + activateOnArrow = true, + connection: [service, condition], +}) => 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), + ], +}) + +export const Menu = ({ name, icon, title, content }) => 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, + }), + ], + }), +}) + +export const SimpleToggleButton = ({ + icon, + label, + toggle, + connection: [service, condition], +}) => 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/packages/ags/bk/modules/quicksettings/widgets/AsusProfile.js b/packages/ags/bk/modules/quicksettings/widgets/AsusProfile.js new file mode 100644 index 0000000..b79bc90 --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/widgets/AsusProfile.js @@ -0,0 +1,51 @@ +import { ArrowToggleButton, Menu } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" +import asusctl from "../../../services/asusctl.js" + +const profile = asusctl.bind("profile") + +export const ProfileToggle = () => ArrowToggleButton({ + name: "asusctl-profile", + icon: profile.as(p => icons.asusctl.profile[p]), + label: profile, + connection: [asusctl, () => asusctl.profile !== "Balanced"], + activate: () => asusctl.setProfile("Quiet"), + deactivate: () => asusctl.setProfile("Balanced"), + activateOnArrow: false, +}) + +export const ProfileSelector = () => Menu({ + name: "asusctl-profile", + icon: profile.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"), + ], + }), + }), + ], +}) diff --git a/packages/ags/bk/modules/quicksettings/widgets/Bluetooth.js b/packages/ags/bk/modules/quicksettings/widgets/Bluetooth.js new file mode 100644 index 0000000..44a0e8a --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/widgets/Bluetooth.js @@ -0,0 +1,62 @@ +import { Menu, ArrowToggleButton } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" +import { watch } from "../../../lib/experiments.js" + +const bluetooth = await Service.import("bluetooth") +const title = 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` +}) + +export const BluetoothToggle = () => ArrowToggleButton({ + name: "bluetooth", + icon: bluetooth.bind("enabled").as(p => icons.bluetooth[p ? "enabled" : "disabled"]), + label: title, + connection: [bluetooth, () => bluetooth.enabled], + deactivate: () => bluetooth.enabled = false, + activate: () => bluetooth.enabled = true, +}) + +const DeviceItem = (device) => 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/packages/ags/bk/modules/quicksettings/widgets/Brightness.js b/packages/ags/bk/modules/quicksettings/widgets/Brightness.js new file mode 100644 index 0000000..29bdf77 --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/widgets/Brightness.js @@ -0,0 +1,23 @@ +import icons from "../../../lib/icons.js" +import brightness from "../../../services/brightness.js" + +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/packages/ags/bk/modules/quicksettings/widgets/DND.js b/packages/ags/bk/modules/quicksettings/widgets/DND.js new file mode 100644 index 0000000..b5d86af --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/widgets/DND.js @@ -0,0 +1,12 @@ +import { SimpleToggleButton } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" + +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/packages/ags/bk/modules/quicksettings/widgets/DarkMode.js b/packages/ags/bk/modules/quicksettings/widgets/DarkMode.js new file mode 100644 index 0000000..4a9a9ad --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/widgets/DarkMode.js @@ -0,0 +1,10 @@ +import { SimpleToggleButton } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" + +let scheme = "dark" + +export const DarkModeToggle = () => SimpleToggleButton({ + icon: icons.color[scheme], + label: scheme === "dark" ? "Dark" : "Light", + toggle: () => scheme = scheme === "dark" ? "light" : "dark", +}) diff --git a/packages/ags/bk/modules/quicksettings/widgets/Header.js b/packages/ags/bk/modules/quicksettings/widgets/Header.js new file mode 100644 index 0000000..63a4327 --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/widgets/Header.js @@ -0,0 +1,60 @@ +import icons from "../../../lib/icons.js" +import { uptime } from "../../../lib/variables.js" +import options from "../../../options.js" +import powermenu from "../../../services/powermenu.js" + +const battery = await Service.import("battery") +const { image, size } = options.quicksettings.avatar + +function up(up) { + 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: ` + min-width: ${size}px; + min-height: ${size}px; + background-image: url('${image}'); + background-size: cover; + `, +}) + +const SysButton = (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([ + 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.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/packages/ags/bk/modules/quicksettings/widgets/Media.js b/packages/ags/bk/modules/quicksettings/widgets/Media.js new file mode 100644 index 0000000..be90edd --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/widgets/Media.js @@ -0,0 +1,148 @@ +import icons from "../../../lib/icons.js" +import options from "../../../options.js" +import { icon } from "../../../lib/utils.js" + +const mpris = await Service.import("mpris") +const players = mpris.bind("players") +const { media } = options.quicksettings + +function lengthStr(length) { + const min = Math.floor(length / 60) + const sec = Math.floor(length % 60) + const sec0 = sec < 10 ? "0" : "" + return `${min}:${sec0}${sec}` +} + +const Player = (player) => { + const cover = Widget.Box({ + class_name: "cover", + vpack: "start", + css: player.bind("cover_path").as(path => ` + min-width: ${media.coverSize}px; + min-height: ${media.coverSize}px; + background-image: url('${path}'); + `), + }) + + 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 = (_, time) => { + 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: player.bind("entry").as(e => { + const name = `${e}${media.monochromeIcon ? "-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/packages/ags/bk/modules/quicksettings/widgets/MicMute.js b/packages/ags/bk/modules/quicksettings/widgets/MicMute.js new file mode 100644 index 0000000..61be3ce --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/widgets/MicMute.js @@ -0,0 +1,26 @@ +import { SimpleToggleButton } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" +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" + +// TODO: Variable watch option +const ico = Variable(icon()) +microphone.connect("changed", () => ico.value = icon()) + +// TODO: Variable watch option +const lbl = Variable(label()) +microphone.connect("changed", () => lbl.value = label()) + +export const MicMute = () => SimpleToggleButton({ + icon: ico.bind(), + label: lbl.bind(), + toggle: () => microphone.is_muted = !microphone.is_muted, + connection: [microphone, () => microphone?.is_muted || false], +}) diff --git a/packages/ags/bk/modules/quicksettings/widgets/Network.js b/packages/ags/bk/modules/quicksettings/widgets/Network.js new file mode 100644 index 0000000..e94d245 --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/widgets/Network.js @@ -0,0 +1,61 @@ +import { Menu, ArrowToggleButton } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" +import { dependencies, sh } from "../../../lib/utils.js" +import options from "../../../options.js" +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), + child: Widget.Box({ + children: [ + Widget.Icon(icons.ui.settings), + Widget.Label("Network"), + ], + }), + }), + ], +}) diff --git a/packages/ags/bk/modules/quicksettings/widgets/Volume.js b/packages/ags/bk/modules/quicksettings/widgets/Volume.js new file mode 100644 index 0000000..57da0b4 --- /dev/null +++ b/packages/ags/bk/modules/quicksettings/widgets/Volume.js @@ -0,0 +1,141 @@ +import { Arrow, Menu } from "../ToggleButton.js" +import { dependencies, icon, sh } from "../../../lib/utils.js" +import icons from "../../../lib/icons.js" +const audio = await Service.import("audio") + +const VolumeIndicator = (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.mic.high)), + tooltipText: audio[type].bind("volume") + .as(vol => `Volume: ${Math.floor(vol * 100)}%`), + }), +}) + +const VolumeSlider = (type = "speaker") => Widget.Slider({ + hexpand: true, + draw_value: false, + on_change: ({ value, dragging }) => dragging && (audio[type].volume = value), + value: audio[type].bind("volume"), +}) + +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: [ + VolumeIndicator("microphone"), + VolumeSlider("microphone"), + ], +}) + +const MixerItem = (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) => 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/packages/ags/bk/options.js b/packages/ags/bk/options.js new file mode 100644 index 0000000..a3eb942 --- /dev/null +++ b/packages/ags/bk/options.js @@ -0,0 +1,139 @@ + +const options = { + recompileSass: true, + monitorCSS: true, + theme: { + PopupTransitionDuration: 400, + PopupCloseDuration: 300, + }, + powermenu: { + sleep: 'systemctl suspend', + lock: 'waylock', + logout: 'hyprctl dispatch exit', + shutdown: 'shutdown now', + reboot: 'reboot', + }, + overview: { + scale: 9, + workspaces: 8, + monochromeIcon: false, + }, + notifications: { + position: ["top", "right"], + blacklist: ["Spotify"], + width: 440, + }, + transitionDuration: 200, + transition: 200, + + bar: { + flatButtons: true, + position: "bottom", + corners: true, + layout: { + start: [ + "launcher", + "workspaces", + "taskbar", + "expander", + // "messages", + ], + center: [ + "date", + ], + end: [ + "media", + "expander", + "systray", + // "colorpicker", + // "screenrecord", + "system", + "battery", + "powermenu", + ], + }, + launcher: { + icon: { + colored: true, + icon: "system-search-symbolic", + }, + label: { + colored: false, + label: " Rofi", // Text that appears with the search icon + }, + action: () => Utils.execAsync('bash -c "pkill rofi || rofi -show drun"'), + }, + date: { + format: "%H:%M - %A %e.", + action: () => App.toggleWindow("datemenu"), + }, + battery: { + bar: "regular", + charging: "#00D787", + percentage: true, + blocks: 10, + width: 70, + low: 30, + }, + workspaces: { + workspaces: 7, + }, + taskbar: { + monochrome: false, + exclusive: false, + }, + messages: { + action: () => App.toggleWindow("datemenu"), + }, + systray: { + ignore: [ + "KDE Connect Indicator", + "spotify-client", + ], + }, + media: { + monochrome: false, + preferred: "spotify", + direction: "right", + length: 40, + }, + powermenu: { + monochrome: false, + action: () => App.toggleWindow("powermenu"), + }, + }, + osd: { + progress: { + vertical: false, + pack: { + h: "center", + v: "end", + }, + }, + microphone: { + pack: { + h: "center", + v: "center", + }, + }, + }, + datemenu: { + position: "center", + }, + + quicksettings: { + avatar: { + image: `/var/lib/AccountsService/icons/${Utils.USER}`, + size: 70, + }, + width: 380, + position: "right", + networkSettings: "gtk-launch gnome-control-center", + media: { + monochromeIcon: false, + coverSize: 100, + }, + }, +}; + +export default options; diff --git a/packages/ags/bk/scss/_bar.scss b/packages/ags/bk/scss/_bar.scss new file mode 100644 index 0000000..3e6b179 --- /dev/null +++ b/packages/ags/bk/scss/_bar.scss @@ -0,0 +1,337 @@ +$radius: 11px; +$bar-battery-blocks: 10; +$button-radius: 10px; +$bg: #171717; +$primary-bg: $blue; +$primary-fg: $mantle; +$charging-bg: $green; +$error-bg: $red; + +.bar { + background-color: $crust; + + .panel-button { + all: unset; + color: $text; + + >* { + border-radius: 10px; + transition: 200ms; + background-color: transparent; + box-shadow: none; + } + + &:focus>*, + &.focused>* { + box-shadow: inset 0 0 0 1px $blue; + background-color: transparentize($subtext1, 94px *.9 / 100); + color: lighten($subtext1, 8%); + } + + &:hover>* { + box-shadow: inset 0 0 0 1px #080808; + background-color: transparentize($subtext1, 94px *.9 / 100); + color: lighten($subtext1, 8%); + } + + &:active, + &.active, + &.on, + &:checked { + >* { + box-shadow: inset 0 0 0 1px #080808; + background-image: linear-gradient(to right, $mantle , darken($mantle, 4%)); + background-color: $blue; + color: $mantle; + } + + &:hover>* { + box-shadow: inset 0 0 0 1px $blue, + inset 0 0 0 99px transparentize($subtext1, 94px *.9 / 100); + } + } + + >* { + border-radius: 10px; + margin: 4px; + padding: 5px 8px; + font-size: 20px; + } + + label, + image { + font-weight: bold; + } + + + &:not(.flat) { + // all: unset; + color: $text; + + >* { + border-radius: 10px; + transition: 200ms; + } + + &:focus>*, + &.focused>* { + box-shadow: inset 0 0 0 1px $blue; + background-color: transparentize($subtext1, 94px *.9 / 100); + color: lighten($subtext1, 8%); + } + + &:hover>* { + box-shadow: inset 0 0 0 1px #080808; + background-color: transparentize($subtext1, 94px *.9 / 100); + color: lighten($subtext1, 8%); + } + + &:active, + &.active, + &.on, + &:checked { + >* { + box-shadow: inset 0 0 0 1px #080808; + background-image: linear-gradient(to right, $mantle , darken($mantle, 4%)); + background-color: $blue; + color: $mantle; + } + + &:hover>* { + box-shadow: inset 0 0 0 1px $blue, + inset 0 0 0 99px transparentize($subtext1, 94px *.9 / 100); + } + } + } + } + + .launcher { + .colored { + color: transparentize($blue, 0.2); + } + + &:hover .colored { + color: $blue; + } + + &:active .colored, + &.active .colored { + color: $blue; + } + } + + .workspaces { + label { + font-size: 0; + min-width: 5pt; + min-height: 5pt; + border-radius: $radius*.6; + box-shadow: inset 0 0 0 1px transparent; + margin: 0 7px*.5; + transition: 100ms* .5; + background-color: transparentize($text, .8); + + &.occupied { + background-color: transparentize($text, .2); + min-width: 7pt; + min-height: 7pt; + // box-shadow: inset 0 0 0 1px $blue; + // box-shadow: inset 0 0 0 1px transparent; + } + + &.active { + // background-color: $primary-bg; + // box-shadow: inset 0 0 0 1px transparent; + box-shadow: inset 0 0 0 1px $blue; + background-image: linear-gradient(to right, $mantle , darken($mantle, 4%)); + min-width: 20pt; + min-height: 12pt; + } + } + + &.active, + &:active { + label { + background-color: transparentize($blue, .3); + + &.occupied { + background-color: transparentize($blue, .15); + } + + &.active { + background-color: $blue; + } + } + } + } + + .media label { + margin: 0 (12px * .5) + } + + .taskbar .indicator.active { + background-color: $blue; + border-radius: $radius; + min-height: 4pt; + min-width: 6pt; + margin: 2pt; + } + + .powermenu.colored, + .recorder { + image { + color: transparentize($red, 0.3); + } + + &:hover image { + color: transparentize($red, 0.15); + } + + &:active image { + color: $mantle; + } + } + + .quicksettings>box>box { + &.horizontal>* { + margin: 0 calc(12px * 0.3 / 2); + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: calc(12px * 0.3 / 2) 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + } + + .quicksettings:not(.active):not(:active) { + .bluetooth { + color: $blue; + + label { + font-size: 13px * .7; + color: $text; + text-shadow: 2pt 2pt 2pt rgba(0, 0, 0, .6); + } + } + } + + .battery-bar { + >* { + padding: 0; + min-height: 10px; + } + + &.bar-hidden>box { + padding: 0 12px* .5; + + image { + margin: 0; + } + } + + levelbar * { + all: unset; + transition: 200ms; + } + + .whole { + image { + -gtk-icon-shadow: 2pt 2pt 2pt rgba(0, 0, 0, .6); + } + + label { + text-shadow: 2pt 2pt 2pt rgba(0, 0, 0, .6); + } + } + + .regular image { + margin-left: 12px * .5; + } + + trough { + transition: 200ms; + border-radius: $radius; + color: $mantle; + background-color: $base; + // border: 1px solid $surface0; + min-height: 5px; + min-width: 140px; + padding: 0px; + margin: 7px; + } + + .regular trough { + margin-right: 5px; + } + + 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: $primary-bg + } + + &.low block:nth-child(#{$i}).filled { + background-color: $error-bg + } + + &.charging block:nth-child(#{$i}).filled { + background-color: $charging-bg + } + + &:active .regular block:nth-child(#{$i}).filled { + background-color: $primary-fg + } + } + + &.low image { + color: $error-bg + } + + &.charging image { + color: $charging-bg + } + + &:active image { + color: $primary-fg + } + } +} diff --git a/packages/ags/bk/scss/_datemenu.scss b/packages/ags/bk/scss/_datemenu.scss new file mode 100644 index 0000000..e95c11c --- /dev/null +++ b/packages/ags/bk/scss/_datemenu.scss @@ -0,0 +1,340 @@ +@import './notifications'; + +@mixin calendar { + transition: 200ms; + border-radius: 18px; + color: $text; + background-color: $mantle; + border: 2px solid $blue; + padding: 8px 8px 0px; + + calendar { + all: unset; + + &.button { + all: unset; + transition: 200ms; + border-radius: 6px; + color: $text; + background-color: transparent; + background-image: none; + box-shadow: none; + + &:focus { + box-shadow: inset 0 0 0 2px #0e0e0e; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:hover { + box-shadow: inset 0 0 0 2px $blue; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:active, + &.on, + &.active, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + } + + &:selected { + box-shadow: inset 0 -8px 0 0 transparentize($green, 0.5), + inset 0 0 0 1px $green; + border-radius: 8px; + color: $blue; + } + + &.header { + background-color: transparent; + border: none; + color: transparentize($text, 0.5); + } + + &.highlight { + background-color: transparent; + color: transparentize($blue, 0.0); + } + + &:indeterminate { + color: transparentize($text, 0.8); + } + + font-size: 1.4em; + padding: .2em; + } +} + +window#datemenu .datemenu { + box-shadow: 0 0 5px 0 #0e0e0e; + + margin: 9px; + border: 2px solid $blue; + background-color: $base; + color: $text; + border-radius: 26px; + padding: 8px; + + .notifications { + .header { + margin-bottom: 30px; + margin-right: 30px; + + >label { + margin-left: 15px; + } + + button { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + + background-color: #FFFFFF; + box-shadow: inset 0 0 0 2px $blue; + + &:focus { + box-shadow: inset 0 0 0 2px #0e0e0e; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:hover { + box-shadow: inset 0 0 0 2px $blue; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:active, + &.on, + &.active, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($mantle, 0.7); + } + + padding: 10px; + } + } + + .notification-scrollable { + + scrollbar, + scrollbar * { + all: unset; + } + + scrollbar.vertical { + transition: 200ms; + background-color: transparentize($base, 0.7); + + &:hover { + background-color: transparentize($base, 0.3); + + slider { + background-color: transparentize($text, 0.3); + min-width: .6em; + } + } + } + + + scrollbar.vertical slider { + background-color: transparentize($text, 0.5); + border-radius: 10px; + min-width: .4em; + min-height: 2em; + transition: 200ms; + } + } + + .notification-list { + margin-right: 10px; + } + + .notification { + &.critical { + box-shadow: inset 0 0 .5em 0 $red; + } + + &:hover button.close-button { + // @include button-hover; + background-color: transparentize($red, .5); + } + + .content { + .title { + margin-top: 5px; + margin-right: 10px; + color: $text; + font-size: 1.2em; + } + + .time { + color: transparentize($text, .2); + margin: 5px 10px; + } + + .description { + font-size: .9em; + color: transparentize($text, .2); + } + + .icon { + border-radius: 10px; + margin-right: 10px; + + &.img { + border: 2px solid $green; + } + } + } + + box.actions { + // @include spacing(0.5); + margin-top: 10px; + + button { + // @include button; + background-color: $surface0; + border: 2px solid $surface0; + border-radius: 10px; + font-size: 1.2em; + margin: 0px 10px 10px 0px; + padding: 10px; + + &:first-child { + margin: 0px 10px 10px 10px; + } + + &:hover { + background-color: $surface2; + border: 2px solid $rosewater; + } + } + } + + button.close-button { + // @include button($flat: true); + margin: 0px 0px 0px 0px; + border-radius: 10px; + min-width: 30px; + min-height: 30px; + background-color: rgba(0, 0, 0, 0); //$crust; + padding: 0px; + + &:hover { + background-color: transparentize($red, .2); + } + + &:active { + background-image: none; + background-color: $red; + } + } + + transition: 200ms; + color: $text; + margin-bottom: 10px; + border-radius: 15px; + background-color: $base; + font-family: "Iosevka"; + padding: 5px; + border: 2px solid $rosewater; + box-shadow: rgba(0, 0, 0, 0.4) 5px 5px 5px; + } + + .placeholder { + image { + font-size: 7em; + } + + label { + font-size: 1.2em; + } + } + } + + + separator { + background-color: $mantle; + min-width: 3px; + border-radius: 100px; + margin-right: 10px; + } + + .datemenu { + &.horizontal>* { + margin: 0 10px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 10px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + } + + .clock-box { + padding: 10px; + + .clock { + font-size: 6em; + } + + .uptime { + font-family: "Iosevka"; + font-size: 18px; + color: transparentize($text, 0.2); + } + } + + .calendar { + font-family: "Iosevka"; + font-size: 18px; + @include calendar; + } +} diff --git a/packages/ags/bk/scss/_dock.scss b/packages/ags/bk/scss/_dock.scss new file mode 100644 index 0000000..a66f84f --- /dev/null +++ b/packages/ags/bk/scss/_dock.scss @@ -0,0 +1,9 @@ +#dock * { + font-family: 'Iosevka'; +} + +#dock-box { + min-height: 10px; + min-width: 100px; + background-color: $base; +} diff --git a/packages/ags/bk/scss/_globals.scss b/packages/ags/bk/scss/_globals.scss new file mode 100644 index 0000000..8bc4ef1 --- /dev/null +++ b/packages/ags/bk/scss/_globals.scss @@ -0,0 +1 @@ +// Trying yo put some global variables diff --git a/packages/ags/bk/scss/_lockscreen.scss b/packages/ags/bk/scss/_lockscreen.scss new file mode 100644 index 0000000..4946be1 --- /dev/null +++ b/packages/ags/bk/scss/_lockscreen.scss @@ -0,0 +1,76 @@ +#lockscreen * { + // all:unset; + font-family: "SF Pro Display"; +} + +/* #lockscreen { */ +/* background-image: url("/home/hooman/gitjargan/wallpapers/0012.jpg"); */ +/* } */ + +#lockscreen-time { + color: #b8bcc2; + font-size: 12.5rem; + margin-bottom: 2.5rem; +} + +#lockscreen-date { + color: #b8bcc2; + font-size: 3.75rem; + margin-bottom: 6rem; +} + +#lockscreen-time, #lockscreen-date { + margin-left: 7.8125rem; +} + +.lockscreen-smolbox { + background-color: #172030; + border-radius: 0.5rem; + min-height: 4.375rem; + min-width: 4.375rem; + margin-right: 5rem; + margin-bottom: 2.5rem; + font-size: 2rem; + + label { + min-height: 4.375rem; + min-width: 4.375rem; + color: #b8bcc2; + } + + &:last-child { + margin-bottom: 5rem; + } +} + +#lockscreen-revealer { + padding-left: 1rem; +} + +.lockscreen-charged { + color: #6b7cdb; +} + +.lockscreen-low { + color: #c45e62; +} + +.lockscreen-charging { + color: #62bf81; +} +.lockscreen-battery { + padding-right: 0; + margin-left: -0.5625rem; + margin-bottom: -0.4375rem; + margin-right: 0.5625rem; + padding-top: 0; +} + +.lockscreen-network { + padding-left: 1.25rem; + margin-right: 1.125rem; +} + +#lockscreen-boxRight { + min-width: 4.375rem; +} diff --git a/packages/ags/bk/scss/_mocha.scss b/packages/ags/bk/scss/_mocha.scss new file mode 100644 index 0000000..728949d --- /dev/null +++ b/packages/ags/bk/scss/_mocha.scss @@ -0,0 +1,26 @@ +$rosewater: #f5e0dc; +$flamingo: #f2cdcd; +$pink: #f5c2e7; +$mauve: #cba6f7; +$red: #f38ba8; +$maroon: #eba0ac; +$peach: #fab387; +$yellow: #f9e2af; +$green: #a6e3a1; +$teal: #94e2d5; +$sky: #89dceb; +$sapphire: #74c7ec; +$blue: #89b4fa; +$lavender: #b4befe; +$text: #cdd6f4; +$subtext1: #bac2de; +$subtext0: #a6adc8; +$overlay2: #9399b2; +$overlay1: #7f849c; +$overlay0: #6c7086; +$surface2: #585b70; +$surface1: #45475a; +$surface0: #313244; +$base: #1e1e2e; +$mantle: #181825; +$crust: #11111b; diff --git a/packages/ags/bk/scss/_notifications.scss b/packages/ags/bk/scss/_notifications.scss new file mode 100644 index 0000000..31e0889 --- /dev/null +++ b/packages/ags/bk/scss/_notifications.scss @@ -0,0 +1,96 @@ +window.notifications { + .notification { + border-radius: 15px; + background-color: $base; + margin: 25px 25px 20px 0px; + font-family: "Iosevka"; + padding: 5px; + // border: 2px solid $rosewater; + box-shadow: rgba(0, 0, 0, 0.4) 5px 5px 5px; + + &.critical { + box-shadow: inset 0 0 .5em 0 $red; + } + + &:hover button.close-button { + // @include button-hover; + background-color: transparentize($red, .5); + } + + .content { + .title { + margin-top: 5px; + margin-right: 10px; + color: $text; + font-size: 1.2em; + } + + .time { + color: transparentize($text, .2); + margin: 5px 10px; + } + + .description { + font-size: .9em; + color: transparentize($text, .2); + } + + .icon { + border-radius: 10px; + margin-right: 10px; + + &.img { + border: 2px solid $green; + } + } + } + + box.actions { + // @include spacing(0.5); + margin-top: 10px; + + button { + // @include button; + background-color: $surface0; + border: 2px solid $surface0; + border-radius: 10px; + font-size: 1.2em; + margin: 0px 10px 10px 0px; + padding: 10px; + + &:first-child { + margin: 0px 10px 10px 10px; + } + + &:hover { + background-color: $surface2; + border: 2px solid $rosewater; + } + } + } + + button.close-button { + // @include button($flat: true); + margin: 0px 0px 0px 0px; + border-radius: 10px; + min-width: 30px; + min-height: 30px; + background-color: rgba(0, 0, 0, 0); //$crust; + padding: 0px; + + &:hover { + background-color: transparentize($red, .5); + } + + &:active { + background-image: none; + background-color: $red; + } + } + + + .description { + min-width: 350px; + } + } +} diff --git a/packages/ags/bk/scss/_osd.scss b/packages/ags/bk/scss/_osd.scss new file mode 100644 index 0000000..1a75a8f --- /dev/null +++ b/packages/ags/bk/scss/_osd.scss @@ -0,0 +1,35 @@ +$shadow-color: #0e0e0e; + +window.indicator { + .progress { + box-shadow: 0 0 5px 0 $shadow-color; + margin: 15px; + border: 2px solid $blue; + background-color: $base; + color: $text; + padding: 5px; + border-radius: 15px; + + .fill { + border-radius: 10px; + background-color: $blue; + color: $crust; + + image { + -gtk-icon-transform: scale(0.7); + } + } + } + + .microphone { + box-shadow: 0 0 5px 0 $shadow-color; + margin: 15px; + border: 2px solid $blue; + background-color: $base; + color: $text; + padding: 5px; + border-radius: 15px; + font-size: 58px; + color: transparentize($crust, .1) + } +} diff --git a/packages/ags/bk/scss/_overview.scss b/packages/ags/bk/scss/_overview.scss new file mode 100644 index 0000000..a01ea7b --- /dev/null +++ b/packages/ags/bk/scss/_overview.scss @@ -0,0 +1,76 @@ +window#overview .overview { + background-color: $crust; + border: 2px solid $overlay0; + border-radius: 35px; + padding: 15px; + margin: 30px; + box-shadow: rgba(0, 0, 0, 0.6) 0 0 10px 10px; + + + &.horizontal>* { + margin: 0 10px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 10px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + .workspace { + + &.active>widget { + border-color: $blue; + } + + >widget { + border-radius: 20px; + background-color: $base; + margin: 10px; + border: 2px solid rgba(0, 0, 0, 0); + + &:hover { + background-color: $surface1; + } + + &:drop(active) { + background-color: $base; + } + } + + .client { + background-color: rgba(0, 0, 0, .3); + border: 2px solid $surface0; + margin: 10px; + border-radius: 10px; + padding: 0px; + + &:active, + &.on, + &.active, + &:checked, + &:focus { + border: 2px solid $blue; + background-color: rgba(0, 0, 0, 0.5); + } + + &:hover { + background-color: rgba(0, 0, 0, 0.5); + } + } + } +} diff --git a/packages/ags/bk/scss/_powermenu.scss b/packages/ags/bk/scss/_powermenu.scss new file mode 100644 index 0000000..99b1bfb --- /dev/null +++ b/packages/ags/bk/scss/_powermenu.scss @@ -0,0 +1,160 @@ +#powermenu { + /* opacity: 0; */ + background-color: rgba(0, 0, 0, 0); +} + +window#powermenu, +window#verification { + // the fraction has to be more than hyprland ignorealpha + background-color: rgba(0, 0, 0, .4); +} + +#powermenu-motherbox, +.verification { + background-color: $crust; + // margin: 162px 288px; + border-radius: 25px; + opacity: 1; + // box-shadow: rgba(0,0,0,0.4) + // 10px 10px; + margin: 30px; + box-shadow: rgba(0, 0, 0, 0.6) 0 0 10px 20px; + border: 2px solid $overlay0; +} + +.verification { + min-height: 158px; + min-width: 358px; +} + + +.powermenu-powerbutton { + background-color: $red; + color: #212228; + border-radius: 200px; + min-height: 48px; + min-width: 48px; + font-size: 20px; + font-family: "Iosevka"; + + &:hover { + background-color: $green; + font-size: 100px; + } + + label { + padding-left: 15px; + padding-top: 0px; + } +} + +.powermenu-username { + background-color: $green; + color: #212228; + border-radius: 200px; + min-height: 48px; + font-size: 20px; + font-family: "Iosevka"; + + label { + padding-left: 24px; + padding-right: 24px; + } +} + +.powermenu-powerbutton, +.powermenu-username { + margin: 10px 0px 0px 10px; +} + + +.powermenu-midbox { + background-color: $base; + color: #FFFFFF; + border-radius: 10px; + margin: 15px 25px; + padding: 10px 25px; + font-size: 20px; + font-family: "Iosevka"; +} + +.verification-label { + background-color: $base; + color: #FFFFFF; + border-radius: 10px; + margin: 25px 25px; + padding: 10px 25px; + font-size: 30px; + font-family: "Iosevka"; +} + +#powermenu-topbox { + margin: 25px 25px 0px 25px; + min-height: 388px; + border-radius: 10px; +} + +.powermenu-buttonLabel { + font-family: "JetBrainsMono Nerd Font"; +} + +.powermenu-buttonBox { + background-color: $base; + padding: 0px; + color: #EEEEEF; + min-height: 242px; + min-width: 242px; + border-radius: 10px; + margin: 0px 0px 25px 0px; + font-size: 100px; + border: 2px solid rgba(0, 0, 0, 0); + + label { + margin-left: 85px; + } + + &:focus { + background-color: $blue; + color: #1E2127; + } + + &:hover { + border: 2px solid $lavender; + background-color: $surface0; + } +} + + +.verification-buttonBox { + background-color: $base; + padding: 0px; + color: #EEEEEF; + min-height: 60px; + min-width: 140px; + border-radius: 10px; + margin: 0px 0px 25px 0px; + font-size: 35px; + font-family: "Iosevka"; + border: 2px solid rgba(0, 0, 0, 0); + + + &:focus { + background-color: $blue; + color: #1E2127; + } + + &:hover { + border: 2px solid $lavender; + background-color: $surface0; + } +} + +.powermenu-buttonBoxFirst, +.verification-buttonBoxFirst { + margin-left: 25px; +} + +.powermenu-buttonBoxLast, +.verification-buttonBoxLast { + margin-right: 25px; +} diff --git a/packages/ags/bk/scss/_quicksettings.scss b/packages/ags/bk/scss/_quicksettings.scss new file mode 100644 index 0000000..45610b7 --- /dev/null +++ b/packages/ags/bk/scss/_quicksettings.scss @@ -0,0 +1,1075 @@ +window#quicksettings .quicksettings { + font-family: "Iosevka"; + margin: 0px 0px 43px 20px; + // border-left: 2px solid $blue; + // border-top: 2px solid $blue; + // border-bottom: 2px solid $blue; + background-color: $base; + color: $text; + border-radius: 25px 0px 0px 25px; + padding: 10px; + // min-height: 1009px; + box-shadow: + rgba(0, 0, 0, 0.4) -8px 4px 10px; + + &.horizontal>* { + margin: 0 5px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 5px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + .avatar { + border-radius: 5px; + transition: 200ms; + color: $text; + background-color: $mantle; + border: 2px solid $rosewater; + } + + .header { + &.horizontal>* { + margin: 0 2.5px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 2.5px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + color: transparentize($text, .15); + + // button { + // all: unset; + // transition: 200ms; + // border-radius: 10px; + // color: $text; + // + // >* { + // border-radius: 50px; + // transition: 200ms; + // background-color: $crust; + // box-shadow: inset 0 0 0 5px $green; + // } + // + // &:focus>*, + // &.focused>* { + // box-shadow: inset 0 0 0 3px #0e0e0e; + // background-color: $blue; + // color: $mantle; + // } + // + // &:hover>* { + // box-shadow: inset 0 0 0 3px $blue; + // background-color: $blue; + // color: $mantle; + // } + // + // &:active, + // &.active, + // &.on, + // &:checked { + // >* { + // box-shadow: inset 0 0 0 2px $blue; + // background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + // background-color: $blue; + // color: $base; + // } + // + // &:hover>* { + // box-shadow: inset 0 0 0 2px $blue, + // inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + // } + // } + // + // &:disabled { + // box-shadow: none; + // background-color: transparent; + // color: transparentize($blue, 0.7); + // } + // + // padding: 20px; + // + // image { + // font-size: 1.4em; + // } + // } + } + + .sliders-box { + border-radius: 15px; + transition: 200ms; + color: $text; + background-color: $surface0; + // border: 2px solid $rosewater; + padding: 5px; + + button { + all: unset; + // border: 2px solid $text; + transition: 200ms; + border-radius: 5px; + margin: 5px; + color: $text; + font-size: 24px; + background-color: transparent; + background-image: none; + box-shadow: none; + padding: 3px; + + &:focus, + &.focused { + // box-shadow: inset 0 0 0 3px #0e0e0e; + background-color: darken($surface0, 10%); + color: $text; + } + + &:hover { + // box-shadow: inset 0 0 0 3px $text; + background-color: darken($surface0, 10%); + color: $text; + } + + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + } + + .volume button.arrow:last-child { + margin-left: 10px; + } + + .volume, + .brightness { + padding: 5px; + } + + scale { + all: unset; + + * { + all: unset + } + + trough { + transition: 200ms; + border-radius: 10px; + // border: 2px solid $green; + background-color: $base; + min-height: 5px; + min-width: 5px; + + highlight, + progress { + border-radius: 20px; + background-image: linear-gradient(to right, darken($blue, 20%), darken($blue, 30%)); + min-height: 5px; + min-width: 5px; + } + } + + slider { + box-shadow: none; + background-color: transparent; + border: 1px solid transparent; + transition: 200ms; + border-radius: 10px; + min-height: 16px; + min-width: 16px; + margin: -4px; + } + + &:hover { + trough { + background-color: $mantle; + } + + slider { + background-color: $text; + border-color: $blue; + // box-shadow: 0 0 3px 0 #0e0e0e; + } + } + + &:disabled { + + highlight, + progress { + background-color: transparentize($text, 0.4); + background-image: none; + } + } + + trough:focus { + background-color: $crust; + // box-shadow: inset 0 0 0 4px $mantle; + + slider { + background-color: darken($text, 10%); + // box-shadow: inset 0 0 0 4px $mantle; + } + } + + margin: 0 10px; + } + } + + .row { + &.horizontal>* { + margin: 0 10px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 10px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + } + + .menu { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + background-color: $surface1; + // border: 2px solid $rosewater; + padding: 5px; + margin-top: 10px; + + .icon { + margin: 0 10px; + margin-left: 4px; + } + + .title { + font-weight: bold; + } + + separator { + margin: 10px; + background-color: $rosewater; + } + + button { + all: unset; + transition: 200ms; + border-radius: 5px; + color: $text; + background-color: transparent; + background-image: none; + box-shadow: none; + + + &:focus, + &.focused { + // box-shadow: inset 0 0 0 3px #0e0e0e; + background-color: $mantle; + color: $blue; + } + + &:hover { + // box-shadow: inset 0 0 0 3px $blue; + background-color: $mantle; + color: $blue; + } + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $mantle; + color: $blue; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + + padding: 10px; + + image:first-child { + margin-right: 10px; + } + } + + .bluetooth-devices { + &.horizontal>* { + margin: 0 5px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 5px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + } + + switch { + + all: unset; + transition: 200ms; + border-radius: 100px; + color: $text; + + background-color: $rosewater; + box-shadow: inset 0 0 0 4px $green; + + + &:focus>*, + &.focused>* { + box-shadow: inset 0 0 0 3px #0e0e0e; + background-color: $blue; + color: $mantle; + } + + &:hover>* { + box-shadow: inset 0 0 0 3px $blue; + background-color: $blue; + color: $mantle; + } + + &:active, + &.active, + &.on, + &:checked { + >* { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + } + + &:hover>* { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + + slider { + background-color: $blue; + border-radius: 10px; + min-width: 24px; + min-height: 24px; + } + + image { + color: transparent; + } + } + } + + .sliders-box .menu { + margin: 10px 0; + + &.app-mixer { + .mixer-item { + padding: 10px; + padding-left: 0; + padding-right: 20px; + + scale { + all: unset; + + * { + all: unset + } + + trough { + transition: 200ms; + border-radius: 10px; + // border: 2px solid $green; + background-color: $base; + min-height: 5px; + min-width: 5px; + + highlight, + progress { + border-radius: 20px; + background-image: linear-gradient(to right, darken($blue, 20%), darken($blue, 30%)); + min-height: 5px; + min-width: 5px; + } + } + + slider { + box-shadow: none; + background-color: transparent; + border: 1px solid transparent; + transition: 200ms; + border-radius: 10px; + min-height: 16px; + min-width: 16px; + margin: -4px; + } + + &:hover { + trough { + background-color: $mantle; + } + + slider { + background-color: $text; + border-color: $blue; + // box-shadow: 0 0 3px 0 #0e0e0e; + } + } + + &:disabled { + + highlight, + progress { + background-color: transparentize($text, 0.4); + background-image: none; + } + } + + trough:focus { + background-color: $crust; + // box-shadow: inset 0 0 0 4px $mantle; + + slider { + background-color: darken($text, 10%); + // box-shadow: inset 0 0 0 4px $mantle; + } + } + + margin: 0 10px; + + } + + image { + font-size: 1.6em; + margin: 0 8px; + } + } + } + } + + .toggle-button { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + background-color: $surface0; + // box-shadow: inset 0 0 0 1px $rosewater; + + + &:focus, + &.focused { + box-shadow: inset 0 0 0 3px #0e0e0e; + background-color: $blue; + color: $mantle; + } + + &:hover { + box-shadow: inset 0 0 0 2px $green; + background-color: $surface2; + color: $text; + } + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $surface2; + color: $base; + + &:hover>* { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + + font-weight: bold; + + image { + font-size: 1.3em; + } + + label { + margin-left: 10px; + } + + button { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + background-color: transparent; + background-image: none; + box-shadow: none; + + &:focus, + &.focused { + box-shadow: inset 0 0 0 2px $blue; + background-color: $surface1; + color: $text; + } + + &:hover { + box-shadow: inset 0 0 0 1px $rosewater; + background-color: $surface2; + color: $text; + } + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 1px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $surface0; + color: $text; + + &:hover>* { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + } + + &:first-child { + padding: 12px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + &:last-child { + padding: 6px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + } + + &.active { + background-color: $surface1; + + label, + image { + color: $text; + } + } + } + + .simple-toggle { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + background-color: $surface0; + background-image: none; + box-shadow: none; + + &:focus, + &.focused { + box-shadow: inset 0 0 0 2px $rosewater; + background-color: $surface1; + color: $text; + } + + &:hover { + box-shadow: inset 0 0 0 1px $blue; + background-color: $surface1; + color: $text; + } + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $text; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + + font-weight: bold; + padding: 10px; + + label { + margin-left: 4px; + } + + image { + font-size: 1.3em; + } + } + + .media { + + &.horizontal>* { + margin: 0 10px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 10px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + .player { + + transition: 200ms; + border-radius: 15px; + color: $text; + background-color: $crust; + // border: 1px solid $rosewater; + padding: 5px; + + .cover { + box-shadow: 2px 2px 2px 0 #0e0e0e; + background-size: cover; + background-position: center; + border-radius: 10px; + margin: 10px 15px 10px 10px; + } + + button { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + padding: 5px; + background-color: transparent; + background-image: none; + box-shadow: none; + + &:focus, + &.focused { + box-shadow: inset 0 0 0 1px $rosewater; + // background-color: $blue; + // color: $mantle; + } + + &:hover { + box-shadow: inset 0 0 0 1px $blue; + background-color: $surface0; + color: $blue; + } + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + + + &.play-pause { + margin: 0 5px; + } + + image { + font-size: 1.5em; + } + } + + .artist { + color: transparentize($text, .2); + font-size: .9em; + } + + scale { + + // @include slider($width: .5em, $slider: false, $gradient: linear-gradient($fg, $fg)); + trough { + transition: 200ms; + border-radius: 10px; + // border: 1px solid $rosewater; + background-color: $base; + min-height: 0.5em; + min-width: 0.5em; + + highlight, + progress { + border-radius: 10px; + background-image: linear-gradient($overlay1, $overlay1); + min-height: 0.5em; + min-width: 0.5em; + } + } + + slider { + box-shadow: none; + background-color: transparent; + border: 4px solid transparent; + transition: 200ms; + border-radius: 10px; + min-height: 0.5em; + min-width: 0.5em; + margin: -0.5em; + } + + &:hover { + trough { + background-color: $surface0; + } + } + + &:disabled { + + highlight, + progress { + background-color: transparentize($text, 0.4); + background-image: none; + } + } + + trough:focus { + background-color: $blue; + box-shadow: inset 0 0 0 4px $mantle; + } + + margin-bottom: 5px; + + trough { + border: none; + } + } + + .icon { + font-size: 22px; + margin: 5px 5px 0px 0px; + } + } + } + + // notification + .notifications { + .header { + margin-bottom: 20px; + margin-right: 30px; + + >label { + margin-left: 15px; + } + + button { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + + background-color: $surface0; + // box-shadow: inset 0 0 0 2px $blue; + + &:focus { + box-shadow: inset 0 0 0 2px #0e0e0e; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:hover { + box-shadow: inset 0 0 0 2px $blue; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:active, + &.on, + &.active, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($mantle, 0.7); + } + + padding: 10px; + } + } + + .notification-scrollable { + + scrollbar, + scrollbar * { + all: unset; + } + + scrollbar.vertical { + transition: 200ms; + background-color: transparentize($base, 0.7); + + &:hover { + background-color: transparentize($base, 0.3); + + slider { + background-color: transparentize($text, 0.3); + min-width: .6em; + } + } + } + + + scrollbar.vertical slider { + background-color: transparentize($text, 0.5); + border-radius: 10px; + min-width: .4em; + min-height: 2em; + transition: 200ms; + } + } + + .notification-list { + margin-right: 10px; + } + + .notification { + &.critical { + box-shadow: inset 0 0 .5em 0 $red; + } + + &:hover button.close-button { + // @include button-hover; + background-color: transparentize($red, .5); + } + + .content { + .title { + margin-top: 5px; + margin-right: 10px; + color: $text; + font-size: 1.2em; + } + + .time { + color: transparentize($text, .2); + margin: 5px 10px; + } + + .description { + font-size: .9em; + color: transparentize($text, .2); + } + + .icon { + border-radius: 10px; + margin-right: 10px; + + &.img { + border: 2px solid $green; + } + } + } + + box.actions { + // @include spacing(0.5); + margin-top: 10px; + + button { + // @include button; + background-color: $surface0; + border: 2px solid $surface0; + border-radius: 10px; + font-size: 1.2em; + margin: 0px 10px 10px 0px; + padding: 10px; + + &:first-child { + margin: 0px 10px 10px 10px; + } + + &:hover { + background-color: $surface2; + border: 2px solid $rosewater; + } + } + } + + button.close-button { + // @include button($flat: true); + margin: 0px 0px 0px 0px; + border-radius: 10px; + min-width: 30px; + min-height: 30px; + background-color: rgba(0, 0, 0, 0); //$crust; + padding: 0px; + + &:hover { + background-color: transparentize($red, .2); + } + + &:active { + background-image: none; + background-color: $red; + } + } + + transition: 200ms; + color: $text; + margin-bottom: 10px; + border-radius: 15px; + background-color: $base; + font-family: "Iosevka"; + padding: 5px; + border: 1px solid $rosewater; + box-shadow: rgba(0, 0, 0, 0.4) 5px 5px 5px; + } + + .placeholder { + image { + font-size: 7em; + } + + label { + font-size: 1.2em; + } + } + } +} diff --git a/packages/ags/bk/scss/main.scss b/packages/ags/bk/scss/main.scss new file mode 100644 index 0000000..32c017d --- /dev/null +++ b/packages/ags/bk/scss/main.scss @@ -0,0 +1,11 @@ +@import './mocha'; +@import './globals'; +@import './lockscreen'; +@import './powermenu'; +@import './dock'; +@import './overview'; +@import './notifications'; +@import './bar'; +@import './osd'; +@import './datemenu'; +@import './quicksettings'; diff --git a/packages/ags/bk/services/asusctl.js b/packages/ags/bk/services/asusctl.js new file mode 100644 index 0000000..d9e6a6b --- /dev/null +++ b/packages/ags/bk/services/asusctl.js @@ -0,0 +1,49 @@ +import { sh } from "../lib/utils.js" + +class Asusctl extends Service { + static { + Service.register(this, {}, { + "profile": ["string", "r"], + "mode": ["string", "r"], + }) + } + + available = !!Utils.exec("which asusctl") + #profile = "Balanced" + #mode = "Hybrid" + + async nextProfile() { + await sh("asusctl profile -n") + const profile = await sh("asusctl profile -p") + const p = profile.split(" ")[3] + this.#profile = p + this.changed("profile") + } + + async setProfile(prof) { + 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") + this.changed("profile") + } + + constructor() { + super() + + if (this.available) { + sh("asusctl profile -p").then(p => this.#profile = p.split(" ")[3]) + sh("supergfxctl -g").then(m => this.#mode = m) + } + } + + get profiles() { return ["Performance", "Balanced", "Quiet"] } + get profile() { return this.#profile } + get mode() { return this.#mode } +} + +export default new Asusctl() diff --git a/packages/ags/bk/services/brightness.js b/packages/ags/bk/services/brightness.js new file mode 100644 index 0000000..715adb4 --- /dev/null +++ b/packages/ags/bk/services/brightness.js @@ -0,0 +1,69 @@ +import { bash, dependencies, sh } from "../lib/utils.js" + +if (!dependencies("brightnessctl")) + App.quit() + +const get = (args) => 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/packages/ags/bk/services/clock.js b/packages/ags/bk/services/clock.js new file mode 100644 index 0000000..73e32ee --- /dev/null +++ b/packages/ags/bk/services/clock.js @@ -0,0 +1,21 @@ +const { GLib } = imports.gi; +class ClockService extends Service { + static { + Service.register ( this, {}, { + 'time': ['gobject'] + } + ) + } + + #time = GLib.DateTime.new_now_local() + + get time() { return this.#time; } + constructor() { + super() + Utils.interval(1000, () => { + this.#time = GLib.DateTime.new_now_local() + this.changed("time") + }) + } +} +export default new ClockService() diff --git a/packages/ags/bk/services/colorpicker.js b/packages/ags/bk/services/colorpicker.js new file mode 100644 index 0000000..eed3923 --- /dev/null +++ b/packages/ags/bk/services/colorpicker.js @@ -0,0 +1,57 @@ +import icons from "../lib/icons.js" +import { bash, dependencies } from "../lib/utils.js" + +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) || "[]") + + get colors() { return [...this.#colors] } + set colors(colors) { + this.#colors = colors + this.changed("colors") + } + + // TODO: doesn't work? + async wlCopy(color) { + if (dependencies("wl-copy")) + bash(`wl-copy ${color}`) + } + + async pick() { + if (!dependencies("hyprpicker")) + return + + const color = await bash("hyprpicker -a -r") + if (!color) + return + + colorpicker.wlCopy(color) + const list = colorpicker.colors + if (!list.includes(color)) { + list.push(color) + if (list.length > MAX_NUM_COLORS) + list.shift() + + colorpicker.colors = list + Utils.writeFile(JSON.stringify(list, null, 2), COLORS_CACHE) + } + + colorpicker.notifID = await Utils.notify({ + id: colorpicker.notifID, + iconName: icons.ui.colorpicker, + summary: color, + }) + } +} + +const colorpicker = new ColorPicker +export default colorpicker diff --git a/packages/ags/bk/services/powermenu.js b/packages/ags/bk/services/powermenu.js new file mode 100644 index 0000000..bf0beaa --- /dev/null +++ b/packages/ags/bk/services/powermenu.js @@ -0,0 +1,43 @@ +import options from '../options.js'; +// 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) { + [this.#cmd, this.#title] = { + sleep: [options.powermenu.sleep, "Sleep"], + reboot: [options.powermenu.reboot, "Reboot"], + logout: [options.powermenu.logout, "Log Out"], + shutdown: [options.powermenu.shutdown, "Shutdown"], + lock: [options.powermenu.lock, "Lock"], + }[action] + + this.notify("cmd") + this.notify("title") + this.emit("changed") + App.closeWindow("powermenu") + App.openWindow("verification") + } + + shutdown = () => { + this.action("shutdown") + } +} + +const powermenu = new PowerMenu +globalThis["powermenu"] = powermenu +export default powermenu diff --git a/packages/ags/bk/services/screenrecord.js b/packages/ags/bk/services/screenrecord.js new file mode 100644 index 0000000..23e6f8b --- /dev/null +++ b/packages/ags/bk/services/screenrecord.js @@ -0,0 +1,93 @@ +import GLib from "gi://GLib" +import icons from "../lib/icons.js" +import { dependencies, sh, bash } from "../lib/utils.js" + +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) + + const wayshot = `wayshot -f ${file} ${full ? "" : `-s "${await sh("slurp")}"`}` + await sh(wayshot) + bash(`wl-copy < ${file}`) + + Utils.notify({ + image: file, + summary: "Screenshot", + body: this.#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 +globalThis["recorder"] = recorder +export default recorder diff --git a/packages/ags/bk/style.css b/packages/ags/bk/style.css new file mode 100644 index 0000000..9ac7355 --- /dev/null +++ b/packages/ags/bk/style.css @@ -0,0 +1,40 @@ +window.bar { + background-color: @theme_bg_color; + color: @theme_fg_color; +} + +button { + min-width: 0; + padding-top: 0; + padding-bottom: 0; + background-color: transparent; +} + +button:active { + background-color: @theme_selected_bg_color; +} + +button:hover { + border-bottom: 3px solid @theme_fg_color; +} + +label { + font-weight: bold; +} + +.workspaces button.focused { + border-bottom: 3px solid @theme_selected_bg_color; +} + +.client-title { + color: @theme_selected_bg_color; +} + +.notification { + color: yellow; +} + +levelbar block, +highlight { + min-height: 10px; +} diff --git a/packages/ags/config.js b/packages/ags/config.js new file mode 100644 index 0000000..4c0a30b --- /dev/null +++ b/packages/ags/config.js @@ -0,0 +1,219 @@ +const hyprland = await Service.import("hyprland") +const notifications = await Service.import("notifications") +const mpris = await Service.import("mpris") +const audio = await Service.import("audio") +const battery = await Service.import("battery") +const systemtray = await Service.import("systemtray") + +const date = Variable("", { + poll: [1000, 'date "+%H:%M:%S %b %e."'], +}) + +// widgets can be only assigned as a child in one container +// so to make a reuseable widget, make it a function +// then you can simply instantiate one by calling it + +function Workspaces() { + const activeId = hyprland.active.workspace.bind("id") + const workspaces = hyprland.bind("workspaces") + .as(ws => ws.map(({ id }) => Widget.Button({ + on_clicked: () => hyprland.messageAsync(`dispatch workspace ${id}`), + child: Widget.Label(`${id}`), + class_name: activeId.as(i => `${i === id ? "focused" : ""}`), + }))) + + return Widget.Box({ + class_name: "workspaces", + children: workspaces, + }) +} + + +function ClientTitle() { + return Widget.Label({ + class_name: "client-title", + label: hyprland.active.client.bind("title"), + }) +} + + +function Clock() { + return Widget.Label({ + class_name: "clock", + label: date.bind(), + }) +} + + +// we don't need dunst or any other notification daemon +// because the Notifications module is a notification daemon itself +function Notification() { + const popups = notifications.bind("popups") + return Widget.Box({ + class_name: "notification", + visible: popups.as(p => p.length > 0), + children: [ + Widget.Icon({ + icon: "preferences-system-notifications-symbolic", + }), + Widget.Label({ + label: popups.as(p => p[0]?.summary || ""), + }), + ], + }) +} + + +function Media() { + const label = Utils.watch("", mpris, "player-changed", () => { + if (mpris.players[0]) { + const { track_artists, track_title } = mpris.players[0] + return `${track_artists.join(", ")} - ${track_title}` + } else { + return "Nothing is playing" + } + }) + + return Widget.Button({ + class_name: "media", + on_primary_click: () => mpris.getPlayer("")?.playPause(), + on_scroll_up: () => mpris.getPlayer("")?.next(), + on_scroll_down: () => mpris.getPlayer("")?.previous(), + child: Widget.Label({ label }), + }) +} + + +function Volume() { + const icons = { + 101: "overamplified", + 67: "high", + 34: "medium", + 1: "low", + 0: "muted", + } + + function getIcon() { + const icon = audio.speaker.is_muted ? 0 : [101, 67, 34, 1, 0].find( + threshold => threshold <= audio.speaker.volume * 100) + + return `audio-volume-${icons[icon]}-symbolic` + } + + const icon = Widget.Icon({ + icon: Utils.watch(getIcon(), audio.speaker, getIcon), + }) + + const slider = Widget.Slider({ + hexpand: true, + draw_value: false, + on_change: ({ value }) => audio.speaker.volume = value, + setup: self => self.hook(audio.speaker, () => { + self.value = audio.speaker.volume || 0 + }), + }) + + return Widget.Box({ + class_name: "volume", + css: "min-width: 180px", + children: [icon, slider], + }) +} + + +function BatteryLabel() { + const value = battery.bind("percent").as(p => p > 0 ? p / 100 : 0) + const icon = battery.bind("percent").as(p => + `battery-level-${Math.floor(p / 10) * 10}-symbolic`) + + return Widget.Box({ + class_name: "battery", + visible: battery.bind("available"), + children: [ + Widget.Icon({ icon }), + Widget.LevelBar({ + widthRequest: 140, + vpack: "center", + value, + }), + ], + }) +} + + +function SysTray() { + const items = systemtray.bind("items") + .as(items => items.map(item => Widget.Button({ + child: Widget.Icon({ icon: item.bind("icon") }), + on_primary_click: (_, event) => item.activate(event), + on_secondary_click: (_, event) => item.openMenu(event), + tooltip_markup: item.bind("tooltip_markup"), + }))) + + return Widget.Box({ + children: items, + }) +} + + +// layout of the bar +function Left() { + return Widget.Box({ + spacing: 8, + children: [ + Workspaces(), + ClientTitle(), + ], + }) +} + +function Center() { + return Widget.Box({ + spacing: 8, + children: [ + Media(), + Notification(), + ], + }) +} + +function Right() { + return Widget.Box({ + hpack: "end", + spacing: 8, + children: [ + Volume(), + BatteryLabel(), + Clock(), + SysTray(), + ], + }) +} + +function Bar(monitor = 0) { + return Widget.Window({ + name: `bar-${monitor}`, // name has to be unique + class_name: "bar", + monitor, + anchor: ["top", "left", "right"], + exclusivity: "exclusive", + child: Widget.CenterBox({ + start_widget: Left(), + center_widget: Center(), + end_widget: Right(), + }), + }) +} + +App.config({ + style: "./style.css", + windows: [ + Bar(), + + // you can call it, for each monitor + // Bar(0), + // Bar(1) + ], +}) + +export { } diff --git a/packages/ags/config/.gitignore b/packages/ags/config/.gitignore new file mode 100644 index 0000000..ec92969 --- /dev/null +++ b/packages/ags/config/.gitignore @@ -0,0 +1,9 @@ +keys.js +style.css +highlight.css +lockstyle.css +node_modules +types +.idea +.editorconfig +*.css.map diff --git a/packages/ags/config/assets/battery-flash-symbolic.svg b/packages/ags/config/assets/battery-flash-symbolic.svg new file mode 100644 index 0000000..21b5e33 --- /dev/null +++ b/packages/ags/config/assets/battery-flash-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/config/assets/chat-bubbles-symbolic.svg b/packages/ags/config/assets/chat-bubbles-symbolic.svg new file mode 100644 index 0000000..fdee0b3 --- /dev/null +++ b/packages/ags/config/assets/chat-bubbles-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/ags/config/assets/controller-symbolic.svg b/packages/ags/config/assets/controller-symbolic.svg new file mode 100644 index 0000000..98bf5d6 --- /dev/null +++ b/packages/ags/config/assets/controller-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/config/assets/controls-symbolic.svg b/packages/ags/config/assets/controls-symbolic.svg new file mode 100644 index 0000000..7df5663 --- /dev/null +++ b/packages/ags/config/assets/controls-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/ags/config/assets/dark-mode-symbolic.svg b/packages/ags/config/assets/dark-mode-symbolic.svg new file mode 100644 index 0000000..9f2e6b4 --- /dev/null +++ b/packages/ags/config/assets/dark-mode-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/config/assets/hourglass-symbolic.svg b/packages/ags/config/assets/hourglass-symbolic.svg new file mode 100644 index 0000000..aa4f97c --- /dev/null +++ b/packages/ags/config/assets/hourglass-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/config/assets/light-mode-symbolic.svg b/packages/ags/config/assets/light-mode-symbolic.svg new file mode 100644 index 0000000..d5fb271 --- /dev/null +++ b/packages/ags/config/assets/light-mode-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/config/assets/mixer-symbolic.svg b/packages/ags/config/assets/mixer-symbolic.svg new file mode 100644 index 0000000..ad6cfa8 --- /dev/null +++ b/packages/ags/config/assets/mixer-symbolic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/ags/config/assets/nixos-symbolic.svg b/packages/ags/config/assets/nixos-symbolic.svg new file mode 100644 index 0000000..7bb42ed --- /dev/null +++ b/packages/ags/config/assets/nixos-symbolic.svg @@ -0,0 +1,155 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ags/config/assets/nixos.svg b/packages/ags/config/assets/nixos.svg new file mode 100644 index 0000000..1a756ed --- /dev/null +++ b/packages/ags/config/assets/nixos.svg @@ -0,0 +1,277 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ags/config/assets/person-symbolic.svg b/packages/ags/config/assets/person-symbolic.svg new file mode 100644 index 0000000..dd2fe1b --- /dev/null +++ b/packages/ags/config/assets/person-symbolic.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ags/config/assets/processor-symbolic.svg b/packages/ags/config/assets/processor-symbolic.svg new file mode 100644 index 0000000..832dbaf --- /dev/null +++ b/packages/ags/config/assets/processor-symbolic.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/ags/config/assets/terminal-symbolic.svg b/packages/ags/config/assets/terminal-symbolic.svg new file mode 100644 index 0000000..9f82bcf --- /dev/null +++ b/packages/ags/config/assets/terminal-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/ags/config/assets/toolbars-symbolic.svg b/packages/ags/config/assets/toolbars-symbolic.svg new file mode 100644 index 0000000..9f4c564 --- /dev/null +++ b/packages/ags/config/assets/toolbars-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ags/config/config.js b/packages/ags/config/config.js new file mode 100644 index 0000000..af42ef8 --- /dev/null +++ b/packages/ags/config/config.js @@ -0,0 +1,85 @@ +App.addIcons(`${App.configDir}/assets`) + +import Gdk from 'gi://Gdk'; +import GLib from 'gi://GLib'; +import App from 'resource:///com/github/Aylur/ags/app.js' +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js' + +import options from './options.js'; +import { init } from './lib/init.js'; + +import Lockscreen from './modules/lock/Lockscreen.js'; +import Powermenu from './modules/powermenu/Powermenu.js'; +import OSD from './modules/osd/OSD.js'; +import Verification from './modules/powermenu/Verification.js'; +import Overview from './modules/overview/Overview.js'; +import NotificationPopups from './modules/notifications/NotificationPopups.js'; +import Bar from './modules/bar/Bar.js' +import { setupDateMenu } from './modules/datemenu/DateMenu.js'; +import { setupQuickSettings } from './modules/quicksettings/QuickSettings.js'; + +const range = (length, start = 1) => Array.from({ length }, (_, i) => i + start); +function forMonitors(widget) { + const n = Gdk.Display.get_default()?.get_n_monitors() || 1; + return range(n, 0).map(widget).flat(1); +} + + +print(App.configDir) +// SCSS compilation +// Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicwal.scss'`); // reset music styles +// Utils.exec(`bash -c 'echo "" > ${App.configDir}/scss/_musicmaterial.scss'`); // reset music styles +async function applyStyle() { + const COMPILED_STYLE_DIR = `${GLib.get_user_cache_dir()}/ags/user/generated` + if (options.recompileSass) { + Utils.exec(`mkdir -p ${COMPILED_STYLE_DIR}`); + Utils.exec(`sassc ${App.configDir}/scss/main.scss ${COMPILED_STYLE_DIR}/style.css`); + } + App.resetCss(); + App.applyCss(`${COMPILED_STYLE_DIR}/style.css`); + print('[LOG] Styles loaded') + print(`CRITICAL: Reload Sass option is set to: ${options.recompileSass}`) +} +applyStyle().catch(print); + + +const Windows = () => [ + Lockscreen(), + Powermenu(), + // Dock(), + Verification(), + Overview(), + forMonitors(NotificationPopups), + forMonitors(Bar), + forMonitors(OSD), +]; + +// const CLOSE_ANIM_TIME = 210; // Longer than actual anim time to make sure widgets animate fully +export default { + onConfigParsed: () => { + setupQuickSettings() + setupDateMenu() + init() + if (options.monitorCSS) { + Utils.monitorFile( + // directory that contains the scss files + `${App.configDir}/scss`, + function() { + applyStyle() + }, + ) + } + }, + css: `${App.configDir}/style.css`, + stackTraceOnError: true, + closeWindowDelay: { // For animations + "powermenu": options.theme.PopupCloseDuration, + "verification": options.theme.PopupCloseDuration, + "overview": options.theme.PopupCloseDuration, + "quicksettings": options.theme.PopupCloseDuration, + // 'sideright': CLOSE_ANIM_TIME, + // 'sideleft': CLOSE_ANIM_TIME, + // 'osk': CLOSE_ANIM_TIME, + }, + windows: Windows().flat(1), +}; diff --git a/packages/ags/config/lib/battery.js b/packages/ags/config/lib/battery.js new file mode 100644 index 0000000..574ba29 --- /dev/null +++ b/packages/ags/config/lib/battery.js @@ -0,0 +1,17 @@ + +import icons from "./icons.js" + +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/packages/ags/config/lib/experiments.js b/packages/ags/config/lib/experiments.js new file mode 100644 index 0000000..fee1c2f --- /dev/null +++ b/packages/ags/config/lib/experiments.js @@ -0,0 +1,67 @@ + +// these are functionalities that I might include in ags + +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Variable } from "resource:///com/github/Aylur/ags/variable.js" +import { App } from "resource:///com/github/Aylur/ags/app.js" +import GObject from "gi://GObject?version=2.0" + +// eslint-disable-next-line max-len +// export function watch(init, objs, callback) +// export function watch(init, obj, callback) +export function pwatch( + init, + objs, + sigOrFn, + callback, +) { + const v = new Variable(init) + const f = typeof sigOrFn === "function" ? sigOrFn : callback ?? (() => v.value) + const set = () => v.value = f() + + if (Array.isArray(objs)) { + // multiple objects + for (const obj of objs) { + if (Array.isArray(obj)) { + // obj signal pair + const [o, s = "changed"] = obj + o.connect(s, set) + } else { + // obj on changed + obj.connect("changed", set) + } + } + } else { + // watch single object + const signal = typeof sigOrFn === "string" ? sigOrFn : "changed" + objs.connect(signal, set) + } + + return v.bind() +} +export function watch(init, objs, sigOrFn, callback) { + print(objs) + const v = new Variable(init); + const f = typeof sigOrFn === "function" ? sigOrFn : (callback || (() => v.value)); + const set = () => { v.value = f(); }; + + if (Array.isArray(objs)) { + // multiple objects + for (const obj of objs) { + if (Array.isArray(obj)) { + // obj signal pair + const [o, s = "changed"] = obj; + o.connect(s, set); + } else { + // obj on changed + obj.connect("changed", set); + } + } + } else { + // watch single object + const signal = typeof sigOrFn === "string" ? sigOrFn : "changed"; + objs.connect(signal, set); + } + + return v.bind(); +} diff --git a/packages/ags/config/lib/icons.js b/packages/ags/config/lib/icons.js new file mode 100644 index 0000000..690a806 --- /dev/null +++ b/packages/ags/config/lib/icons.js @@ -0,0 +1,133 @@ + +export const substitutes = { + "transmission-gtk": "transmission", + "blueberry.py": "blueberry", + "Caprine": "facebook-messenger", + "com.raggesilver.BlackBox-symbolic": "terminal-symbolic", + "audio-headset-bluetooth": "audio-headphones-symbolic", + "audio-card-analog-usb": "audio-speakers-symbolic", + "audio-card-analog-pci": "audio-card-symbolic", + "preferences-system": "emblem-system-symbolic", + "com.github.Aylur.ags-symbolic": "controls-symbolic", + "com.github.Aylur.ags": "controls-symbolic", +} + +export default { + fallback: { + executable: "application-x-executable-symbolic", + notification: "dialog-information-symbolic", + video: "video-x-generic-symbolic", + audio: "audio-x-generic-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", + person: "person-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-overamplified-symbolic", + }, + type: { + headset: "audio-headphones-symbolic", + speaker: "audio-speakers-symbolic", + card: "audio-card-symbolic", + }, + mixer: "mixer-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", + }, +} diff --git a/packages/ags/config/lib/init.js b/packages/ags/config/lib/init.js new file mode 100644 index 0000000..d42da9e --- /dev/null +++ b/packages/ags/config/lib/init.js @@ -0,0 +1,19 @@ + +// import hyprland from "./hyprland.js" +// import tmux from "./tmux" +// import gtk from "./gtk" +import lowBattery from "./battery.js" +// import swww from "./swww" + +export async function init() { + try { + // gtk() + // tmux() + lowBattery() + // hyprland() + // css() + // swww() + } catch (error) { + logError(error) + } +} diff --git a/packages/ags/config/lib/utils.js b/packages/ags/config/lib/utils.js new file mode 100644 index 0000000..d3cf694 --- /dev/null +++ b/packages/ags/config/lib/utils.js @@ -0,0 +1,120 @@ + +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { substitutes } from "./icons.js" +import Gtk from "gi://Gtk?version=3.0" +import Gdk from "gi://Gdk" +import GLib from "gi://GLib?version=2.0" + +export function config(config) { + return config +} + +/** + * @returns substitute icon || name || fallback icon + */ +export function icon(name, fallback = name) { + 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, ...values) { + 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) { + return Utils.execAsync(cmd).catch(err => { + console.error(typeof cmd === "string" ? cmd : cmd.join(" "), err) + return "" + }) +} + +export function forMonitors(widget) { + 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, start = 1) { + return Array.from({ length }, (_, i) => i + start) +} + +/** + * promisified timeout + */ +export function wait(ms, callback) { + return new Promise(resolve => Utils.timeout(ms, () => { + resolve(callback()) + })) +} + +/** + * @returns true if all of the `bins` are found + */ +export function dependencies(...bins) { + const missing = bins.filter(bin => { + return !Utils.exec(`which ${bin}`) + }) + + if (missing.length > 0) + console.warn("missing dependencies:", missing.join(", ")) + + return missing.length === 0 +} + +/** + * run app detached + */ +export function launchApp(app) { + 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) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const cairo = imports.gi.cairo + 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/packages/ags/config/lib/variables.js b/packages/ags/config/lib/variables.js new file mode 100644 index 0000000..383d77e --- /dev/null +++ b/packages/ags/config/lib/variables.js @@ -0,0 +1,16 @@ + +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, + ], +}) diff --git a/packages/ags/config/modules/PopupWindow.js b/packages/ags/config/modules/PopupWindow.js new file mode 100644 index 0000000..15fc73b --- /dev/null +++ b/packages/ags/config/modules/PopupWindow.js @@ -0,0 +1,158 @@ +import Gtk from "gi://Gtk?version=3.0" +import options from '../options.js'; + +export const Padding = (name, + css = "", + hexpand = true, + vexpand = true, +) => Widget.EventBox({ + hexpand, + vexpand, + can_focus: false, + child: Widget.Box({ css }), + setup: w => w.on("button-press-event", () => App.toggleWindow(name)), +}) + +const PopupRevealer = ( + name, + child, + transition = "slide_down", +) => Widget.Box( + { css: "padding: 1px;", class_name: "popup-borderbox" }, + Widget.Revealer({ + transition, + child: Widget.Box({ + class_name: "window-content", + child, + }), + transitionDuration: options.theme.PopupTransitionDuration, + setup: self => self.hook(App, (_, wname, visible) => { + if (wname === name) + self.reveal_child = visible + }), + }), +) + +const Layout = (name, child, 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), + ), + ), + "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 +}) => Widget.Window({ + name, + class_names: [name, "popup-window"], + popup: true, + visible: false, + keymode: "on-demand", + exclusivity, + layer: "top", + anchor: ["top", "bottom", "right", "left"], + child: Layout(name, child, transition)[layout](), + ...props, + setup: (self => { + self.keybind("Escape", () => App.closeWindow(name)) + }) +}) diff --git a/packages/ags/config/modules/bar/Bar.js b/packages/ags/config/modules/bar/Bar.js new file mode 100644 index 0000000..9dea146 --- /dev/null +++ b/packages/ags/config/modules/bar/Bar.js @@ -0,0 +1,56 @@ +import BatteryBar from "./buttons/BatteryBar.js" +import ColorPicker from "./buttons/ColorPicker.js" +import Date from "./buttons/Date.js" +import Launcher from "./buttons/Launcher.js" +import Media from "./buttons/Media.js" +import PowerMenu from "./buttons/PowerMenu.js" +import SysTray from "./buttons/SysTray.js" +import SystemIndicators from "./buttons/SystemIndicators.js" +import Taskbar from "./buttons/Taskbar.js" +import Workspaces from "./buttons/Workspaces.js" +import ScreenRecord from "./buttons/ScreenRecord.js" +import Messages from "./buttons/Messages.js" +import options from "../../options.js" + +const { start, center, end } = options.bar.layout +const pos = options.bar.position + +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) => Widget.Window({ + monitor, + class_name: "bar", + name: `bar${monitor}`, + exclusivity: "exclusive", + layer: "top", + anchor: [pos, "right", "left"], + child: Widget.CenterBox({ + css: "min-width: 2px; min-height: 2px;", + startWidget: Widget.Box({ + hexpand: true, + children: start.map(w => widget[w]()), + }), + centerWidget: Widget.Box({ + hpack: "center", + children: center.map(w => widget[w]()), + }), + endWidget: Widget.Box({ + hexpand: true, + children: end.map(w => widget[w]()), + }), + }), +}) diff --git a/packages/ags/config/modules/bar/PanelButton.js b/packages/ags/config/modules/bar/PanelButton.js new file mode 100644 index 0000000..8c1fc50 --- /dev/null +++ b/packages/ags/config/modules/bar/PanelButton.js @@ -0,0 +1,38 @@ +import options from "../../options.js" + +export default ({ + window = "", + flat, + child, + setup, + ...rest +}) => Widget.Button({ + child: Widget.Box({ child }), + setup: self => { + let open = false + + self.toggleClassName("panel-button") + self.toggleClassName(window) + + self.toggleClassName("flat", flat ?? options.bar.flatButtons) + + 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/packages/ags/config/modules/bar/ScreenCorners.js b/packages/ags/config/modules/bar/ScreenCorners.js new file mode 100644 index 0000000..4e7cba4 --- /dev/null +++ b/packages/ags/config/modules/bar/ScreenCorners.js @@ -0,0 +1,25 @@ +import options from "../../options.js" + +const { corners } = options.bar + +export default (monitor) => 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.toggleClassName("corners", corners) + }, +}) diff --git a/packages/ags/config/modules/bar/bar.scss b/packages/ags/config/modules/bar/bar.scss new file mode 100644 index 0000000..472894c --- /dev/null +++ b/packages/ags/config/modules/bar/bar.scss @@ -0,0 +1,234 @@ +@use 'sass:color'; + +$bar-spacing: $spacing * .3; +$button-radius: $radius; + +@mixin panel-button($flat: true, $reactive: true) { + @include accs-button($flat, $reactive); + + >* { + border-radius: $button-radius; + margin: $bar-spacing; + } + + label, + image { + font-weight: bold; + } + + >* { + padding: $padding * 0.4 $padding * 0.8; + } +} + +.bar { + background-color: $bg; + + .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*.6; + box-shadow: inset 0 0 0 $border-width $border-color; + margin: 0 $padding * .5; + transition: $transition* .5; + background-color: transparentize($fg, .8); + + &.occupied { + background-color: transparentize($fg, .2); + min-width: 7pt; + min-height: 7pt; + } + + &.active { + // background-color: $primary-bg; + background-image: $active-gradient; + min-width: 20pt; + min-height: 12pt; + } + } + + &.active, + &:active { + label { + background-color: transparentize($primary-fg, .3); + + &.occupied { + background-color: transparentize($primary-fg, .15); + } + + &.active { + background-color: $primary-fg; + } + } + } + } + + .media label { + margin: 0 ($spacing * .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 * .7; + color: $fg; + text-shadow: $text-shadow; + } + } + } + + .battery-bar { + >* { + padding: 0; + } + + &.bar-hidden>box { + padding: 0 $spacing * .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 * .5; + } + + trough { + @include widget; + min-height: 12pt; + min-width: 12pt; + } + + .regular trough { + margin-right: $spacing * .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/packages/ags/config/modules/bar/buttons/BatteryBar.js b/packages/ags/config/modules/bar/buttons/BatteryBar.js new file mode 100644 index 0000000..4eada2a --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/BatteryBar.js @@ -0,0 +1,94 @@ +import icons from "../../../lib/icons.js" +import options from "../../../options.js" +import PanelButton from "../PanelButton.js" + +const battery = await Service.import("battery") +let { bar, percentage, blocks, width, low } = options.bar.battery + +percentage = Variable(percentage, {}) + +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({ + mode: 1, + max_value: blocks, + visible: bar !== "hidden", + value: battery.bind("percent").as(p => (p / 100) * blocks), + }) + const update = () => { + level.value = (battery.percent / 100) * blocks + level.css = `block { min-width: ${width / blocks}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 }, + child: Widget.Box({ + expand: true, + visible: battery.bind("available"), + child: bar === "whole" ? WholeButton() : Regular(), + }), + setup: self => { + self.toggleClassName("bar-hidden", bar === "hidden") + self.toggleClassName("charging", battery.charging || battery.charged) + self.toggleClassName("low", battery.percent < low) + } +}) diff --git a/packages/ags/config/modules/bar/buttons/ColorPicker.js b/packages/ags/config/modules/bar/buttons/ColorPicker.js new file mode 100644 index 0000000..e4366ae --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/ColorPicker.js @@ -0,0 +1,37 @@ +import PanelButton from "../PanelButton.js" +import colorpicker from "../../../services/colorpicker.js" +import Gdk from "gi://Gdk" + +const css = (color) => ` +* { + 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/packages/ags/config/modules/bar/buttons/Date.js b/packages/ags/config/modules/bar/buttons/Date.js new file mode 100644 index 0000000..b9c5f26 --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/Date.js @@ -0,0 +1,19 @@ +import clock from "../../../services/clock.js" +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" + +const { format, action } = options.bar.date +// const time = Utils.derive([clock], (c) => { +// c.format(format) || "" +// }) + + +// const time = Variable('', { +// poll: [1000, `date "+${format}"`], +// }); + +export default () => PanelButton({ + window: "dashboard", + on_clicked: action, + child: Widget.Label({ label: clock.bind('time').as(t => `${t.format(format)}`) }), +}) diff --git a/packages/ags/config/modules/bar/buttons/Launcher.js b/packages/ags/config/modules/bar/buttons/Launcher.js new file mode 100644 index 0000000..0288171 --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/Launcher.js @@ -0,0 +1,21 @@ +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" + +const { icon, label, action } = options.bar.launcher + +export default () => PanelButton({ + window: "launcher", + on_clicked: action, + child: Widget.Box([ + Widget.Icon({ + class_name: icon.colored ? "colored" : "", + visible: !!icon.icon, + icon: icon.icon, + }), + Widget.Label({ + class_name: label.colored ? "colored" : "", + visible: !!label.label, + label: label.label, + }), + ]), +}) diff --git a/packages/ags/config/modules/bar/buttons/Media.js b/packages/ags/config/modules/bar/buttons/Media.js new file mode 100644 index 0000000..0c13b34 --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/Media.js @@ -0,0 +1,81 @@ +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" +import icons from "../../../lib/icons.js" +import { icon } from "../../../lib/utils.js" + +const mpris = await Service.import("mpris") +const { length, direction, preferred, monochrome } = options.bar.media + +const getPlayer = (name = preferred) => + mpris.getPlayer(name) || mpris.players[0] || null + +const Content = (player) => { + const revealer = Widget.Revealer({ + click_through: true, + visible: (length > 0), + transition: `slide_${direction}`, + 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, + label: player.bind("track_title").as(() => + `${player.track_artists.join(", ")} - ${player.track_title}`), + }), + }) + + const playericon = Widget.Icon({ + icon: player.bind("entry").as(entry => { + const name = `${entry}${monochrome ? "-symbolic" : ""}` + return icon(name, icons.fallback.audio) + }), + }) + + return Widget.Box({ + attribute: { revealer }, + children: direction === "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/packages/ags/config/modules/bar/buttons/Messages.js b/packages/ags/config/modules/bar/buttons/Messages.js new file mode 100644 index 0000000..3d90dc7 --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/Messages.js @@ -0,0 +1,16 @@ +import icons from "../../../lib/icons.js" +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" + +const n = await Service.import("notifications") +const notifs = n.bind("notifications") +const action = options.bar.messages.action + +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/packages/ags/config/modules/bar/buttons/PowerMenu.js b/packages/ags/config/modules/bar/buttons/PowerMenu.js new file mode 100644 index 0000000..39dd147 --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/PowerMenu.js @@ -0,0 +1,15 @@ +import icons from "../../../lib/icons.js" +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" + +const { monochrome, action } = options.bar.powermenu + +export default () => PanelButton({ + window: "powermenu", + on_clicked: action, + child: Widget.Icon(icons.powermenu.shutdown), + setup: self => { + self.toggleClassName("colored", !monochrome) + self.toggleClassName("box") + }, +}) diff --git a/packages/ags/config/modules/bar/buttons/ScreenRecord.js b/packages/ags/config/modules/bar/buttons/ScreenRecord.js new file mode 100644 index 0000000..9171d21 --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/ScreenRecord.js @@ -0,0 +1,21 @@ +import PanelButton from "../PanelButton.js" +import screenrecord from "../../../services/screenrecord.js" +import icons from "../../../lib/icons.js" + +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/packages/ags/config/modules/bar/buttons/SysTray.js b/packages/ags/config/modules/bar/buttons/SysTray.js new file mode 100644 index 0000000..e962a6b --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/SysTray.js @@ -0,0 +1,39 @@ +import PanelButton from "../PanelButton.js" +import Gdk from "gi://Gdk" +import options from "../../../options.js" + +const systemtray = await Service.import("systemtray") +const { ignore } = options.bar.systray + +const SysTrayItem = (item) => PanelButton({ + class_name: "tray-item", + child: Widget.Icon({ icon: item.bind("icon") }), + tooltip_markup: item.bind("tooltip_markup"), + setup: self => { + const menu = item.menu + if (!menu) + return + + const id = item.menu?.connect("popped-up", () => { + self.toggleClassName("active") + menu.connect("notify::visible", () => { + self.toggleClassName("active", menu.visible) + }) + menu.disconnect(id) + }) + + if (id) + self.connect("destroy", () => item.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.includes(id)) + .map(SysTrayItem)) diff --git a/packages/ags/config/modules/bar/buttons/SystemIndicators.js b/packages/ags/config/modules/bar/buttons/SystemIndicators.js new file mode 100644 index 0000000..2a5c44a --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/SystemIndicators.js @@ -0,0 +1,79 @@ +import PanelButton from "../PanelButton.js" +import icons from "../../../lib/icons.js" +import asusctl from "../../../services/asusctl.js" + +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 ProfileIndicator = () => Widget.Icon() + .bind("visible", asusctl, "profile", p => p !== "Balanced") + .bind("icon", asusctl, "profile", p => icons.asusctl.profile[p]) + +const ModeIndicator = () => Widget.Icon() + .bind("visible", asusctl, "mode", m => m !== "Hybrid") + .bind("icon", asusctl, "mode", m => icons.asusctl.mode[m]) + +const MicrophoneIndicator = () => Widget.Icon() + .hook(audio, self => self.visible = + audio.recorders.length > 0 + || audio.microphone.stream?.is_muted + || audio.microphone.is_muted) + .hook(audio.microphone, self => { + const vol = audio.microphone.stream.is_muted ? 0 : audio.microphone.volume + const { muted, low, medium, high } = icons.audio.mic + const cons = [[67, high], [34, medium], [1, low], [0, muted]] + 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({ + icon: audio.speaker.bind("volume").as(vol => { + const { muted, low, medium, high, overamplified } = icons.audio.volume + const cons = [[101, overamplified], [67, high], [34, medium], [1, low], [0, muted]] + const icon = cons.find(([n]) => n <= vol * 100)?.[1] || "" + return audio.speaker.is_muted ? muted : icon + }), +}) + +export default () => PanelButton({ + class_name: "quicksettings panel-button", + on_clicked: () => App.toggleWindow("quicksettings"), + on_scroll_up: () => audio.speaker.volume += 0.02, + on_scroll_down: () => audio.speaker.volume -= 0.02, + child: Widget.Box([ + asusctl?.available && ProfileIndicator(), + asusctl?.available && ModeIndicator(), + DNDIndicator(), + BluetoothIndicator(), + NetworkIndicator(), + AudioIndicator(), + MicrophoneIndicator(), + ]), +}) diff --git a/packages/ags/config/modules/bar/buttons/Taskbar.js b/packages/ags/config/modules/bar/buttons/Taskbar.js new file mode 100644 index 0000000..1c63b4b --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/Taskbar.js @@ -0,0 +1,86 @@ +import { launchApp, icon } from "../../../lib/utils.js" +import icons from "../../../lib/icons.js" +import options from "../../../options.js" +import { watch } from "../../../lib/experiments.js" +import PanelButton from "../PanelButton.js" + +const hyprland = await Service.import("hyprland") +const apps = await Service.import("applications") +const { monochrome, exclusive } = options.bar.taskbar +const { position } = options.bar + +const focus = (address) => hyprland.messageAsync( + `dispatch focuswindow address:${address}`) + +const DummyItem = (address) => Widget.Box({ + attribute: { address }, + visible: false, +}) + +const AppItem = (address) => { + 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: client.title, + on_primary_click: () => focus(address), + on_middle_click: () => app && launchApp(app), + visible: watch(true, [hyprland], () => { + return exclusive + ? hyprland.active.workspace.id === client.workspace.id + : true + }), + child: Widget.Icon({ + icon: icon( + (app?.icon_name || client.class) + (monochrome ? "-symbolic" : ""), + icons.fallback.executable, + ), + }), + }) + + return Widget.Box( + { attribute: { address } }, + Widget.Overlay({ + child: btn, + pass_through: true, + overlay: Widget.Box({ + className: "indicator", + hpack: "center", + vpack: (position === "top" ? "start" : "end"), + setup: w => w.hook(hyprland, () => { + w.toggleClassName("active", hyprland.active.client.address === address) + }), + }), + }), + ) +} + +function sortItems(arr) { + 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) => { + if (typeof address === "string") + w.children = w.children.filter(ch => ch.attribute.address !== address) + }, "client-removed") + .hook(hyprland, (w, address) => { + if (typeof address === "string") + w.children = sortItems([...w.children, AppItem(address)]) + }, "client-added") + .hook(hyprland, (w, event) => { + if (event === "movewindow") + w.children = sortItems(w.children) + }, "event"), +}) diff --git a/packages/ags/config/modules/bar/buttons/Workspaces.js b/packages/ags/config/modules/bar/buttons/Workspaces.js new file mode 100644 index 0000000..a3207f0 --- /dev/null +++ b/packages/ags/config/modules/bar/buttons/Workspaces.js @@ -0,0 +1,38 @@ +import PanelButton from "../PanelButton.js" +import options from "../../../options.js" +import { sh, range } from "../../../lib/utils.js" + +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 => self.hook(hyprland, () => { + self.toggleClassName("active", hyprland.active.workspace.id === i) + self.toggleClassName("occupied", (hyprland.getWorkspace(i)?.windows || 0) > 0) + }), + })), + setup: box => { + if (ws === 0) { + box.hook(hyprland.active.workspace, () => box.children.map(btn => { + btn.visible = hyprland.workspaces.some(ws => ws.id === btn.attribute) + })) + } + }, +}) + +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(workspaces), +}) diff --git a/packages/ags/config/modules/bar/screencorner.scss b/packages/ags/config/modules/bar/screencorner.scss new file mode 100644 index 0000000..d3b43a0 --- /dev/null +++ b/packages/ags/config/modules/bar/screencorner.scss @@ -0,0 +1,50 @@ +$_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 $shadow-color; + } + + @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 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/packages/ags/config/modules/datemenu/DateColumn.js b/packages/ags/config/modules/datemenu/DateColumn.js new file mode 100644 index 0000000..1fae685 --- /dev/null +++ b/packages/ags/config/modules/datemenu/DateColumn.js @@ -0,0 +1,38 @@ +import { uptime } from "../../lib/variables.js" +import clock from "../../services/clock.js" + +function up(up) { + 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('time').as(t => t.format("%H:%M")), + }), + Widget.Label({ + class_name: "uptime", + label: uptime.bind().as(up), + }), + ], + }), + Widget.Box({ + class_name: "calendar", + children: [ + Widget.Calendar({ + hexpand: true, + hpack: "center", + }), + ], + }), + ], +}) diff --git a/packages/ags/config/modules/datemenu/DateMenu.js b/packages/ags/config/modules/datemenu/DateMenu.js new file mode 100644 index 0000000..12cdfe2 --- /dev/null +++ b/packages/ags/config/modules/datemenu/DateMenu.js @@ -0,0 +1,33 @@ +import PopupWindow from "../PopupWindow.js" +import DateColumn from "./DateColumn.js" +import options from "../../options.js" + +const { bar, datemenu } = options +const pos = bar.position +const layout = `${bar.position}-${datemenu.position}` + +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 === "top" ? "slide_down" : "slide_up", + layout: layout, + child: Settings(), +}) + +export function setupDateMenu() { + App.addWindow(DateMenu()) + // layout.connect("changed", () => { + // App.removeWindow("datemenu") + // App.addWindow(DateMenu()) + // }) +} diff --git a/packages/ags/config/modules/dock/Dock.js b/packages/ags/config/modules/dock/Dock.js new file mode 100644 index 0000000..7e79fb1 --- /dev/null +++ b/packages/ags/config/modules/dock/Dock.js @@ -0,0 +1,31 @@ +// import Gtk from 'gi://Gtk'; + +/** @param {string} windowName */ +const Padding = windowName => Widget.EventBox({ + class_name: 'padding', + hexpand: true, + vexpand: true, + setup: w => w.on('button-press-event', () => App.toggleWindow(windowName)), +}); + +const Dock = () => { + return Widget.Box({ + name: 'dock-box', + child: Widget.Label({ + label: "Yo Yo YO YO bfbhgjhdfvbfb", + }) + }) +} + + +export default () => Widget.Window({ + name: 'dock', + anchor: ['bottom'], + exclusivity: 'normal', + keymode: 'on-demand', + layer: 'background', + // popup: true, + // monitor: 0, + visible: true, + child: Dock(), +}) diff --git a/packages/ags/config/modules/lock/Lockscreen.js b/packages/ags/config/modules/lock/Lockscreen.js new file mode 100644 index 0000000..e9de717 --- /dev/null +++ b/packages/ags/config/modules/lock/Lockscreen.js @@ -0,0 +1,156 @@ +import GLib from 'gi://GLib' + +const time = Widget.Label({ + name: 'lockscreen-time', + label: GLib.DateTime.new_now_local().format("%H:%M"), + setup: (self) => self.poll(1000, label => { + label.label = GLib.DateTime.new_now_local().format("%H:%M"); + }), + justification: 'left', + hexpand: true, + hpack: 'start', +}) + +const date = Widget.Label({ + name: 'lockscreen-date', + label: GLib.DateTime.new_now_local().format("%e, %B %A"), + setup: (self) => self.poll(60000, label => { + label.label = GLib.DateTime.new_now_local().format("%e, %B %A"); + }), + justification: 'left', +}) + +const network = await Service.import('network') +const battery = await Service.import('battery') + + +const Lockscreen = () => { + const WifiIndicator = () => Widget.Box({ + child: Widget.Icon({ + icon: network.wifi.bind('icon_name'), + }), + }) + + const WiredIndicator = () => Widget.Icon({ + icon: network.wired.bind('icon_name'), + }) + + const batPercent = Widget.Revealer({ + revealChild: false, + transitionDuration: 600, + transition: 'slide_left', + child: Widget.Label({ + name: "lockscreen-revealer", + label: battery.bind('percent').transform(p => "" + p + "%"), + }), + }) + + const batInfo = Widget.Box({ + "class-name": 'lockscreen-smolbox', + hexpand: false, + hpack: 'end', + children: [ + batPercent, + Widget.EventBox({ + child: Widget.Box({ + child: Widget.Label({ + label: battery.bind('percent').transform(p => ['', '', '', '', ''][Math.floor(p / 20)]), + class_name: 'lockscreen-battery', + hexpand: false, + setup: icon => icon.hook(battery, () => { + icon.toggleClassName('lockscreen-charging', battery.charging); + icon.toggleClassName('lockscreen-charged', battery.charged); + icon.toggleClassName('lockscreen-low', battery.percent < 30); + }), + }), + }), + onHover: () => { + batPercent.reveal_child = true; + print("revealed"); + }, + onHoverLost: () => { + batPercent.reveal_child = false; + } + }) + ] + }) + + const networkName = Widget.Revealer({ + revealChild: false, + transitionDuration: 600, + transition: 'slide_left', + child: Widget.Label({ + name: "lockscreen-revealer", + label: network.wifi.bind('ssid') + .as(ssid => ssid || 'Unknown'), + }), + }) + + const networkStatus = Widget.Box({ + class_name: "lockscreen-smolbox", + hexpand: false, + hpack: 'end', + children: [ + networkName, + Widget.EventBox({ + child: Widget.Stack({ + class_name: "lockscreen-network", + children: { + 'wifi': WifiIndicator(), + 'wired': WiredIndicator() + }, + shown: network.bind('primary').transform(p => p || 'wifi'), + }), + onHover: () => { + networkName.reveal_child = true; + }, + onHoverLost: () => { + networkName.reveal_child = false; + } + }) + ] + }) + + const boxRight = Widget.Box({ + homogeneous: false, + name: "lockscreen-boxRight", + vertical: true, + hexpand: false, + children: [networkStatus, batInfo], + vpack: 'end', + hpack: 'end', + }) + + const boxLeft = Widget.Box({ + homogeneous: false, + vertical: true, + children: [time, date], + vpack: 'end', + hpack: 'start', + }) + + return Widget.Box({ + children: [boxLeft, boxRight], + vertical: false, + }) +} + +function randomImage() { + const a = Utils.exec(`bash -c "ls ${App.configDir + '/modules/lock/images/'} | shuf -n 1"`); + const b = `background-image: url("${App.configDir + '/modules/lock/images/' + a}");`; + print(b); + return b; + +} + +export default () => Widget.Window({ + name: 'lockscreen', + css: randomImage(), + anchor: ['top', 'left', 'right', 'bottom'], + exclusivity: 'normal', + keymode: 'on-demand', + layer: 'top', + visible: false, + monitor: 0, + child: Lockscreen(), +}) diff --git a/packages/ags/config/modules/lock/images/astronautart.png b/packages/ags/config/modules/lock/images/astronautart.png new file mode 100644 index 0000000..5b0d5d7 Binary files /dev/null and b/packages/ags/config/modules/lock/images/astronautart.png differ diff --git a/packages/ags/config/modules/lock/images/cityevening.png b/packages/ags/config/modules/lock/images/cityevening.png new file mode 100644 index 0000000..97de3b7 Binary files /dev/null and b/packages/ags/config/modules/lock/images/cityevening.png differ diff --git a/packages/ags/config/modules/lock/images/dark-sand.jpg b/packages/ags/config/modules/lock/images/dark-sand.jpg new file mode 100644 index 0000000..da884e9 Binary files /dev/null and b/packages/ags/config/modules/lock/images/dark-sand.jpg differ diff --git a/packages/ags/config/modules/lock/images/something.png b/packages/ags/config/modules/lock/images/something.png new file mode 100644 index 0000000..ae4c761 Binary files /dev/null and b/packages/ags/config/modules/lock/images/something.png differ diff --git a/packages/ags/config/modules/lock/images/space.png b/packages/ags/config/modules/lock/images/space.png new file mode 100644 index 0000000..cb1d74f Binary files /dev/null and b/packages/ags/config/modules/lock/images/space.png differ diff --git a/packages/ags/config/modules/notifications/Notification.js b/packages/ags/config/modules/notifications/Notification.js new file mode 100644 index 0000000..cca1822 --- /dev/null +++ b/packages/ags/config/modules/notifications/Notification.js @@ -0,0 +1,139 @@ +import GLib from "gi://GLib" +import icons from "../../lib/icons.js" + +const time = (time, format = "%H:%M") => GLib.DateTime + .new_from_unix_local(time) + .format(format) + +const NotificationIcon = ({ app_entry, app_icon, image }) => { + 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) => { + 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", + hexpand: false, + vexpand: false, + 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/packages/ags/config/modules/notifications/NotificationPopups.js b/packages/ags/config/modules/notifications/NotificationPopups.js new file mode 100644 index 0000000..5a77308 --- /dev/null +++ b/packages/ags/config/modules/notifications/NotificationPopups.js @@ -0,0 +1,95 @@ +import Notification from "./Notification.js" +import options from "../../options.js" + +const notifications = await Service.import("notifications") +const { transitionDuration } = options +const { position, blacklist } = options.notifications +const { timeout, idle } = Utils + +function Animated(id) { + const n = notifications.getNotification(id) + const widget = Notification(n) + + const inner = Widget.Revealer({ + css: "border: 1px solid magenta;", + transition: "slide_left", + transition_duration: transitionDuration, + child: widget, + }) + + const outer = Widget.Revealer({ + css: "border: 1px solid yellow;", + transition: "slide_down", + transition_duration: transitionDuration, + child: inner, + }) + + const box = Widget.Box({ + hpack: "end", + child: outer, + }) + + idle(() => { + outer.reveal_child = true + timeout(transitionDuration, () => { + inner.reveal_child = true + }) + }) + + return Object.assign(box, { + dismiss() { + inner.reveal_child = false + timeout(transitionDuration, () => { + outer.reveal_child = false + timeout(transitionDuration, () => { + box.destroy() + }) + }) + }, + }) +} + +function PopupList() { + const map = new Map + const box = Widget.Box({ + hpack: "end", + vertical: true, + css: `min-width: ${options.notifications.width}px;`, + }) + + function remove(_, id) { + map.get(id)?.dismiss() + map.delete(id) + } + + return box + .hook(notifications, (_, id) => { + if (id !== undefined) { + if (map.has(id)) + remove(null, id) + + if (blacklist.includes(notifications.getNotification(id).app_name)) + return + + 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) => Widget.Window({ + monitor, + name: `notifications${monitor}`, + anchor: position, + class_name: "notifications", + child: Widget.Box({ + css: "padding: 2px;", + child: PopupList(), + }), +}) diff --git a/packages/ags/config/modules/osd/OSD.js b/packages/ags/config/modules/osd/OSD.js new file mode 100644 index 0000000..66279f7 --- /dev/null +++ b/packages/ags/config/modules/osd/OSD.js @@ -0,0 +1,111 @@ +import { icon } from "../../lib/utils.js" +import icons from "../../lib/icons.js" +import Progress from "./Progress.js" +import brightness from "../../services/brightness.js" +import options from "../../options.js" + +const audio = await Service.import("audio") +const { progress, microphone } = options.osd + +const DELAY = 2500 + +function OnScreenProgress(vertical) { + 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, icon) { + 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) => 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, + vpack: progress.pack.v, + child: OnScreenProgress(progress.vertical), + }), + Widget.Box({ + hpack: microphone.pack.h, + vpack: microphone.pack.v, + child: MicrophoneMute(), + }), + ), + }), +}) diff --git a/packages/ags/config/modules/osd/Progress.js b/packages/ags/config/modules/osd/Progress.js new file mode 100644 index 0000000..4e15b07 --- /dev/null +++ b/packages/ags/config/modules/osd/Progress.js @@ -0,0 +1,66 @@ +import GLib from "gi://GLib?version=2.0" +import { range } from "../../lib/utils.js" +import options from "../../options.js" + +export default ({ + height = 18, + width = 180, + vertical = false, + child, +}) => { + 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 = [] + + return Object.assign(container, { + setValue(value) { + 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 / 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/packages/ags/config/modules/overview/Overview.js b/packages/ags/config/modules/overview/Overview.js new file mode 100644 index 0000000..823e939 --- /dev/null +++ b/packages/ags/config/modules/overview/Overview.js @@ -0,0 +1,43 @@ +import PopupWindow from "../PopupWindow.js" +import Workspace from "./Workspace.js" +import options from "../../options.js" +import { range } from "../../lib/utils.js" + +const hyprland = await Service.import("hyprland") + +const Overview = (ws) => Widget.Box({ + class_name: "overview horizontal", + // spacing: 10, + 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) => { + if (id === undefined) + return + + w.children = w.children + .filter(ch => ch.attribute.id !== Number(id)) + }, "workspace-removed") + w.hook(hyprland, (w, id) => { + 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", + transition: "slide_down", + child: Overview(options.overview.workspaces), +}) diff --git a/packages/ags/config/modules/overview/Window.js b/packages/ags/config/modules/overview/Window.js new file mode 100644 index 0000000..1c0924b --- /dev/null +++ b/packages/ags/config/modules/overview/Window.js @@ -0,0 +1,51 @@ +import { createSurfaceFromWidget, icon } from "../../lib/utils.js" +import Gdk from "gi://Gdk" +import Gtk from "gi://Gtk?version=3.0" +import options from "../../options.js" +import icons from "../../lib/icons.js" + +const monochrome = options.overview.monochromeIcon +const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)] +const scale = (size) => (options.overview.scale / 100) * size +const hyprland = await Service.import("hyprland") +const apps = await Service.import("applications") +const dispatch = (args) => hyprland.messageAsync(`dispatch ${args}`) + + +const Icon = (m, c) => { + const app = apps.list.find(app => app.match(c)) + if (!app) + return icons.fallback.executable + + return icon( + app.icon_name + (m ? "-symbolic" : ""), + icons.fallback.executable, + ) +} + +export default ({ address, size: [w, h], class: c, title }) => Widget.Button({ + class_name: "client", + attribute: { address }, + tooltip_text: `${title}`, + child: Widget.Icon({ + class_name: "overview-icon", + css: ` + min-width: ${scale(w)}px; + min-height: ${scale(h)}px; + `, + icon: Icon(monochrome, c), + }), + 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/packages/ags/config/modules/overview/Workspace.js b/packages/ags/config/modules/overview/Workspace.js new file mode 100644 index 0000000..27bff76 --- /dev/null +++ b/packages/ags/config/modules/overview/Workspace.js @@ -0,0 +1,59 @@ +import Window from "./Window.js" +import Gdk from "gi://Gdk" +import Gtk from "gi://Gtk?version=3.0" +import options from "../../options.js" + +const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)] +const scale = (size) => (options.overview.scale / 100) * size +const hyprland = await Service.import("hyprland") + +const dispatch = (args) => hyprland.messageAsync(`dispatch ${args}`) + +const size = (id) => { + 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) => Widget.Box({ + attribute: { id }, + tooltipText: `${id}`, + class_name: "workspace", + vpack: "center", + css: ` + min-width: ${scale(size(id).w)}px; + min-height: ${scale(size(id).h)}px; + `, + setup: box => box.hook(hyprland, () => { + 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: Widget.Fixed().hook(hyprland, fixed => { + fixed.get_children().forEach(ch => ch.destroy()) + hyprland.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() + }, "notify::clients"), + }), +}) diff --git a/packages/ags/config/modules/powermenu/Powermenu.js b/packages/ags/config/modules/powermenu/Powermenu.js new file mode 100644 index 0000000..7d7a2b3 --- /dev/null +++ b/packages/ags/config/modules/powermenu/Powermenu.js @@ -0,0 +1,144 @@ +// import Gtk from 'gi://Gtk'; +import { uptime } from '../../lib/variables.js'; +import powermenu from '../../services/powermenu.js'; +import PopupWindow from "../PopupWindow.js"; + +const options = [ + { + label: "lock", + icon: "" + }, + { + label: "sleep", + icon: "" + }, + { + label: "logout", + icon: "" + }, + { + label: "shutdown", + icon: "" + }, + { + label: "reboot", + icon: "" + } +]; + +function buttonCreator(option, flag = 'none') { + const but = Widget.Button({ + className: 'powermenu-buttonBox', + child: Widget.Box({ + child: Widget.Label({ + className: 'powermenu-buttonLabel', + label: option['icon'], + }), + }), + onClicked: () => powermenu.action(option['label']),//Utils.execAsync(option['cmd']), + }) + if (flag == 'first') { + but.toggleClassName('powermenu-buttonBoxFirst', true); + } else if (flag == 'last') { + but.toggleClassName('powermenu-buttonBoxLast', true); + } + + return but; +} + +const Powermenu = () => { + + let powerbutton = Widget.Box({ + hpack: 'start', + vpack: 'start', + className: 'powermenu-powerbutton', + child: Widget.Label({ + label: "", + hpack: 'start', + }), + }) + + const topBox = Widget.Box({ + vertical: false, + homogeneous: false, + name: "powermenu-topbox", + css: `background-image: url('${App.configDir + '/modules/powermenu/imag2.png'}'); background-position: center;`, + spacing: 0, + children: [ + Widget.EventBox({ + vexpand: false, + vpack: 'start', + child: powerbutton, + onPrimaryClick: () => { + App.toggleWindow('powermenu'); + } + }), + Widget.Box({ + hpack: 'start', + vpack: 'start', + className: 'powermenu-username', + child: Widget.Label({ + hpack: 'start', + label: `${Utils.exec('whoami')}` + }) + }) + ] + }) + + const midBox = Widget.Box({ + hpack: 'fill', + className: 'powermenu-midbox', + child: Widget.Label({ + label: uptime.bind().as(value => 'Uptime : ' + Math.floor(value / 60) + ' hours ' + Math.floor(value % 60) + ' minutes'), + setup: self => self.hook(uptime, () => { + self.label = uptime.value.toString(); + }) + }) + }) + + let bottomBox = Widget.Box({ + vertical: false, + homogeneous: false, + name: 'powermenu-bottombox', + spacing: 20, + children: [ + buttonCreator(options[0], 'first'), + buttonCreator(options[1]), + buttonCreator(options[2]), + buttonCreator(options[3]), + buttonCreator(options[4], 'last'), + ], + }) + + let motherBox = Widget.Box({ + homogeneous: false, + vertical: true, + name: 'powermenu-motherbox', + // hexpand: true, + // vexpand: false, + hpack: 'center', + vpack: 'center', + children: [topBox, midBox, bottomBox], + }) + + return motherBox; +} + +export default () => PopupWindow({ + name: 'powermenu', + transition: "slide_down", + child: Powermenu(), +}) + + +// export default () => Widget.Window({ +// name: 'powermenu', +// anchor: ['top', 'left', 'right', 'bottom'], +// exclusivity: 'normal', +// keymode: 'exclusive', +// layer: 'top', +// popup: true, +// // monitor: 0, +// visible: false, +// child: Powermenu(), +// }) diff --git a/packages/ags/config/modules/powermenu/Verification.js b/packages/ags/config/modules/powermenu/Verification.js new file mode 100644 index 0000000..e62acdd --- /dev/null +++ b/packages/ags/config/modules/powermenu/Verification.js @@ -0,0 +1,70 @@ + +import PopupWindow from '../PopupWindow.js'; +import powermenu from '../../services/powermenu.js'; + +export default () => PopupWindow({ + name: "verification", + transition: "crossfade", + layout: 'center', + child: Widget.Box({ + class_name: "verification", + vertical: true, + homogeneous: false, + children: [ + Widget.Box({ + class_name: "verification-label", + vertical: true, + children: [ + Widget.Label({ + class_name: "desc", + label: powermenu.bind("title"), + }), + ], + }), + Widget.Box({ + vexpand: true, + vpack: "end", + spacing: 20, + homogeneous: false, + children: [ + Widget.Button({ + class_name: "verification-buttonBox verification-buttonBoxFirst", + child: Widget.Label({ + label: "No", + hpack: "center", + vpack: "center", + }), + on_clicked: () => App.toggleWindow("verification"), + setup: self => self.hook(App, (_, name, visible) => { + if (name === "verification" && visible) + self.grab_focus() + }), + }), + Widget.Button({ + class_name: "verification-buttonBox verification-buttonBoxLast", + child: Widget.Label({ + label: "Yes", + hpack: "center", + vpack: "center", + }), + on_clicked: () => Utils.exec(powermenu.cmd), + }), + ], + }), + ], + }), +}) + + +// export default () => Widget.Window({ +// name: 'verification', +// // transition: 'crossfade', +// anchor: ['top', 'left', 'right', 'bottom'], +// exclusivity: 'normal', +// keymode: 'exclusive', +// layer: 'top', +// popup: true, +// // monitor: 0, +// visible: false, +// child: Verification(), +// }) diff --git a/packages/ags/config/modules/powermenu/imag2.png b/packages/ags/config/modules/powermenu/imag2.png new file mode 100644 index 0000000..dc030f2 Binary files /dev/null and b/packages/ags/config/modules/powermenu/imag2.png differ diff --git a/packages/ags/config/modules/powermenu/image.png b/packages/ags/config/modules/powermenu/image.png new file mode 100644 index 0000000..65d14f7 Binary files /dev/null and b/packages/ags/config/modules/powermenu/image.png differ diff --git a/packages/ags/config/modules/quicksettings/NotificationColumn.js b/packages/ags/config/modules/quicksettings/NotificationColumn.js new file mode 100644 index 0000000..0812685 --- /dev/null +++ b/packages/ags/config/modules/quicksettings/NotificationColumn.js @@ -0,0 +1,117 @@ +import Notification from "../notifications/Notification.js" +import options from "../../options.js" +import icons from "../../lib/icons.js" + +const notifications = await Service.import("notifications") +const notifs = notifications.bind("notifications") + +const Animated = (n) => Widget.Revealer({ + transition_duration: options.transition, + transition: "slide_down", + child: Notification(n), + setup: self => Utils.timeout(options.transition, () => { + 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 = 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(_, id) { + const n = map.get(id) + if (n) { + n.reveal_child = false + Utils.timeout(options.transition, () => { + n.destroy() + map.delete(id) + }) + } + } + + return box + .hook(notifications, remove, "closed") + .hook(notifications, (_, id) => { + 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: `min-width: ${options.notifications.width}px`, + // vpack: "fill", + vexpand: true, + vertical: true, + children: [ + Header(), + Widget.Scrollable({ + vexpand: true, + // vpack: "fill", + hscroll: "never", + class_name: "notification-scrollable", + child: Widget.Box({ + class_name: "notification-list vertical", + vertical: true, + vexpand: true, + // vpack: "fill", + // vexpand: true, + children: [ + NotificationList(), + Placeholder(), + ], + }), + }), + ], +}) diff --git a/packages/ags/config/modules/quicksettings/QuickSettings.js b/packages/ags/config/modules/quicksettings/QuickSettings.js new file mode 100644 index 0000000..e55755f --- /dev/null +++ b/packages/ags/config/modules/quicksettings/QuickSettings.js @@ -0,0 +1,87 @@ +import { ProfileSelector, ProfileToggle } from "./widgets/AsusProfile.js" +// import { Header } from "./widgets/Header.js" +import { Volume, Microhone, SinkSelector, AppMixer } from "./widgets/Volume.js" +import { Brightness } from "./widgets/Brightness.js" +import { NetworkToggle, WifiSelection } from "./widgets/Network.js" +import { BluetoothToggle, BluetoothDevices } from "./widgets/Bluetooth.js" +import { DND } from "./widgets/DND.js" +// import { DarkModeToggle } from "./widgets/DarkMode.js" +import { MicMute } from "./widgets/MicMute.js" +import { Media } from "./widgets/Media.js" +import PopupWindow from "../PopupWindow.js" +import NotificationColumn from "./NotificationColumn.js" +import options from "../../options.js" + +const { bar, quicksettings } = options +const media = (await Service.import("mpris")).bind("players") +const layout = `${quicksettings.position}` + + +const Row = ( + toggles = [], + menus = [], +) => 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, + vexpand: true, + vpack: "fill", + class_name: "quicksettings vertical", + css: `min-width: ${quicksettings.width}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], + [ProfileSelector], + ), + Row([MicMute, DND]), + Widget.Box({ + visible: media.as(l => l.length > 0), + child: Media(), + }), + NotificationColumn(), + ], +}) + +const QuickSettings = () => PopupWindow({ + name: "quicksettings", + transition: "slide_left", //bar.position === "top" ? "slide_down" : "slide_up", + // anchor: ["right", "top"], + keymode: 'on-demand', + layout: layout, + child: Settings(), + vpack: "fill", + // hpack: "fill", + vexpand: true, + // hexpand: true, +}) + +export function setupQuickSettings() { + App.addWindow(QuickSettings()) +} diff --git a/packages/ags/config/modules/quicksettings/ToggleButton.js b/packages/ags/config/modules/quicksettings/ToggleButton.js new file mode 100644 index 0000000..43cb7c9 --- /dev/null +++ b/packages/ags/config/modules/quicksettings/ToggleButton.js @@ -0,0 +1,129 @@ +import icons from "../../lib/icons.js" + +export const opened = Variable("") +App.connect("window-toggled", (_, name, visible) => { + if (name === "quicksettings" && !visible) + Utils.timeout(500, () => opened.value = "") +}) + +export const Arrow = (name, activate) => { + 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() + }, + }) +} + +export const ArrowToggleButton = ({ + name, + icon, + label, + activate, + deactivate, + activateOnArrow = true, + connection: [service, condition], +}) => 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), + ], +}) + +export const Menu = ({ name, icon, title, content }) => 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, + }), + ], + }), +}) + +export const SimpleToggleButton = ({ + icon, + label, + toggle, + connection: [service, condition], +}) => 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/packages/ags/config/modules/quicksettings/widgets/AsusProfile.js b/packages/ags/config/modules/quicksettings/widgets/AsusProfile.js new file mode 100644 index 0000000..b79bc90 --- /dev/null +++ b/packages/ags/config/modules/quicksettings/widgets/AsusProfile.js @@ -0,0 +1,51 @@ +import { ArrowToggleButton, Menu } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" +import asusctl from "../../../services/asusctl.js" + +const profile = asusctl.bind("profile") + +export const ProfileToggle = () => ArrowToggleButton({ + name: "asusctl-profile", + icon: profile.as(p => icons.asusctl.profile[p]), + label: profile, + connection: [asusctl, () => asusctl.profile !== "Balanced"], + activate: () => asusctl.setProfile("Quiet"), + deactivate: () => asusctl.setProfile("Balanced"), + activateOnArrow: false, +}) + +export const ProfileSelector = () => Menu({ + name: "asusctl-profile", + icon: profile.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"), + ], + }), + }), + ], +}) diff --git a/packages/ags/config/modules/quicksettings/widgets/Bluetooth.js b/packages/ags/config/modules/quicksettings/widgets/Bluetooth.js new file mode 100644 index 0000000..44a0e8a --- /dev/null +++ b/packages/ags/config/modules/quicksettings/widgets/Bluetooth.js @@ -0,0 +1,62 @@ +import { Menu, ArrowToggleButton } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" +import { watch } from "../../../lib/experiments.js" + +const bluetooth = await Service.import("bluetooth") +const title = 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` +}) + +export const BluetoothToggle = () => ArrowToggleButton({ + name: "bluetooth", + icon: bluetooth.bind("enabled").as(p => icons.bluetooth[p ? "enabled" : "disabled"]), + label: title, + connection: [bluetooth, () => bluetooth.enabled], + deactivate: () => bluetooth.enabled = false, + activate: () => bluetooth.enabled = true, +}) + +const DeviceItem = (device) => 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/packages/ags/config/modules/quicksettings/widgets/Brightness.js b/packages/ags/config/modules/quicksettings/widgets/Brightness.js new file mode 100644 index 0000000..29bdf77 --- /dev/null +++ b/packages/ags/config/modules/quicksettings/widgets/Brightness.js @@ -0,0 +1,23 @@ +import icons from "../../../lib/icons.js" +import brightness from "../../../services/brightness.js" + +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/packages/ags/config/modules/quicksettings/widgets/DND.js b/packages/ags/config/modules/quicksettings/widgets/DND.js new file mode 100644 index 0000000..b5d86af --- /dev/null +++ b/packages/ags/config/modules/quicksettings/widgets/DND.js @@ -0,0 +1,12 @@ +import { SimpleToggleButton } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" + +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/packages/ags/config/modules/quicksettings/widgets/DarkMode.js b/packages/ags/config/modules/quicksettings/widgets/DarkMode.js new file mode 100644 index 0000000..4a9a9ad --- /dev/null +++ b/packages/ags/config/modules/quicksettings/widgets/DarkMode.js @@ -0,0 +1,10 @@ +import { SimpleToggleButton } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" + +let scheme = "dark" + +export const DarkModeToggle = () => SimpleToggleButton({ + icon: icons.color[scheme], + label: scheme === "dark" ? "Dark" : "Light", + toggle: () => scheme = scheme === "dark" ? "light" : "dark", +}) diff --git a/packages/ags/config/modules/quicksettings/widgets/Header.js b/packages/ags/config/modules/quicksettings/widgets/Header.js new file mode 100644 index 0000000..63a4327 --- /dev/null +++ b/packages/ags/config/modules/quicksettings/widgets/Header.js @@ -0,0 +1,60 @@ +import icons from "../../../lib/icons.js" +import { uptime } from "../../../lib/variables.js" +import options from "../../../options.js" +import powermenu from "../../../services/powermenu.js" + +const battery = await Service.import("battery") +const { image, size } = options.quicksettings.avatar + +function up(up) { + 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: ` + min-width: ${size}px; + min-height: ${size}px; + background-image: url('${image}'); + background-size: cover; + `, +}) + +const SysButton = (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([ + 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.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/packages/ags/config/modules/quicksettings/widgets/Media.js b/packages/ags/config/modules/quicksettings/widgets/Media.js new file mode 100644 index 0000000..be90edd --- /dev/null +++ b/packages/ags/config/modules/quicksettings/widgets/Media.js @@ -0,0 +1,148 @@ +import icons from "../../../lib/icons.js" +import options from "../../../options.js" +import { icon } from "../../../lib/utils.js" + +const mpris = await Service.import("mpris") +const players = mpris.bind("players") +const { media } = options.quicksettings + +function lengthStr(length) { + const min = Math.floor(length / 60) + const sec = Math.floor(length % 60) + const sec0 = sec < 10 ? "0" : "" + return `${min}:${sec0}${sec}` +} + +const Player = (player) => { + const cover = Widget.Box({ + class_name: "cover", + vpack: "start", + css: player.bind("cover_path").as(path => ` + min-width: ${media.coverSize}px; + min-height: ${media.coverSize}px; + background-image: url('${path}'); + `), + }) + + 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 = (_, time) => { + 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: player.bind("entry").as(e => { + const name = `${e}${media.monochromeIcon ? "-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/packages/ags/config/modules/quicksettings/widgets/MicMute.js b/packages/ags/config/modules/quicksettings/widgets/MicMute.js new file mode 100644 index 0000000..61be3ce --- /dev/null +++ b/packages/ags/config/modules/quicksettings/widgets/MicMute.js @@ -0,0 +1,26 @@ +import { SimpleToggleButton } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" +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" + +// TODO: Variable watch option +const ico = Variable(icon()) +microphone.connect("changed", () => ico.value = icon()) + +// TODO: Variable watch option +const lbl = Variable(label()) +microphone.connect("changed", () => lbl.value = label()) + +export const MicMute = () => SimpleToggleButton({ + icon: ico.bind(), + label: lbl.bind(), + toggle: () => microphone.is_muted = !microphone.is_muted, + connection: [microphone, () => microphone?.is_muted || false], +}) diff --git a/packages/ags/config/modules/quicksettings/widgets/Network.js b/packages/ags/config/modules/quicksettings/widgets/Network.js new file mode 100644 index 0000000..e94d245 --- /dev/null +++ b/packages/ags/config/modules/quicksettings/widgets/Network.js @@ -0,0 +1,61 @@ +import { Menu, ArrowToggleButton } from "../ToggleButton.js" +import icons from "../../../lib/icons.js" +import { dependencies, sh } from "../../../lib/utils.js" +import options from "../../../options.js" +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), + child: Widget.Box({ + children: [ + Widget.Icon(icons.ui.settings), + Widget.Label("Network"), + ], + }), + }), + ], +}) diff --git a/packages/ags/config/modules/quicksettings/widgets/Volume.js b/packages/ags/config/modules/quicksettings/widgets/Volume.js new file mode 100644 index 0000000..57da0b4 --- /dev/null +++ b/packages/ags/config/modules/quicksettings/widgets/Volume.js @@ -0,0 +1,141 @@ +import { Arrow, Menu } from "../ToggleButton.js" +import { dependencies, icon, sh } from "../../../lib/utils.js" +import icons from "../../../lib/icons.js" +const audio = await Service.import("audio") + +const VolumeIndicator = (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.mic.high)), + tooltipText: audio[type].bind("volume") + .as(vol => `Volume: ${Math.floor(vol * 100)}%`), + }), +}) + +const VolumeSlider = (type = "speaker") => Widget.Slider({ + hexpand: true, + draw_value: false, + on_change: ({ value, dragging }) => dragging && (audio[type].volume = value), + value: audio[type].bind("volume"), +}) + +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: [ + VolumeIndicator("microphone"), + VolumeSlider("microphone"), + ], +}) + +const MixerItem = (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) => 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/packages/ags/config/options.js b/packages/ags/config/options.js new file mode 100644 index 0000000..2a0374f --- /dev/null +++ b/packages/ags/config/options.js @@ -0,0 +1,139 @@ + +const options = { + recompileSass: false, + monitorCSS: false, + theme: { + PopupTransitionDuration: 400, + PopupCloseDuration: 300, + }, + powermenu: { + sleep: 'systemctl suspend', + lock: 'waylock', + logout: 'hyprctl dispatch exit', + shutdown: 'shutdown now', + reboot: 'reboot', + }, + overview: { + scale: 9, + workspaces: 8, + monochromeIcon: false, + }, + notifications: { + position: ["top", "right"], + blacklist: ["Spotify"], + width: 440, + }, + transitionDuration: 200, + transition: 200, + + bar: { + flatButtons: true, + position: "bottom", + corners: true, + layout: { + start: [ + "launcher", + "workspaces", + "taskbar", + "expander", + // "messages", + ], + center: [ + "date", + ], + end: [ + "media", + "expander", + "systray", + // "colorpicker", + // "screenrecord", + "system", + "battery", + "powermenu", + ], + }, + launcher: { + icon: { + colored: true, + icon: "system-search-symbolic", + }, + label: { + colored: false, + label: " Rofi", // Text that appears with the search icon + }, + action: () => Utils.execAsync('bash -c "pkill rofi || rofi -show drun"'), + }, + date: { + format: "%H:%M - %A %e.", + action: () => App.toggleWindow("datemenu"), + }, + battery: { + bar: "regular", + charging: "#00D787", + percentage: true, + blocks: 10, + width: 70, + low: 30, + }, + workspaces: { + workspaces: 7, + }, + taskbar: { + monochrome: false, + exclusive: false, + }, + messages: { + action: () => App.toggleWindow("datemenu"), + }, + systray: { + ignore: [ + "KDE Connect Indicator", + "spotify-client", + ], + }, + media: { + monochrome: false, + preferred: "spotify", + direction: "right", + length: 40, + }, + powermenu: { + monochrome: false, + action: () => App.toggleWindow("powermenu"), + }, + }, + osd: { + progress: { + vertical: false, + pack: { + h: "center", + v: "end", + }, + }, + microphone: { + pack: { + h: "center", + v: "center", + }, + }, + }, + datemenu: { + position: "center", + }, + + quicksettings: { + avatar: { + image: `/var/lib/AccountsService/icons/${Utils.USER}`, + size: 70, + }, + width: 380, + position: "right", + networkSettings: "gtk-launch gnome-control-center", + media: { + monochromeIcon: false, + coverSize: 100, + }, + }, +}; + +export default options; diff --git a/packages/ags/config/scss/_bar.scss b/packages/ags/config/scss/_bar.scss new file mode 100644 index 0000000..3e6b179 --- /dev/null +++ b/packages/ags/config/scss/_bar.scss @@ -0,0 +1,337 @@ +$radius: 11px; +$bar-battery-blocks: 10; +$button-radius: 10px; +$bg: #171717; +$primary-bg: $blue; +$primary-fg: $mantle; +$charging-bg: $green; +$error-bg: $red; + +.bar { + background-color: $crust; + + .panel-button { + all: unset; + color: $text; + + >* { + border-radius: 10px; + transition: 200ms; + background-color: transparent; + box-shadow: none; + } + + &:focus>*, + &.focused>* { + box-shadow: inset 0 0 0 1px $blue; + background-color: transparentize($subtext1, 94px *.9 / 100); + color: lighten($subtext1, 8%); + } + + &:hover>* { + box-shadow: inset 0 0 0 1px #080808; + background-color: transparentize($subtext1, 94px *.9 / 100); + color: lighten($subtext1, 8%); + } + + &:active, + &.active, + &.on, + &:checked { + >* { + box-shadow: inset 0 0 0 1px #080808; + background-image: linear-gradient(to right, $mantle , darken($mantle, 4%)); + background-color: $blue; + color: $mantle; + } + + &:hover>* { + box-shadow: inset 0 0 0 1px $blue, + inset 0 0 0 99px transparentize($subtext1, 94px *.9 / 100); + } + } + + >* { + border-radius: 10px; + margin: 4px; + padding: 5px 8px; + font-size: 20px; + } + + label, + image { + font-weight: bold; + } + + + &:not(.flat) { + // all: unset; + color: $text; + + >* { + border-radius: 10px; + transition: 200ms; + } + + &:focus>*, + &.focused>* { + box-shadow: inset 0 0 0 1px $blue; + background-color: transparentize($subtext1, 94px *.9 / 100); + color: lighten($subtext1, 8%); + } + + &:hover>* { + box-shadow: inset 0 0 0 1px #080808; + background-color: transparentize($subtext1, 94px *.9 / 100); + color: lighten($subtext1, 8%); + } + + &:active, + &.active, + &.on, + &:checked { + >* { + box-shadow: inset 0 0 0 1px #080808; + background-image: linear-gradient(to right, $mantle , darken($mantle, 4%)); + background-color: $blue; + color: $mantle; + } + + &:hover>* { + box-shadow: inset 0 0 0 1px $blue, + inset 0 0 0 99px transparentize($subtext1, 94px *.9 / 100); + } + } + } + } + + .launcher { + .colored { + color: transparentize($blue, 0.2); + } + + &:hover .colored { + color: $blue; + } + + &:active .colored, + &.active .colored { + color: $blue; + } + } + + .workspaces { + label { + font-size: 0; + min-width: 5pt; + min-height: 5pt; + border-radius: $radius*.6; + box-shadow: inset 0 0 0 1px transparent; + margin: 0 7px*.5; + transition: 100ms* .5; + background-color: transparentize($text, .8); + + &.occupied { + background-color: transparentize($text, .2); + min-width: 7pt; + min-height: 7pt; + // box-shadow: inset 0 0 0 1px $blue; + // box-shadow: inset 0 0 0 1px transparent; + } + + &.active { + // background-color: $primary-bg; + // box-shadow: inset 0 0 0 1px transparent; + box-shadow: inset 0 0 0 1px $blue; + background-image: linear-gradient(to right, $mantle , darken($mantle, 4%)); + min-width: 20pt; + min-height: 12pt; + } + } + + &.active, + &:active { + label { + background-color: transparentize($blue, .3); + + &.occupied { + background-color: transparentize($blue, .15); + } + + &.active { + background-color: $blue; + } + } + } + } + + .media label { + margin: 0 (12px * .5) + } + + .taskbar .indicator.active { + background-color: $blue; + border-radius: $radius; + min-height: 4pt; + min-width: 6pt; + margin: 2pt; + } + + .powermenu.colored, + .recorder { + image { + color: transparentize($red, 0.3); + } + + &:hover image { + color: transparentize($red, 0.15); + } + + &:active image { + color: $mantle; + } + } + + .quicksettings>box>box { + &.horizontal>* { + margin: 0 calc(12px * 0.3 / 2); + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: calc(12px * 0.3 / 2) 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + } + + .quicksettings:not(.active):not(:active) { + .bluetooth { + color: $blue; + + label { + font-size: 13px * .7; + color: $text; + text-shadow: 2pt 2pt 2pt rgba(0, 0, 0, .6); + } + } + } + + .battery-bar { + >* { + padding: 0; + min-height: 10px; + } + + &.bar-hidden>box { + padding: 0 12px* .5; + + image { + margin: 0; + } + } + + levelbar * { + all: unset; + transition: 200ms; + } + + .whole { + image { + -gtk-icon-shadow: 2pt 2pt 2pt rgba(0, 0, 0, .6); + } + + label { + text-shadow: 2pt 2pt 2pt rgba(0, 0, 0, .6); + } + } + + .regular image { + margin-left: 12px * .5; + } + + trough { + transition: 200ms; + border-radius: $radius; + color: $mantle; + background-color: $base; + // border: 1px solid $surface0; + min-height: 5px; + min-width: 140px; + padding: 0px; + margin: 7px; + } + + .regular trough { + margin-right: 5px; + } + + 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: $primary-bg + } + + &.low block:nth-child(#{$i}).filled { + background-color: $error-bg + } + + &.charging block:nth-child(#{$i}).filled { + background-color: $charging-bg + } + + &:active .regular block:nth-child(#{$i}).filled { + background-color: $primary-fg + } + } + + &.low image { + color: $error-bg + } + + &.charging image { + color: $charging-bg + } + + &:active image { + color: $primary-fg + } + } +} diff --git a/packages/ags/config/scss/_datemenu.scss b/packages/ags/config/scss/_datemenu.scss new file mode 100644 index 0000000..e95c11c --- /dev/null +++ b/packages/ags/config/scss/_datemenu.scss @@ -0,0 +1,340 @@ +@import './notifications'; + +@mixin calendar { + transition: 200ms; + border-radius: 18px; + color: $text; + background-color: $mantle; + border: 2px solid $blue; + padding: 8px 8px 0px; + + calendar { + all: unset; + + &.button { + all: unset; + transition: 200ms; + border-radius: 6px; + color: $text; + background-color: transparent; + background-image: none; + box-shadow: none; + + &:focus { + box-shadow: inset 0 0 0 2px #0e0e0e; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:hover { + box-shadow: inset 0 0 0 2px $blue; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:active, + &.on, + &.active, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + } + + &:selected { + box-shadow: inset 0 -8px 0 0 transparentize($green, 0.5), + inset 0 0 0 1px $green; + border-radius: 8px; + color: $blue; + } + + &.header { + background-color: transparent; + border: none; + color: transparentize($text, 0.5); + } + + &.highlight { + background-color: transparent; + color: transparentize($blue, 0.0); + } + + &:indeterminate { + color: transparentize($text, 0.8); + } + + font-size: 1.4em; + padding: .2em; + } +} + +window#datemenu .datemenu { + box-shadow: 0 0 5px 0 #0e0e0e; + + margin: 9px; + border: 2px solid $blue; + background-color: $base; + color: $text; + border-radius: 26px; + padding: 8px; + + .notifications { + .header { + margin-bottom: 30px; + margin-right: 30px; + + >label { + margin-left: 15px; + } + + button { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + + background-color: #FFFFFF; + box-shadow: inset 0 0 0 2px $blue; + + &:focus { + box-shadow: inset 0 0 0 2px #0e0e0e; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:hover { + box-shadow: inset 0 0 0 2px $blue; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:active, + &.on, + &.active, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($mantle, 0.7); + } + + padding: 10px; + } + } + + .notification-scrollable { + + scrollbar, + scrollbar * { + all: unset; + } + + scrollbar.vertical { + transition: 200ms; + background-color: transparentize($base, 0.7); + + &:hover { + background-color: transparentize($base, 0.3); + + slider { + background-color: transparentize($text, 0.3); + min-width: .6em; + } + } + } + + + scrollbar.vertical slider { + background-color: transparentize($text, 0.5); + border-radius: 10px; + min-width: .4em; + min-height: 2em; + transition: 200ms; + } + } + + .notification-list { + margin-right: 10px; + } + + .notification { + &.critical { + box-shadow: inset 0 0 .5em 0 $red; + } + + &:hover button.close-button { + // @include button-hover; + background-color: transparentize($red, .5); + } + + .content { + .title { + margin-top: 5px; + margin-right: 10px; + color: $text; + font-size: 1.2em; + } + + .time { + color: transparentize($text, .2); + margin: 5px 10px; + } + + .description { + font-size: .9em; + color: transparentize($text, .2); + } + + .icon { + border-radius: 10px; + margin-right: 10px; + + &.img { + border: 2px solid $green; + } + } + } + + box.actions { + // @include spacing(0.5); + margin-top: 10px; + + button { + // @include button; + background-color: $surface0; + border: 2px solid $surface0; + border-radius: 10px; + font-size: 1.2em; + margin: 0px 10px 10px 0px; + padding: 10px; + + &:first-child { + margin: 0px 10px 10px 10px; + } + + &:hover { + background-color: $surface2; + border: 2px solid $rosewater; + } + } + } + + button.close-button { + // @include button($flat: true); + margin: 0px 0px 0px 0px; + border-radius: 10px; + min-width: 30px; + min-height: 30px; + background-color: rgba(0, 0, 0, 0); //$crust; + padding: 0px; + + &:hover { + background-color: transparentize($red, .2); + } + + &:active { + background-image: none; + background-color: $red; + } + } + + transition: 200ms; + color: $text; + margin-bottom: 10px; + border-radius: 15px; + background-color: $base; + font-family: "Iosevka"; + padding: 5px; + border: 2px solid $rosewater; + box-shadow: rgba(0, 0, 0, 0.4) 5px 5px 5px; + } + + .placeholder { + image { + font-size: 7em; + } + + label { + font-size: 1.2em; + } + } + } + + + separator { + background-color: $mantle; + min-width: 3px; + border-radius: 100px; + margin-right: 10px; + } + + .datemenu { + &.horizontal>* { + margin: 0 10px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 10px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + } + + .clock-box { + padding: 10px; + + .clock { + font-size: 6em; + } + + .uptime { + font-family: "Iosevka"; + font-size: 18px; + color: transparentize($text, 0.2); + } + } + + .calendar { + font-family: "Iosevka"; + font-size: 18px; + @include calendar; + } +} diff --git a/packages/ags/config/scss/_dock.scss b/packages/ags/config/scss/_dock.scss new file mode 100644 index 0000000..a66f84f --- /dev/null +++ b/packages/ags/config/scss/_dock.scss @@ -0,0 +1,9 @@ +#dock * { + font-family: 'Iosevka'; +} + +#dock-box { + min-height: 10px; + min-width: 100px; + background-color: $base; +} diff --git a/packages/ags/config/scss/_globals.scss b/packages/ags/config/scss/_globals.scss new file mode 100644 index 0000000..8bc4ef1 --- /dev/null +++ b/packages/ags/config/scss/_globals.scss @@ -0,0 +1 @@ +// Trying yo put some global variables diff --git a/packages/ags/config/scss/_lockscreen.scss b/packages/ags/config/scss/_lockscreen.scss new file mode 100644 index 0000000..4946be1 --- /dev/null +++ b/packages/ags/config/scss/_lockscreen.scss @@ -0,0 +1,76 @@ +#lockscreen * { + // all:unset; + font-family: "SF Pro Display"; +} + +/* #lockscreen { */ +/* background-image: url("/home/hooman/gitjargan/wallpapers/0012.jpg"); */ +/* } */ + +#lockscreen-time { + color: #b8bcc2; + font-size: 12.5rem; + margin-bottom: 2.5rem; +} + +#lockscreen-date { + color: #b8bcc2; + font-size: 3.75rem; + margin-bottom: 6rem; +} + +#lockscreen-time, #lockscreen-date { + margin-left: 7.8125rem; +} + +.lockscreen-smolbox { + background-color: #172030; + border-radius: 0.5rem; + min-height: 4.375rem; + min-width: 4.375rem; + margin-right: 5rem; + margin-bottom: 2.5rem; + font-size: 2rem; + + label { + min-height: 4.375rem; + min-width: 4.375rem; + color: #b8bcc2; + } + + &:last-child { + margin-bottom: 5rem; + } +} + +#lockscreen-revealer { + padding-left: 1rem; +} + +.lockscreen-charged { + color: #6b7cdb; +} + +.lockscreen-low { + color: #c45e62; +} + +.lockscreen-charging { + color: #62bf81; +} +.lockscreen-battery { + padding-right: 0; + margin-left: -0.5625rem; + margin-bottom: -0.4375rem; + margin-right: 0.5625rem; + padding-top: 0; +} + +.lockscreen-network { + padding-left: 1.25rem; + margin-right: 1.125rem; +} + +#lockscreen-boxRight { + min-width: 4.375rem; +} diff --git a/packages/ags/config/scss/_mocha.scss b/packages/ags/config/scss/_mocha.scss new file mode 100644 index 0000000..728949d --- /dev/null +++ b/packages/ags/config/scss/_mocha.scss @@ -0,0 +1,26 @@ +$rosewater: #f5e0dc; +$flamingo: #f2cdcd; +$pink: #f5c2e7; +$mauve: #cba6f7; +$red: #f38ba8; +$maroon: #eba0ac; +$peach: #fab387; +$yellow: #f9e2af; +$green: #a6e3a1; +$teal: #94e2d5; +$sky: #89dceb; +$sapphire: #74c7ec; +$blue: #89b4fa; +$lavender: #b4befe; +$text: #cdd6f4; +$subtext1: #bac2de; +$subtext0: #a6adc8; +$overlay2: #9399b2; +$overlay1: #7f849c; +$overlay0: #6c7086; +$surface2: #585b70; +$surface1: #45475a; +$surface0: #313244; +$base: #1e1e2e; +$mantle: #181825; +$crust: #11111b; diff --git a/packages/ags/config/scss/_notifications.scss b/packages/ags/config/scss/_notifications.scss new file mode 100644 index 0000000..31e0889 --- /dev/null +++ b/packages/ags/config/scss/_notifications.scss @@ -0,0 +1,96 @@ +window.notifications { + .notification { + border-radius: 15px; + background-color: $base; + margin: 25px 25px 20px 0px; + font-family: "Iosevka"; + padding: 5px; + // border: 2px solid $rosewater; + box-shadow: rgba(0, 0, 0, 0.4) 5px 5px 5px; + + &.critical { + box-shadow: inset 0 0 .5em 0 $red; + } + + &:hover button.close-button { + // @include button-hover; + background-color: transparentize($red, .5); + } + + .content { + .title { + margin-top: 5px; + margin-right: 10px; + color: $text; + font-size: 1.2em; + } + + .time { + color: transparentize($text, .2); + margin: 5px 10px; + } + + .description { + font-size: .9em; + color: transparentize($text, .2); + } + + .icon { + border-radius: 10px; + margin-right: 10px; + + &.img { + border: 2px solid $green; + } + } + } + + box.actions { + // @include spacing(0.5); + margin-top: 10px; + + button { + // @include button; + background-color: $surface0; + border: 2px solid $surface0; + border-radius: 10px; + font-size: 1.2em; + margin: 0px 10px 10px 0px; + padding: 10px; + + &:first-child { + margin: 0px 10px 10px 10px; + } + + &:hover { + background-color: $surface2; + border: 2px solid $rosewater; + } + } + } + + button.close-button { + // @include button($flat: true); + margin: 0px 0px 0px 0px; + border-radius: 10px; + min-width: 30px; + min-height: 30px; + background-color: rgba(0, 0, 0, 0); //$crust; + padding: 0px; + + &:hover { + background-color: transparentize($red, .5); + } + + &:active { + background-image: none; + background-color: $red; + } + } + + + .description { + min-width: 350px; + } + } +} diff --git a/packages/ags/config/scss/_osd.scss b/packages/ags/config/scss/_osd.scss new file mode 100644 index 0000000..1a75a8f --- /dev/null +++ b/packages/ags/config/scss/_osd.scss @@ -0,0 +1,35 @@ +$shadow-color: #0e0e0e; + +window.indicator { + .progress { + box-shadow: 0 0 5px 0 $shadow-color; + margin: 15px; + border: 2px solid $blue; + background-color: $base; + color: $text; + padding: 5px; + border-radius: 15px; + + .fill { + border-radius: 10px; + background-color: $blue; + color: $crust; + + image { + -gtk-icon-transform: scale(0.7); + } + } + } + + .microphone { + box-shadow: 0 0 5px 0 $shadow-color; + margin: 15px; + border: 2px solid $blue; + background-color: $base; + color: $text; + padding: 5px; + border-radius: 15px; + font-size: 58px; + color: transparentize($crust, .1) + } +} diff --git a/packages/ags/config/scss/_overview.scss b/packages/ags/config/scss/_overview.scss new file mode 100644 index 0000000..a01ea7b --- /dev/null +++ b/packages/ags/config/scss/_overview.scss @@ -0,0 +1,76 @@ +window#overview .overview { + background-color: $crust; + border: 2px solid $overlay0; + border-radius: 35px; + padding: 15px; + margin: 30px; + box-shadow: rgba(0, 0, 0, 0.6) 0 0 10px 10px; + + + &.horizontal>* { + margin: 0 10px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 10px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + .workspace { + + &.active>widget { + border-color: $blue; + } + + >widget { + border-radius: 20px; + background-color: $base; + margin: 10px; + border: 2px solid rgba(0, 0, 0, 0); + + &:hover { + background-color: $surface1; + } + + &:drop(active) { + background-color: $base; + } + } + + .client { + background-color: rgba(0, 0, 0, .3); + border: 2px solid $surface0; + margin: 10px; + border-radius: 10px; + padding: 0px; + + &:active, + &.on, + &.active, + &:checked, + &:focus { + border: 2px solid $blue; + background-color: rgba(0, 0, 0, 0.5); + } + + &:hover { + background-color: rgba(0, 0, 0, 0.5); + } + } + } +} diff --git a/packages/ags/config/scss/_powermenu.scss b/packages/ags/config/scss/_powermenu.scss new file mode 100644 index 0000000..99b1bfb --- /dev/null +++ b/packages/ags/config/scss/_powermenu.scss @@ -0,0 +1,160 @@ +#powermenu { + /* opacity: 0; */ + background-color: rgba(0, 0, 0, 0); +} + +window#powermenu, +window#verification { + // the fraction has to be more than hyprland ignorealpha + background-color: rgba(0, 0, 0, .4); +} + +#powermenu-motherbox, +.verification { + background-color: $crust; + // margin: 162px 288px; + border-radius: 25px; + opacity: 1; + // box-shadow: rgba(0,0,0,0.4) + // 10px 10px; + margin: 30px; + box-shadow: rgba(0, 0, 0, 0.6) 0 0 10px 20px; + border: 2px solid $overlay0; +} + +.verification { + min-height: 158px; + min-width: 358px; +} + + +.powermenu-powerbutton { + background-color: $red; + color: #212228; + border-radius: 200px; + min-height: 48px; + min-width: 48px; + font-size: 20px; + font-family: "Iosevka"; + + &:hover { + background-color: $green; + font-size: 100px; + } + + label { + padding-left: 15px; + padding-top: 0px; + } +} + +.powermenu-username { + background-color: $green; + color: #212228; + border-radius: 200px; + min-height: 48px; + font-size: 20px; + font-family: "Iosevka"; + + label { + padding-left: 24px; + padding-right: 24px; + } +} + +.powermenu-powerbutton, +.powermenu-username { + margin: 10px 0px 0px 10px; +} + + +.powermenu-midbox { + background-color: $base; + color: #FFFFFF; + border-radius: 10px; + margin: 15px 25px; + padding: 10px 25px; + font-size: 20px; + font-family: "Iosevka"; +} + +.verification-label { + background-color: $base; + color: #FFFFFF; + border-radius: 10px; + margin: 25px 25px; + padding: 10px 25px; + font-size: 30px; + font-family: "Iosevka"; +} + +#powermenu-topbox { + margin: 25px 25px 0px 25px; + min-height: 388px; + border-radius: 10px; +} + +.powermenu-buttonLabel { + font-family: "JetBrainsMono Nerd Font"; +} + +.powermenu-buttonBox { + background-color: $base; + padding: 0px; + color: #EEEEEF; + min-height: 242px; + min-width: 242px; + border-radius: 10px; + margin: 0px 0px 25px 0px; + font-size: 100px; + border: 2px solid rgba(0, 0, 0, 0); + + label { + margin-left: 85px; + } + + &:focus { + background-color: $blue; + color: #1E2127; + } + + &:hover { + border: 2px solid $lavender; + background-color: $surface0; + } +} + + +.verification-buttonBox { + background-color: $base; + padding: 0px; + color: #EEEEEF; + min-height: 60px; + min-width: 140px; + border-radius: 10px; + margin: 0px 0px 25px 0px; + font-size: 35px; + font-family: "Iosevka"; + border: 2px solid rgba(0, 0, 0, 0); + + + &:focus { + background-color: $blue; + color: #1E2127; + } + + &:hover { + border: 2px solid $lavender; + background-color: $surface0; + } +} + +.powermenu-buttonBoxFirst, +.verification-buttonBoxFirst { + margin-left: 25px; +} + +.powermenu-buttonBoxLast, +.verification-buttonBoxLast { + margin-right: 25px; +} diff --git a/packages/ags/config/scss/_quicksettings.scss b/packages/ags/config/scss/_quicksettings.scss new file mode 100644 index 0000000..45610b7 --- /dev/null +++ b/packages/ags/config/scss/_quicksettings.scss @@ -0,0 +1,1075 @@ +window#quicksettings .quicksettings { + font-family: "Iosevka"; + margin: 0px 0px 43px 20px; + // border-left: 2px solid $blue; + // border-top: 2px solid $blue; + // border-bottom: 2px solid $blue; + background-color: $base; + color: $text; + border-radius: 25px 0px 0px 25px; + padding: 10px; + // min-height: 1009px; + box-shadow: + rgba(0, 0, 0, 0.4) -8px 4px 10px; + + &.horizontal>* { + margin: 0 5px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 5px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + .avatar { + border-radius: 5px; + transition: 200ms; + color: $text; + background-color: $mantle; + border: 2px solid $rosewater; + } + + .header { + &.horizontal>* { + margin: 0 2.5px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 2.5px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + color: transparentize($text, .15); + + // button { + // all: unset; + // transition: 200ms; + // border-radius: 10px; + // color: $text; + // + // >* { + // border-radius: 50px; + // transition: 200ms; + // background-color: $crust; + // box-shadow: inset 0 0 0 5px $green; + // } + // + // &:focus>*, + // &.focused>* { + // box-shadow: inset 0 0 0 3px #0e0e0e; + // background-color: $blue; + // color: $mantle; + // } + // + // &:hover>* { + // box-shadow: inset 0 0 0 3px $blue; + // background-color: $blue; + // color: $mantle; + // } + // + // &:active, + // &.active, + // &.on, + // &:checked { + // >* { + // box-shadow: inset 0 0 0 2px $blue; + // background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + // background-color: $blue; + // color: $base; + // } + // + // &:hover>* { + // box-shadow: inset 0 0 0 2px $blue, + // inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + // } + // } + // + // &:disabled { + // box-shadow: none; + // background-color: transparent; + // color: transparentize($blue, 0.7); + // } + // + // padding: 20px; + // + // image { + // font-size: 1.4em; + // } + // } + } + + .sliders-box { + border-radius: 15px; + transition: 200ms; + color: $text; + background-color: $surface0; + // border: 2px solid $rosewater; + padding: 5px; + + button { + all: unset; + // border: 2px solid $text; + transition: 200ms; + border-radius: 5px; + margin: 5px; + color: $text; + font-size: 24px; + background-color: transparent; + background-image: none; + box-shadow: none; + padding: 3px; + + &:focus, + &.focused { + // box-shadow: inset 0 0 0 3px #0e0e0e; + background-color: darken($surface0, 10%); + color: $text; + } + + &:hover { + // box-shadow: inset 0 0 0 3px $text; + background-color: darken($surface0, 10%); + color: $text; + } + + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + } + + .volume button.arrow:last-child { + margin-left: 10px; + } + + .volume, + .brightness { + padding: 5px; + } + + scale { + all: unset; + + * { + all: unset + } + + trough { + transition: 200ms; + border-radius: 10px; + // border: 2px solid $green; + background-color: $base; + min-height: 5px; + min-width: 5px; + + highlight, + progress { + border-radius: 20px; + background-image: linear-gradient(to right, darken($blue, 20%), darken($blue, 30%)); + min-height: 5px; + min-width: 5px; + } + } + + slider { + box-shadow: none; + background-color: transparent; + border: 1px solid transparent; + transition: 200ms; + border-radius: 10px; + min-height: 16px; + min-width: 16px; + margin: -4px; + } + + &:hover { + trough { + background-color: $mantle; + } + + slider { + background-color: $text; + border-color: $blue; + // box-shadow: 0 0 3px 0 #0e0e0e; + } + } + + &:disabled { + + highlight, + progress { + background-color: transparentize($text, 0.4); + background-image: none; + } + } + + trough:focus { + background-color: $crust; + // box-shadow: inset 0 0 0 4px $mantle; + + slider { + background-color: darken($text, 10%); + // box-shadow: inset 0 0 0 4px $mantle; + } + } + + margin: 0 10px; + } + } + + .row { + &.horizontal>* { + margin: 0 10px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 10px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + } + + .menu { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + background-color: $surface1; + // border: 2px solid $rosewater; + padding: 5px; + margin-top: 10px; + + .icon { + margin: 0 10px; + margin-left: 4px; + } + + .title { + font-weight: bold; + } + + separator { + margin: 10px; + background-color: $rosewater; + } + + button { + all: unset; + transition: 200ms; + border-radius: 5px; + color: $text; + background-color: transparent; + background-image: none; + box-shadow: none; + + + &:focus, + &.focused { + // box-shadow: inset 0 0 0 3px #0e0e0e; + background-color: $mantle; + color: $blue; + } + + &:hover { + // box-shadow: inset 0 0 0 3px $blue; + background-color: $mantle; + color: $blue; + } + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $mantle; + color: $blue; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + + padding: 10px; + + image:first-child { + margin-right: 10px; + } + } + + .bluetooth-devices { + &.horizontal>* { + margin: 0 5px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 5px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + } + + switch { + + all: unset; + transition: 200ms; + border-radius: 100px; + color: $text; + + background-color: $rosewater; + box-shadow: inset 0 0 0 4px $green; + + + &:focus>*, + &.focused>* { + box-shadow: inset 0 0 0 3px #0e0e0e; + background-color: $blue; + color: $mantle; + } + + &:hover>* { + box-shadow: inset 0 0 0 3px $blue; + background-color: $blue; + color: $mantle; + } + + &:active, + &.active, + &.on, + &:checked { + >* { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + } + + &:hover>* { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + + slider { + background-color: $blue; + border-radius: 10px; + min-width: 24px; + min-height: 24px; + } + + image { + color: transparent; + } + } + } + + .sliders-box .menu { + margin: 10px 0; + + &.app-mixer { + .mixer-item { + padding: 10px; + padding-left: 0; + padding-right: 20px; + + scale { + all: unset; + + * { + all: unset + } + + trough { + transition: 200ms; + border-radius: 10px; + // border: 2px solid $green; + background-color: $base; + min-height: 5px; + min-width: 5px; + + highlight, + progress { + border-radius: 20px; + background-image: linear-gradient(to right, darken($blue, 20%), darken($blue, 30%)); + min-height: 5px; + min-width: 5px; + } + } + + slider { + box-shadow: none; + background-color: transparent; + border: 1px solid transparent; + transition: 200ms; + border-radius: 10px; + min-height: 16px; + min-width: 16px; + margin: -4px; + } + + &:hover { + trough { + background-color: $mantle; + } + + slider { + background-color: $text; + border-color: $blue; + // box-shadow: 0 0 3px 0 #0e0e0e; + } + } + + &:disabled { + + highlight, + progress { + background-color: transparentize($text, 0.4); + background-image: none; + } + } + + trough:focus { + background-color: $crust; + // box-shadow: inset 0 0 0 4px $mantle; + + slider { + background-color: darken($text, 10%); + // box-shadow: inset 0 0 0 4px $mantle; + } + } + + margin: 0 10px; + + } + + image { + font-size: 1.6em; + margin: 0 8px; + } + } + } + } + + .toggle-button { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + background-color: $surface0; + // box-shadow: inset 0 0 0 1px $rosewater; + + + &:focus, + &.focused { + box-shadow: inset 0 0 0 3px #0e0e0e; + background-color: $blue; + color: $mantle; + } + + &:hover { + box-shadow: inset 0 0 0 2px $green; + background-color: $surface2; + color: $text; + } + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $surface2; + color: $base; + + &:hover>* { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + + font-weight: bold; + + image { + font-size: 1.3em; + } + + label { + margin-left: 10px; + } + + button { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + background-color: transparent; + background-image: none; + box-shadow: none; + + &:focus, + &.focused { + box-shadow: inset 0 0 0 2px $blue; + background-color: $surface1; + color: $text; + } + + &:hover { + box-shadow: inset 0 0 0 1px $rosewater; + background-color: $surface2; + color: $text; + } + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 1px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $surface0; + color: $text; + + &:hover>* { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + } + + &:first-child { + padding: 12px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + &:last-child { + padding: 6px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + } + + &.active { + background-color: $surface1; + + label, + image { + color: $text; + } + } + } + + .simple-toggle { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + background-color: $surface0; + background-image: none; + box-shadow: none; + + &:focus, + &.focused { + box-shadow: inset 0 0 0 2px $rosewater; + background-color: $surface1; + color: $text; + } + + &:hover { + box-shadow: inset 0 0 0 1px $blue; + background-color: $surface1; + color: $text; + } + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $text; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + + font-weight: bold; + padding: 10px; + + label { + margin-left: 4px; + } + + image { + font-size: 1.3em; + } + } + + .media { + + &.horizontal>* { + margin: 0 10px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + } + + &.vertical>* { + margin: 10px 0; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + } + + .player { + + transition: 200ms; + border-radius: 15px; + color: $text; + background-color: $crust; + // border: 1px solid $rosewater; + padding: 5px; + + .cover { + box-shadow: 2px 2px 2px 0 #0e0e0e; + background-size: cover; + background-position: center; + border-radius: 10px; + margin: 10px 15px 10px 10px; + } + + button { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + padding: 5px; + background-color: transparent; + background-image: none; + box-shadow: none; + + &:focus, + &.focused { + box-shadow: inset 0 0 0 1px $rosewater; + // background-color: $blue; + // color: $mantle; + } + + &:hover { + box-shadow: inset 0 0 0 1px $blue; + background-color: $surface0; + color: $blue; + } + + &:active, + &.active, + &.on, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($blue, 0.7); + } + + + &.play-pause { + margin: 0 5px; + } + + image { + font-size: 1.5em; + } + } + + .artist { + color: transparentize($text, .2); + font-size: .9em; + } + + scale { + + // @include slider($width: .5em, $slider: false, $gradient: linear-gradient($fg, $fg)); + trough { + transition: 200ms; + border-radius: 10px; + // border: 1px solid $rosewater; + background-color: $base; + min-height: 0.5em; + min-width: 0.5em; + + highlight, + progress { + border-radius: 10px; + background-image: linear-gradient($overlay1, $overlay1); + min-height: 0.5em; + min-width: 0.5em; + } + } + + slider { + box-shadow: none; + background-color: transparent; + border: 4px solid transparent; + transition: 200ms; + border-radius: 10px; + min-height: 0.5em; + min-width: 0.5em; + margin: -0.5em; + } + + &:hover { + trough { + background-color: $surface0; + } + } + + &:disabled { + + highlight, + progress { + background-color: transparentize($text, 0.4); + background-image: none; + } + } + + trough:focus { + background-color: $blue; + box-shadow: inset 0 0 0 4px $mantle; + } + + margin-bottom: 5px; + + trough { + border: none; + } + } + + .icon { + font-size: 22px; + margin: 5px 5px 0px 0px; + } + } + } + + // notification + .notifications { + .header { + margin-bottom: 20px; + margin-right: 30px; + + >label { + margin-left: 15px; + } + + button { + all: unset; + transition: 200ms; + border-radius: 10px; + color: $text; + + background-color: $surface0; + // box-shadow: inset 0 0 0 2px $blue; + + &:focus { + box-shadow: inset 0 0 0 2px #0e0e0e; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:hover { + box-shadow: inset 0 0 0 2px $blue; + background-color: transparentize(#eeeeee, $amount: 0.8); + color: lighten($color: #eeeeee, $amount: 8%); + } + + &:active, + &.on, + &.active, + &:checked { + box-shadow: inset 0 0 0 2px $blue; + background-image: linear-gradient(to right, $mantle, darken($mantle, 4%)); + background-color: $blue; + color: $base; + + &:hover { + box-shadow: inset 0 0 0 2px $blue, + inset 0 0 0 99px transparentize(#eeeeee, $amount: 0.8); + } + } + + &:disabled { + box-shadow: none; + background-color: transparent; + color: transparentize($mantle, 0.7); + } + + padding: 10px; + } + } + + .notification-scrollable { + + scrollbar, + scrollbar * { + all: unset; + } + + scrollbar.vertical { + transition: 200ms; + background-color: transparentize($base, 0.7); + + &:hover { + background-color: transparentize($base, 0.3); + + slider { + background-color: transparentize($text, 0.3); + min-width: .6em; + } + } + } + + + scrollbar.vertical slider { + background-color: transparentize($text, 0.5); + border-radius: 10px; + min-width: .4em; + min-height: 2em; + transition: 200ms; + } + } + + .notification-list { + margin-right: 10px; + } + + .notification { + &.critical { + box-shadow: inset 0 0 .5em 0 $red; + } + + &:hover button.close-button { + // @include button-hover; + background-color: transparentize($red, .5); + } + + .content { + .title { + margin-top: 5px; + margin-right: 10px; + color: $text; + font-size: 1.2em; + } + + .time { + color: transparentize($text, .2); + margin: 5px 10px; + } + + .description { + font-size: .9em; + color: transparentize($text, .2); + } + + .icon { + border-radius: 10px; + margin-right: 10px; + + &.img { + border: 2px solid $green; + } + } + } + + box.actions { + // @include spacing(0.5); + margin-top: 10px; + + button { + // @include button; + background-color: $surface0; + border: 2px solid $surface0; + border-radius: 10px; + font-size: 1.2em; + margin: 0px 10px 10px 0px; + padding: 10px; + + &:first-child { + margin: 0px 10px 10px 10px; + } + + &:hover { + background-color: $surface2; + border: 2px solid $rosewater; + } + } + } + + button.close-button { + // @include button($flat: true); + margin: 0px 0px 0px 0px; + border-radius: 10px; + min-width: 30px; + min-height: 30px; + background-color: rgba(0, 0, 0, 0); //$crust; + padding: 0px; + + &:hover { + background-color: transparentize($red, .2); + } + + &:active { + background-image: none; + background-color: $red; + } + } + + transition: 200ms; + color: $text; + margin-bottom: 10px; + border-radius: 15px; + background-color: $base; + font-family: "Iosevka"; + padding: 5px; + border: 1px solid $rosewater; + box-shadow: rgba(0, 0, 0, 0.4) 5px 5px 5px; + } + + .placeholder { + image { + font-size: 7em; + } + + label { + font-size: 1.2em; + } + } + } +} diff --git a/packages/ags/config/scss/main.scss b/packages/ags/config/scss/main.scss new file mode 100644 index 0000000..32c017d --- /dev/null +++ b/packages/ags/config/scss/main.scss @@ -0,0 +1,11 @@ +@import './mocha'; +@import './globals'; +@import './lockscreen'; +@import './powermenu'; +@import './dock'; +@import './overview'; +@import './notifications'; +@import './bar'; +@import './osd'; +@import './datemenu'; +@import './quicksettings'; diff --git a/packages/ags/config/services/asusctl.js b/packages/ags/config/services/asusctl.js new file mode 100644 index 0000000..d9e6a6b --- /dev/null +++ b/packages/ags/config/services/asusctl.js @@ -0,0 +1,49 @@ +import { sh } from "../lib/utils.js" + +class Asusctl extends Service { + static { + Service.register(this, {}, { + "profile": ["string", "r"], + "mode": ["string", "r"], + }) + } + + available = !!Utils.exec("which asusctl") + #profile = "Balanced" + #mode = "Hybrid" + + async nextProfile() { + await sh("asusctl profile -n") + const profile = await sh("asusctl profile -p") + const p = profile.split(" ")[3] + this.#profile = p + this.changed("profile") + } + + async setProfile(prof) { + 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") + this.changed("profile") + } + + constructor() { + super() + + if (this.available) { + sh("asusctl profile -p").then(p => this.#profile = p.split(" ")[3]) + sh("supergfxctl -g").then(m => this.#mode = m) + } + } + + get profiles() { return ["Performance", "Balanced", "Quiet"] } + get profile() { return this.#profile } + get mode() { return this.#mode } +} + +export default new Asusctl() diff --git a/packages/ags/config/services/brightness.js b/packages/ags/config/services/brightness.js new file mode 100644 index 0000000..715adb4 --- /dev/null +++ b/packages/ags/config/services/brightness.js @@ -0,0 +1,69 @@ +import { bash, dependencies, sh } from "../lib/utils.js" + +if (!dependencies("brightnessctl")) + App.quit() + +const get = (args) => 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/packages/ags/config/services/clock.js b/packages/ags/config/services/clock.js new file mode 100644 index 0000000..73e32ee --- /dev/null +++ b/packages/ags/config/services/clock.js @@ -0,0 +1,21 @@ +const { GLib } = imports.gi; +class ClockService extends Service { + static { + Service.register ( this, {}, { + 'time': ['gobject'] + } + ) + } + + #time = GLib.DateTime.new_now_local() + + get time() { return this.#time; } + constructor() { + super() + Utils.interval(1000, () => { + this.#time = GLib.DateTime.new_now_local() + this.changed("time") + }) + } +} +export default new ClockService() diff --git a/packages/ags/config/services/colorpicker.js b/packages/ags/config/services/colorpicker.js new file mode 100644 index 0000000..eed3923 --- /dev/null +++ b/packages/ags/config/services/colorpicker.js @@ -0,0 +1,57 @@ +import icons from "../lib/icons.js" +import { bash, dependencies } from "../lib/utils.js" + +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) || "[]") + + get colors() { return [...this.#colors] } + set colors(colors) { + this.#colors = colors + this.changed("colors") + } + + // TODO: doesn't work? + async wlCopy(color) { + if (dependencies("wl-copy")) + bash(`wl-copy ${color}`) + } + + async pick() { + if (!dependencies("hyprpicker")) + return + + const color = await bash("hyprpicker -a -r") + if (!color) + return + + colorpicker.wlCopy(color) + const list = colorpicker.colors + if (!list.includes(color)) { + list.push(color) + if (list.length > MAX_NUM_COLORS) + list.shift() + + colorpicker.colors = list + Utils.writeFile(JSON.stringify(list, null, 2), COLORS_CACHE) + } + + colorpicker.notifID = await Utils.notify({ + id: colorpicker.notifID, + iconName: icons.ui.colorpicker, + summary: color, + }) + } +} + +const colorpicker = new ColorPicker +export default colorpicker diff --git a/packages/ags/config/services/powermenu.js b/packages/ags/config/services/powermenu.js new file mode 100644 index 0000000..bf0beaa --- /dev/null +++ b/packages/ags/config/services/powermenu.js @@ -0,0 +1,43 @@ +import options from '../options.js'; +// 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) { + [this.#cmd, this.#title] = { + sleep: [options.powermenu.sleep, "Sleep"], + reboot: [options.powermenu.reboot, "Reboot"], + logout: [options.powermenu.logout, "Log Out"], + shutdown: [options.powermenu.shutdown, "Shutdown"], + lock: [options.powermenu.lock, "Lock"], + }[action] + + this.notify("cmd") + this.notify("title") + this.emit("changed") + App.closeWindow("powermenu") + App.openWindow("verification") + } + + shutdown = () => { + this.action("shutdown") + } +} + +const powermenu = new PowerMenu +globalThis["powermenu"] = powermenu +export default powermenu diff --git a/packages/ags/config/services/screenrecord.js b/packages/ags/config/services/screenrecord.js new file mode 100644 index 0000000..23e6f8b --- /dev/null +++ b/packages/ags/config/services/screenrecord.js @@ -0,0 +1,93 @@ +import GLib from "gi://GLib" +import icons from "../lib/icons.js" +import { dependencies, sh, bash } from "../lib/utils.js" + +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) + + const wayshot = `wayshot -f ${file} ${full ? "" : `-s "${await sh("slurp")}"`}` + await sh(wayshot) + bash(`wl-copy < ${file}`) + + Utils.notify({ + image: file, + summary: "Screenshot", + body: this.#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 +globalThis["recorder"] = recorder +export default recorder diff --git a/packages/ags/default.nix b/packages/ags/default.nix new file mode 100644 index 0000000..d7c1650 --- /dev/null +++ b/packages/ags/default.nix @@ -0,0 +1,21 @@ +{ inputs, pkgs, lib, config, ... }: +{ + imports = [ inputs.ags.homeManagerModules.default ]; + + + programs.ags = { + enable = true; + + extraPackages = with pkgs; [ + dart-sass + brightnessctl + wl-clipboard + ]; + }; + + home.file.".config/ags" = { + source = ./config; + recursive = true; + }; + +} diff --git a/packages/ags/example/applauncher/README.md b/packages/ags/example/applauncher/README.md new file mode 100644 index 0000000..88791dc --- /dev/null +++ b/packages/ags/example/applauncher/README.md @@ -0,0 +1,19 @@ +# Application Launcher + +setup + +```bash +mkdir -p ~/.config/ags +git clone https://github.com/Aylur/ags.git /tmp/ags +cp -r /tmp/ags/example/applauncher/* ~/.config/ags + +# optionally setup types +ags --init -c ~/.config/ags/config.js +``` + +running + +```bash +ags -c ~/.config/ags/config.js & +ags -t applauncher +``` diff --git a/packages/ags/example/applauncher/applauncher.js b/packages/ags/example/applauncher/applauncher.js new file mode 100644 index 0000000..23dfa40 --- /dev/null +++ b/packages/ags/example/applauncher/applauncher.js @@ -0,0 +1,107 @@ +const { query } = await Service.import("applications") +const WINDOW_NAME = "applauncher" + +/** @param {import('resource:///com/github/Aylur/ags/service/applications.js').Application} app */ +const AppItem = app => Widget.Button({ + on_clicked: () => { + App.closeWindow(WINDOW_NAME) + app.launch() + }, + attribute: { app }, + child: Widget.Box({ + children: [ + Widget.Icon({ + icon: app.icon_name || "", + size: 42, + }), + Widget.Label({ + class_name: "title", + label: app.name, + xalign: 0, + vpack: "center", + truncate: "end", + }), + ], + }), +}) + +const Applauncher = ({ width = 500, height = 500, spacing = 12 }) => { + // list of application buttons + let applications = query("").map(AppItem) + + // container holding the buttons + const list = Widget.Box({ + vertical: true, + children: applications, + spacing, + }) + + // repopulate the box, so the most frequent apps are on top of the list + function repopulate() { + applications = query("").map(AppItem) + list.children = applications + } + + // search entry + const entry = Widget.Entry({ + hexpand: true, + css: `margin-bottom: ${spacing}px;`, + + // to launch the first item on Enter + on_accept: () => { + // make sure we only consider visible (searched for) applications + const results = applications.filter((item) => item.visible); + if (results[0]) { + App.toggleWindow(WINDOW_NAME) + results[0].attribute.app.launch() + } + }, + + // filter out the list + on_change: ({ text }) => applications.forEach(item => { + item.visible = item.attribute.app.match(text ?? "") + }), + }) + + return Widget.Box({ + vertical: true, + css: `margin: ${spacing * 2}px;`, + children: [ + entry, + + // wrap the list in a scrollable + Widget.Scrollable({ + hscroll: "never", + css: `min-width: ${width}px;` + + `min-height: ${height}px;`, + child: list, + }), + ], + setup: self => self.hook(App, (_, windowName, visible) => { + if (windowName !== WINDOW_NAME) + return + + // when the applauncher shows up + if (visible) { + repopulate() + entry.text = "" + entry.grab_focus() + } + }), + }) +} + +// there needs to be only one instance +export const applauncher = Widget.Window({ + name: WINDOW_NAME, + setup: self => self.keybind("Escape", () => { + App.closeWindow(WINDOW_NAME) + }), + visible: false, + keymode: "exclusive", + child: Applauncher({ + width: 500, + height: 500, + spacing: 12, + }), +}) diff --git a/packages/ags/example/applauncher/config.js b/packages/ags/example/applauncher/config.js new file mode 100644 index 0000000..61842ee --- /dev/null +++ b/packages/ags/example/applauncher/config.js @@ -0,0 +1,5 @@ +import { applauncher } from "./applauncher.js" + +App.config({ + windows: [applauncher], +}) diff --git a/packages/ags/example/icon-browser/README.md b/packages/ags/example/icon-browser/README.md new file mode 100644 index 0000000..2657f69 --- /dev/null +++ b/packages/ags/example/icon-browser/README.md @@ -0,0 +1,6 @@ +# Icon Browser + +```bash +chmod +x icon-browser.js +./icon-browser.js +``` diff --git a/packages/ags/example/icon-browser/icon-browser.js b/packages/ags/example/icon-browser/icon-browser.js new file mode 100755 index 0000000..f4187af --- /dev/null +++ b/packages/ags/example/icon-browser/icon-browser.js @@ -0,0 +1,73 @@ +#!/usr/bin/env -S ags -b icon-browser -c + +import Gtk from "gi://Gtk?version=3.0" + +const IconPicker = () => { + const selected = Widget.Label({ + css: "font-size: 1.2em;", + label: "Click on an icon te get its name", + selectable: true, + }) + + const flowbox = Widget.FlowBox({ + min_children_per_line: 7, + row_spacing: 12, + column_spacing: 12, + vpack: "start", + hpack: "start", + setup: self => { + self.connect("child-activated", (_, child) => { + selected.label = child.get_child()?.iconName || "" + }) + + Gtk.IconTheme.get_default().list_icons(null).sort().map(icon => { + !icon.endsWith(".symbolic") && self.insert(Widget.Icon({ + icon, + size: 38, + }), -1) + }) + + self.show_all() + }, + }) + + const entry = Widget.Entry({ + placeholder_text: "Type to seach...", + primary_icon_name: "system-search-symbolic", + on_change: ({ text }) => flowbox.get_children().forEach(child => { + child.visible = child.get_child().iconName.includes(text) + }), + }) + + return Widget.Box({ + css: "padding: 30px;", + spacing: 20, + vertical: true, + children: [ + entry, + Widget.Scrollable({ + hscroll: "never", + vscroll: "always", + hexpand: true, + vexpand: true, + css: "min-width: 400px;" + + "min-height: 500px;", + child: flowbox, + }), + selected, + ], + }) +} + +const win = new Gtk.Window({ + name: "icon-browser", + child: IconPicker(), +}) + +win.show_all() +win.connect("delete-event", () => { + App.quit() + return true +}) + +export default { windows: [win] } diff --git a/packages/ags/example/media-widget/Media.js b/packages/ags/example/media-widget/Media.js new file mode 100644 index 0000000..337656d --- /dev/null +++ b/packages/ags/example/media-widget/Media.js @@ -0,0 +1,153 @@ +const mpris = await Service.import("mpris") +const players = mpris.bind("players") + +const FALLBACK_ICON = "audio-x-generic-symbolic" +const PLAY_ICON = "media-playback-start-symbolic" +const PAUSE_ICON = "media-playback-pause-symbolic" +const PREV_ICON = "media-skip-backward-symbolic" +const NEXT_ICON = "media-skip-forward-symbolic" + +/** @param {number} length */ +function lengthStr(length) { + const min = Math.floor(length / 60) + const sec = Math.floor(length % 60) + const sec0 = sec < 10 ? "0" : "" + return `${min}:${sec0}${sec}` +} + +/** @param {import('types/service/mpris').MprisPlayer} player */ +function Player(player) { + const img = Widget.Box({ + class_name: "img", + vpack: "start", + css: player.bind("cover_path").transform(p => ` + background-image: url('${p}'); + `), + }) + + const title = Widget.Label({ + class_name: "title", + wrap: true, + hpack: "start", + label: player.bind("track_title"), + }) + + const artist = Widget.Label({ + class_name: "artist", + wrap: true, + hpack: "start", + label: player.bind("track_artists").transform(a => a.join(", ")), + }) + + const positionSlider = Widget.Slider({ + class_name: "position", + draw_value: false, + on_change: ({ value }) => player.position = value * player.length, + visible: player.bind("length").as(l => l > 0), + setup: self => { + function update() { + const value = player.position / player.length + self.value = value > 0 ? value : 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 = (_, time) => { + 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").transform(l => l > 0), + label: player.bind("length").transform(lengthStr), + }) + + const icon = Widget.Icon({ + class_name: "icon", + hexpand: true, + hpack: "end", + vpack: "start", + tooltip_text: player.identity || "", + icon: player.bind("entry").transform(entry => { + const name = `${entry}-symbolic` + return Utils.lookUpIcon(name) ? name : FALLBACK_ICON + }), + }) + + 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").transform(s => { + switch (s) { + case "Playing": return PAUSE_ICON + case "Paused": + case "Stopped": return PLAY_ICON + } + }), + }), + }) + + const prev = Widget.Button({ + on_clicked: () => player.previous(), + visible: player.bind("can_go_prev"), + child: Widget.Icon(PREV_ICON), + }) + + const next = Widget.Button({ + on_clicked: () => player.next(), + visible: player.bind("can_go_next"), + child: Widget.Icon(NEXT_ICON), + }) + + return Widget.Box( + { class_name: "player" }, + img, + Widget.Box( + { + vertical: true, + hexpand: true, + }, + Widget.Box([ + title, + icon, + ]), + artist, + Widget.Box({ vexpand: true }), + positionSlider, + Widget.CenterBox({ + start_widget: positionLabel, + center_widget: Widget.Box([ + prev, + playPause, + next, + ]), + end_widget: lengthLabel, + }), + ), + ) +} + +export function Media() { + return Widget.Box({ + vertical: true, + css: "min-height: 2px; min-width: 2px;", // small hack to make it visible + visible: players.as(p => p.length > 0), + children: players.as(p => p.map(Player)), + }) +} diff --git a/packages/ags/example/media-widget/README.md b/packages/ags/example/media-widget/README.md new file mode 100644 index 0000000..0db707c --- /dev/null +++ b/packages/ags/example/media-widget/README.md @@ -0,0 +1,18 @@ +# Media Widget + +setup + +```bash +mkdir -p ~/.config/ags +git clone https://github.com/Aylur/ags.git /tmp/ags +cp -r /tmp/ags/example/media-widget/* ~/.config/ags + +# optionally setup types +ags --init -c ~/.config/ags/config.js +``` + +running + +```bash +ags -c ~/.config/ags/config.js & +``` diff --git a/packages/ags/example/media-widget/config.js b/packages/ags/example/media-widget/config.js new file mode 100644 index 0000000..d8f5c86 --- /dev/null +++ b/packages/ags/example/media-widget/config.js @@ -0,0 +1,12 @@ +import { Media } from "./Media.js" + +const win = Widget.Window({ + name: "mpris", + anchor: ["top", "right"], + child: Media(), +}) + +App.config({ + style: "./style.css", + windows: [win], +}) diff --git a/packages/ags/example/media-widget/style.css b/packages/ags/example/media-widget/style.css new file mode 100644 index 0000000..8488ac8 --- /dev/null +++ b/packages/ags/example/media-widget/style.css @@ -0,0 +1,49 @@ +.player { + padding: 10px; + min-width: 350px; +} + +.player .img { + min-width: 100px; + min-height: 100px; + background-size: cover; + background-position: center; + border-radius: 13px; + margin-right: 1em; +} + +.player .title { + font-size: 1.2em; +} + +.player .artist { + font-size: 1.1em; + color: @insensitive_fg_color; +} + +.player scale.position { + padding: 0; + margin-bottom: .3em; +} + +.player scale.position trough { + min-height: 8px; +} + +.player scale.position highlight { + background-color: @theme_fg_color; +} + +.player scale.position slider { + all: unset; +} + +.player button { + min-height: 1em; + min-width: 1em; + padding: .3em; +} + +.player button.play-pause { + margin: 0 .3em; +} diff --git a/packages/ags/example/notification-popups/README.md b/packages/ags/example/notification-popups/README.md new file mode 100644 index 0000000..7ca2c7f --- /dev/null +++ b/packages/ags/example/notification-popups/README.md @@ -0,0 +1,18 @@ +# Notification Popups + +setup + +```bash +mkdir -p ~/.config/ags +git clone https://github.com/Aylur/ags.git /tmp/ags +cp -r /tmp/ags/example/notification-popups/* ~/.config/ags + +# optionally setup types +ags --init -c ~/.config/ags/config.js +``` + +running + +```bash +ags -c ~/.config/ags/config.js & +``` diff --git a/packages/ags/example/notification-popups/config.js b/packages/ags/example/notification-popups/config.js new file mode 100644 index 0000000..bcb678b --- /dev/null +++ b/packages/ags/example/notification-popups/config.js @@ -0,0 +1,18 @@ +import { NotificationPopups } from "./notificationPopups.js" + +Utils.timeout(100, () => Utils.notify({ + summary: "Notification Popup Example", + iconName: "info-symbolic", + body: "Lorem ipsum dolor sit amet, qui minim labore adipisicing " + + "minim sint cillum sint consectetur cupidatat.", + actions: { + "Cool": () => print("pressed Cool"), + }, +})) + +App.config({ + style: App.configDir + "/style.css", + windows: [ + NotificationPopups(), + ], +}) diff --git a/packages/ags/example/notification-popups/notificationPopups.js b/packages/ags/example/notification-popups/notificationPopups.js new file mode 100644 index 0000000..f360d3c --- /dev/null +++ b/packages/ags/example/notification-popups/notificationPopups.js @@ -0,0 +1,130 @@ +const notifications = await Service.import("notifications") + +/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */ +function NotificationIcon({ app_entry, app_icon, image }) { + if (image) { + return Widget.Box({ + css: `background-image: url("${image}");` + + "background-size: contain;" + + "background-repeat: no-repeat;" + + "background-position: center;", + }) + } + + let icon = "dialog-information-symbolic" + if (Utils.lookUpIcon(app_icon)) + icon = app_icon + + if (app_entry && Utils.lookUpIcon(app_entry)) + icon = app_entry + + return Widget.Box({ + child: Widget.Icon(icon), + }) +} + +/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */ +function Notification(n) { + const icon = Widget.Box({ + vpack: "start", + class_name: "icon", + child: NotificationIcon(n), + }) + + const title = Widget.Label({ + class_name: "title", + xalign: 0, + justification: "left", + hexpand: true, + max_width_chars: 24, + truncate: "end", + wrap: true, + label: n.summary, + use_markup: true, + }) + + const body = Widget.Label({ + class_name: "body", + hexpand: true, + use_markup: true, + xalign: 0, + justification: "left", + label: n.body, + wrap: true, + }) + + const actions = Widget.Box({ + class_name: "actions", + children: n.actions.map(({ id, label }) => Widget.Button({ + class_name: "action-button", + on_clicked: () => { + n.invoke(id) + n.dismiss() + }, + hexpand: true, + child: Widget.Label(label), + })), + }) + + return Widget.EventBox( + { + attribute: { id: n.id }, + on_primary_click: n.dismiss, + }, + Widget.Box( + { + class_name: `notification ${n.urgency}`, + vertical: true, + }, + Widget.Box([ + icon, + Widget.Box( + { vertical: true }, + title, + body, + ), + ]), + actions, + ), + ) +} + +export function NotificationPopups(monitor = 0) { + const list = Widget.Box({ + vertical: true, + children: notifications.popups.map(Notification), + }) + + function onNotified(_, /** @type {number} */ id) { + const n = notifications.getNotification(id) + if (n) + list.children = [Notification(n), ...list.children] + } + + function onDismissed(_, /** @type {number} */ id) { + list.children.find(n => n.attribute.id === id)?.destroy() + } + + list.hook(notifications, onNotified, "notified") + .hook(notifications, onDismissed, "dismissed") + + return Widget.Window({ + monitor, + name: `notifications${monitor}`, + class_name: "notification-popups", + anchor: ["top", "right"], + child: Widget.Box({ + css: "min-width: 2px; min-height: 2px;", + class_name: "notifications", + vertical: true, + child: list, + + /** this is a simple one liner that could be used instead of + hooking into the 'notified' and 'dismissed' signals. + but its not very optimized becuase it will recreate + the whole list everytime a notification is added or dismissed */ + // children: notifications.bind('popups') + // .as(popups => popups.map(Notification)) + }), + }) +} diff --git a/packages/ags/example/notification-popups/style.css b/packages/ags/example/notification-popups/style.css new file mode 100644 index 0000000..2d2883b --- /dev/null +++ b/packages/ags/example/notification-popups/style.css @@ -0,0 +1,57 @@ +window.notification-popups box.notifications { + padding: .5em; +} + +.icon { + min-width: 68px; + min-height: 68px; + margin-right: 1em; +} + +.icon image { + font-size: 58px; + /* to center the icon */ + margin: 5px; + color: @theme_fg_color; +} + +.icon box { + min-width: 68px; + min-height: 68px; + border-radius: 7px; +} + +.notification { + min-width: 350px; + border-radius: 11px; + padding: 1em; + margin: .5em; + border: 1px solid @wm_borders_edge; + background-color: @theme_bg_color; +} + +.notification.critical { + border: 1px solid lightcoral; +} + +.title { + color: @theme_fg_color; + font-size: 1.4em; +} + +.body { + color: @theme_unfocused_fg_color; +} + +.actions .action-button { + margin: 0 .4em; + margin-top: .8em; +} + +.actions .action-button:first-child { + margin-left: 0; +} + +.actions .action-button:last-child { + margin-right: 0; +} diff --git a/packages/ags/example/simple-bar/README.md b/packages/ags/example/simple-bar/README.md new file mode 100644 index 0000000..054a467 --- /dev/null +++ b/packages/ags/example/simple-bar/README.md @@ -0,0 +1,18 @@ +# Simple Bar + +setup + +```bash +mkdir -p ~/.config/ags +git clone https://github.com/Aylur/ags.git /tmp/ags +cp -r /tmp/ags/example/simple-bar/* ~/.config/ags + +# optionally setup types +ags --init -c ~/.config/ags/config.js +``` + +running + +```bash +ags -c ~/.config/ags/config.js & +``` diff --git a/packages/ags/example/simple-bar/config.js b/packages/ags/example/simple-bar/config.js new file mode 100644 index 0000000..4c0a30b --- /dev/null +++ b/packages/ags/example/simple-bar/config.js @@ -0,0 +1,219 @@ +const hyprland = await Service.import("hyprland") +const notifications = await Service.import("notifications") +const mpris = await Service.import("mpris") +const audio = await Service.import("audio") +const battery = await Service.import("battery") +const systemtray = await Service.import("systemtray") + +const date = Variable("", { + poll: [1000, 'date "+%H:%M:%S %b %e."'], +}) + +// widgets can be only assigned as a child in one container +// so to make a reuseable widget, make it a function +// then you can simply instantiate one by calling it + +function Workspaces() { + const activeId = hyprland.active.workspace.bind("id") + const workspaces = hyprland.bind("workspaces") + .as(ws => ws.map(({ id }) => Widget.Button({ + on_clicked: () => hyprland.messageAsync(`dispatch workspace ${id}`), + child: Widget.Label(`${id}`), + class_name: activeId.as(i => `${i === id ? "focused" : ""}`), + }))) + + return Widget.Box({ + class_name: "workspaces", + children: workspaces, + }) +} + + +function ClientTitle() { + return Widget.Label({ + class_name: "client-title", + label: hyprland.active.client.bind("title"), + }) +} + + +function Clock() { + return Widget.Label({ + class_name: "clock", + label: date.bind(), + }) +} + + +// we don't need dunst or any other notification daemon +// because the Notifications module is a notification daemon itself +function Notification() { + const popups = notifications.bind("popups") + return Widget.Box({ + class_name: "notification", + visible: popups.as(p => p.length > 0), + children: [ + Widget.Icon({ + icon: "preferences-system-notifications-symbolic", + }), + Widget.Label({ + label: popups.as(p => p[0]?.summary || ""), + }), + ], + }) +} + + +function Media() { + const label = Utils.watch("", mpris, "player-changed", () => { + if (mpris.players[0]) { + const { track_artists, track_title } = mpris.players[0] + return `${track_artists.join(", ")} - ${track_title}` + } else { + return "Nothing is playing" + } + }) + + return Widget.Button({ + class_name: "media", + on_primary_click: () => mpris.getPlayer("")?.playPause(), + on_scroll_up: () => mpris.getPlayer("")?.next(), + on_scroll_down: () => mpris.getPlayer("")?.previous(), + child: Widget.Label({ label }), + }) +} + + +function Volume() { + const icons = { + 101: "overamplified", + 67: "high", + 34: "medium", + 1: "low", + 0: "muted", + } + + function getIcon() { + const icon = audio.speaker.is_muted ? 0 : [101, 67, 34, 1, 0].find( + threshold => threshold <= audio.speaker.volume * 100) + + return `audio-volume-${icons[icon]}-symbolic` + } + + const icon = Widget.Icon({ + icon: Utils.watch(getIcon(), audio.speaker, getIcon), + }) + + const slider = Widget.Slider({ + hexpand: true, + draw_value: false, + on_change: ({ value }) => audio.speaker.volume = value, + setup: self => self.hook(audio.speaker, () => { + self.value = audio.speaker.volume || 0 + }), + }) + + return Widget.Box({ + class_name: "volume", + css: "min-width: 180px", + children: [icon, slider], + }) +} + + +function BatteryLabel() { + const value = battery.bind("percent").as(p => p > 0 ? p / 100 : 0) + const icon = battery.bind("percent").as(p => + `battery-level-${Math.floor(p / 10) * 10}-symbolic`) + + return Widget.Box({ + class_name: "battery", + visible: battery.bind("available"), + children: [ + Widget.Icon({ icon }), + Widget.LevelBar({ + widthRequest: 140, + vpack: "center", + value, + }), + ], + }) +} + + +function SysTray() { + const items = systemtray.bind("items") + .as(items => items.map(item => Widget.Button({ + child: Widget.Icon({ icon: item.bind("icon") }), + on_primary_click: (_, event) => item.activate(event), + on_secondary_click: (_, event) => item.openMenu(event), + tooltip_markup: item.bind("tooltip_markup"), + }))) + + return Widget.Box({ + children: items, + }) +} + + +// layout of the bar +function Left() { + return Widget.Box({ + spacing: 8, + children: [ + Workspaces(), + ClientTitle(), + ], + }) +} + +function Center() { + return Widget.Box({ + spacing: 8, + children: [ + Media(), + Notification(), + ], + }) +} + +function Right() { + return Widget.Box({ + hpack: "end", + spacing: 8, + children: [ + Volume(), + BatteryLabel(), + Clock(), + SysTray(), + ], + }) +} + +function Bar(monitor = 0) { + return Widget.Window({ + name: `bar-${monitor}`, // name has to be unique + class_name: "bar", + monitor, + anchor: ["top", "left", "right"], + exclusivity: "exclusive", + child: Widget.CenterBox({ + start_widget: Left(), + center_widget: Center(), + end_widget: Right(), + }), + }) +} + +App.config({ + style: "./style.css", + windows: [ + Bar(), + + // you can call it, for each monitor + // Bar(0), + // Bar(1) + ], +}) + +export { } diff --git a/packages/ags/example/simple-bar/style.css b/packages/ags/example/simple-bar/style.css new file mode 100644 index 0000000..9ac7355 --- /dev/null +++ b/packages/ags/example/simple-bar/style.css @@ -0,0 +1,40 @@ +window.bar { + background-color: @theme_bg_color; + color: @theme_fg_color; +} + +button { + min-width: 0; + padding-top: 0; + padding-bottom: 0; + background-color: transparent; +} + +button:active { + background-color: @theme_selected_bg_color; +} + +button:hover { + border-bottom: 3px solid @theme_fg_color; +} + +label { + font-weight: bold; +} + +.workspaces button.focused { + border-bottom: 3px solid @theme_selected_bg_color; +} + +.client-title { + color: @theme_selected_bg_color; +} + +.notification { + color: yellow; +} + +levelbar block, +highlight { + min-height: 10px; +} diff --git a/packages/ags/example/ts-starter-config/README.md b/packages/ags/example/ts-starter-config/README.md new file mode 100644 index 0000000..25696e1 --- /dev/null +++ b/packages/ags/example/ts-starter-config/README.md @@ -0,0 +1,21 @@ +# TypeScript Starter Config + +Dependency: `bun` + +```bash +mkdir -p ~/.config/ags +git clone https://github.com/Aylur/ags.git /tmp/ags +cp -r /tmp/ags/example/ts-starter-config/* ~/.config/ags +``` + +optionally setup types + +```bash +ags --init -c ~/.config/ags/config.js +``` + +running + +```bash +ags -c ~/.config/ags/config.js & +``` diff --git a/packages/ags/example/ts-starter-config/config.js b/packages/ags/example/ts-starter-config/config.js new file mode 100644 index 0000000..d8d6444 --- /dev/null +++ b/packages/ags/example/ts-starter-config/config.js @@ -0,0 +1,15 @@ +const main = '/tmp/ags/main.js'; + +try { + await Utils.execAsync([ + 'bun', 'build', `${App.configDir}/main.ts`, + '--outfile', main, + '--external', 'resource://*', + '--external', 'gi://*', + '--external', 'file://*', + ]); + await import(`file://${main}`); +} catch (error) { + console.error(error); + App.quit(); +} diff --git a/packages/ags/example/ts-starter-config/main.ts b/packages/ags/example/ts-starter-config/main.ts new file mode 100644 index 0000000..f5a29a4 --- /dev/null +++ b/packages/ags/example/ts-starter-config/main.ts @@ -0,0 +1,26 @@ +const time = Variable('', { + poll: [1000, function() { + return Date().toString(); + }], +}); + +const Bar = (monitor: number) => Widget.Window({ + monitor, + name: `bar${monitor}`, + anchor: ['top', 'left', 'right'], + exclusivity: 'exclusive', + child: Widget.CenterBox({ + start_widget: Widget.Label({ + hpack: 'center', + label: 'Welcome to AGS!', + }), + end_widget: Widget.Label({ + hpack: 'center', + label: time.bind(), + }), + }), +}); + +App.config({ + windows: [Bar(0)], +}); diff --git a/packages/ags/style.css b/packages/ags/style.css new file mode 100644 index 0000000..9ac7355 --- /dev/null +++ b/packages/ags/style.css @@ -0,0 +1,40 @@ +window.bar { + background-color: @theme_bg_color; + color: @theme_fg_color; +} + +button { + min-width: 0; + padding-top: 0; + padding-bottom: 0; + background-color: transparent; +} + +button:active { + background-color: @theme_selected_bg_color; +} + +button:hover { + border-bottom: 3px solid @theme_fg_color; +} + +label { + font-weight: bold; +} + +.workspaces button.focused { + border-bottom: 3px solid @theme_selected_bg_color; +} + +.client-title { + color: @theme_selected_bg_color; +} + +.notification { + color: yellow; +} + +levelbar block, +highlight { + min-height: 10px; +} diff --git a/packages/default.nix b/packages/default.nix new file mode 100644 index 0000000..1e6b9f4 --- /dev/null +++ b/packages/default.nix @@ -0,0 +1,12 @@ +{ ... }: +{ + imports = [ +# ./ags + ./waybar + ./wpaperd + ./nvim +# ./kitty +# ./hyprland +# ./vscode + ]; +} diff --git a/packages/hyprland/colors.conf b/packages/hyprland/colors.conf new file mode 100644 index 0000000..b435106 --- /dev/null +++ b/packages/hyprland/colors.conf @@ -0,0 +1,2 @@ +$borderActive = 0xffb072d1 +$borderInactive = 0xff292a37 diff --git a/packages/hyprland/default.nix b/packages/hyprland/default.nix new file mode 100644 index 0000000..d4b7dc3 --- /dev/null +++ b/packages/hyprland/default.nix @@ -0,0 +1,22 @@ +{ pkgs, config, lib,... }: +{ + home.packages = with pkgs; [ + hyprland + swww + wl-clipboard + pulseaudio + ]; + + home.file = + lib.mkMerge [ + { + ".config/hypr/hyprland.conf".source = ./hyprland.conf; + ".config/hypr/keybinds.conf".source = ./keybinds.conf; + ".config/hypr/rules.conf".source = ./rules.conf; + ".config/hypr/setup.conf".source = ./setup.conf; + ".config/hypr/startup.conf".source = ./startup.conf; + ".config/hypr/colors.conf".source = ./colors.conf; + ".config/hypr/theme.conf".source = ./theme.conf; + } + ]; +} diff --git a/packages/hyprland/hyprland.conf b/packages/hyprland/hyprland.conf new file mode 100644 index 0000000..fffc7ac --- /dev/null +++ b/packages/hyprland/hyprland.conf @@ -0,0 +1,12 @@ +#monitor=DP-3,1920x1080,-1920x0,1 +#monitor=DP-2,preferred,0x0,1 +monitor=eDP-1,preferred,0x0,1 + + +source = ~/.config/hypr/startup.conf +source = ~/.config/hypr/setup.conf +source = ~/.config/hypr/rules.conf +source = ~/.config/hypr/keybinds.conf +source = ~/.config/hypr/colors.conf +source = ~/.config/hypr/theme.conf + diff --git a/packages/hyprland/keybinds.conf b/packages/hyprland/keybinds.conf new file mode 100644 index 0000000..9d1fbe5 --- /dev/null +++ b/packages/hyprland/keybinds.conf @@ -0,0 +1,55 @@ +$mainMod = SUPER + +# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more +bind = $mainMod, RETURN, exec, kitty +bind = $mainMod, Q, exec, brave +bind = SUPER, BackSpace, killactive, +bind = SUPER_SHIFT, P, exec, sudo shutdown now +bind = SUPER_SHIFT, R, exec, sudo reboot now +bind = $mainMod, M, exec, hyprctl dispatch exit +#bind = $mainMod, E, exec, dolphin +bind = $mainMod, V, togglefloating, +# bind = $mainMod, R, exec, rofi -show drun +#bind = $mainMod, P, pseudo, # dwindle +bind = $mainMod, J, togglesplit, # dwindle +bind = $mainMod, F, fullscreen,1 +bind = SUPER_SHIFT, F, fullscreen,0 + +# Move focus with mainMod + arrow keys +bind = $mainMod, left, movefocus, l +bind = $mainMod, right, movefocus, r +bind = $mainMod, up, movefocus, u +bind = $mainMod, down, movefocus, d + +# Switch workspaces with mainMod + [0-9] +bind = $mainMod, 1, workspace, 1 +bind = $mainMod, 2, workspace, 2 +bind = $mainMod, 3, workspace, 3 +bind = $mainMod, 4, workspace, 4 +bind = $mainMod, 5, workspace, 5 +bind = $mainMod, 6, workspace, 6 +bind = $mainMod, 7, workspace, 7 +bind = $mainMod, 8, workspace, 8 +bind = $mainMod, 9, workspace, 9 +bind = $mainMod, 0, workspace, 10 + + +# Move active window to a workspace with mainMod + SHIFT + [0-9] +bind = $mainMod SHIFT, 1, movetoworkspace, 1 +bind = $mainMod SHIFT, 2, movetoworkspace, 2 +bind = $mainMod SHIFT, 3, movetoworkspace, 3 +bind = $mainMod SHIFT, 4, movetoworkspace, 4 +bind = $mainMod SHIFT, 5, movetoworkspace, 5 +bind = $mainMod SHIFT, 6, movetoworkspace, 6 +bind = $mainMod SHIFT, 7, movetoworkspace, 7 +bind = $mainMod SHIFT, 8, movetoworkspace, 8 +bind = $mainMod SHIFT, 9, movetoworkspace, 9 +bind = $mainMod SHIFT, 0, movetoworkspace, 10 + +# Scroll through existing workspaces with mainMod + scroll +bind = $mainMod, mouse_down, workspace, e+1 +bind = $mainMod, mouse_up, workspace, e-1 + +# Move/resize windows with mainMod + LMB/RMB and dragging +bindm = $mainMod, mouse:272, movewindow +bindm = $mainMod, mouse:273, resizewindow diff --git a/packages/hyprland/rules.conf b/packages/hyprland/rules.conf new file mode 100644 index 0000000..a3f2fa5 --- /dev/null +++ b/packages/hyprland/rules.conf @@ -0,0 +1,78 @@ +# ######## Window rules ######## +windowrule = noblur,.* +# windowrule = opacity 0.89 override 0.89 override, .* # Applies transparency to EVERY WINDOW +windowrule = float, ^(blueberry.py)$ +windowrule = float, ^(steam)$ +windowrule = float, ^(guifetch)$ # FlafyDev/guifetch +windowrulev2 = tile, class:(dev.warp.Warp) +windowrulev2 = float, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ +windowrule = center, title:^(Open File)(.*)$ +windowrule = center, title:^(Select a File)(.*)$ +windowrule = center, title:^(Choose wallpaper)(.*)$ +windowrule = center, title:^(Open Folder)(.*)$ +windowrule = center, title:^(Save As)(.*)$ +windowrule = center, title:^(Library)(.*)$ +windowrule = center, title:^(File Upload)(.*)$ + +# Dialogs +windowrule=float,title:^(Open File)(.*)$ +windowrule=float,title:^(Select a File)(.*)$ +windowrule=float,title:^(Choose wallpaper)(.*)$ +windowrule=float,title:^(Open Folder)(.*)$ +windowrule=float,title:^(Save As)(.*)$ +windowrule=float,title:^(Library)(.*)$ +windowrule=float,title:^(File Upload)(.*)$ + +# Tearing +windowrule=immediate,.*\.exe +windowrulev2=immediate,class:(steam_app) + +# No shadow for tiled windows +windowrulev2 = noshadow,floating:0 + +# ######## Layer rules ######## +layerrule = xray 1, .* +# layerrule = noanim, .* +layerrule = noanim, walker +layerrule = noanim, selection +layerrule = noanim, overview +layerrule = noanim, anyrun +layerrule = noanim, indicator.* +layerrule = noanim, osk +layerrule = noanim, hyprpicker +layerrule = blur, shell:* +layerrule = ignorealpha 0.6, shell:* + +layerrule = noanim, noanim +layerrule = blur, gtk-layer-shell +layerrule = ignorezero, gtk-layer-shell +layerrule = blur, launcher +layerrule = ignorealpha 0.5, launcher +layerrule = blur, notifications +layerrule = ignorealpha 0.69, notifications + +# ags +layerrule = animation slide top, sideleft.* +layerrule = animation slide top, sideright.* +layerrule = blur, session + +layerrule = blur, bar +layerrule = ignorealpha 0.6, bar +layerrule = blur, corner.* +layerrule = ignorealpha 0.6, corner.* +layerrule = blur, dock +layerrule = ignorealpha 0.6, dock +layerrule = blur, indicator.* +layerrule = ignorealpha 0.6, indicator.* +layerrule = blur, overview +layerrule = ignorealpha 0.6, overview +layerrule = blur, cheatsheet +layerrule = ignorealpha 0.6, cheatsheet +layerrule = blur, sideright +layerrule = ignorealpha 0.6, sideright +layerrule = blur, sideleft +layerrule = ignorealpha 0.6, sideleft +layerrule = blur, indicator* +layerrule = ignorealpha 0.6, indicator* +layerrule = blur, osk +layerrule = ignorealpha 0.6, osk diff --git a/packages/hyprland/setup.conf b/packages/hyprland/setup.conf new file mode 100644 index 0000000..56bf83f --- /dev/null +++ b/packages/hyprland/setup.conf @@ -0,0 +1,55 @@ +input { + kb_layout = de + kb_variant = + kb_model = + kb_options = + kb_rules = + + follow_mouse = 1 + + touchpad { + natural_scroll = no + } +} + +general { + #sensitivity=1 + + gaps_in=6 + gaps_out=12 + border_size=4 + col.active_border=$borderActive + col.inactive_border=$borderInactive +} + +misc { + disable_hyprland_logo=true + disable_splash_rendering=true +} + +decoration { + rounding = 8 + active_opacity = 0.95 + inactive_opacity = 0.83 + fullscreen_opacity = 1 + blur { + enabled = true + xray = true + } + drop_shadow=true + shadow_range=4 + col.shadow=0x66000000 +} + +animations { + enabled=1 + + animation=windows,1,4,default,slide + animation=border,1,5,default + animation=fade,1,5,default + animation=workspaces,1,3,default +} + +dwindle { + pseudotile=0 +} diff --git a/packages/hyprland/startup.conf b/packages/hyprland/startup.conf new file mode 100644 index 0000000..fd9927e --- /dev/null +++ b/packages/hyprland/startup.conf @@ -0,0 +1 @@ +exec-once=asztal diff --git a/packages/hyprland/theme.conf b/packages/hyprland/theme.conf new file mode 100644 index 0000000..147413f --- /dev/null +++ b/packages/hyprland/theme.conf @@ -0,0 +1,37 @@ +# exec = gsettings set org.gnome.desktop.interface icon-theme 'Papirus-Dark' +# exec = gsettings set org.gnome.desktop.interface gtk-theme 'Layan-Dark-Solid' +# exec = gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' + +general { + gaps_in = 3 + gaps_out = 8 + border_size = 2 + col.active_border = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg + col.inactive_border = rgba(b4befecc) rgba(6c7086cc) 45deg + layout = dwindle + resize_on_border = true + no_border_on_floating = true +} + +group { + col.border_active = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg + col.border_inactive = rgba(b4befecc) rgba(6c7086cc) 45deg + col.border_locked_active = rgba(ca9ee6ff) rgba(f2d5cfff) 45deg + col.border_locked_inactive = rgba(b4befecc) rgba(6c7086cc) 45deg +} + +decoration { + rounding = 10 + drop_shadow = false + + blur { + enabled = yes + size = 6 + passes = 3 + new_optimizations = on + ignore_opacity = on + xray = false + } +} + +layerrule = blur,waybar diff --git a/packages/kitty/default.nix b/packages/kitty/default.nix new file mode 100644 index 0000000..790c616 --- /dev/null +++ b/packages/kitty/default.nix @@ -0,0 +1,14 @@ + +{ pkgs, config, lib,... }: +{ + home.packages = with pkgs; [ + + ]; + + home.file = + lib.mkMerge [ + { + ".config/kitty/kitty.conf".source = ./kitty.conf; + } + ]; +} diff --git a/packages/kitty/kitty.conf b/packages/kitty/kitty.conf new file mode 100644 index 0000000..f37adf9 --- /dev/null +++ b/packages/kitty/kitty.conf @@ -0,0 +1,80 @@ +# vim:ft=kitty + +## name: Catppuccin Kitty Mocha +## author: Catppuccin Org +## license: MIT +## upstream: https://github.com/catppuccin/kitty/blob/main/themes/mocha.conf +## blurb: Soothing pastel theme for the high-spirited! + + + +# The basic colors +foreground #cdd6f4 +background #1e1e2e +selection_foreground #1e1e2e +selection_background #f5e0dc + +# Cursor colors +cursor #f5e0dc +cursor_text_color #1e1e2e + +# URL underline color when hovering with mouse +url_color #f5e0dc + +# Kitty window border colors +active_border_color #b4befe +inactive_border_color #6c7086 +bell_border_color #f9e2af + +# OS Window titlebar colors +wayland_titlebar_color system +macos_titlebar_color system + +# Tab bar colors +active_tab_foreground #11111b +active_tab_background #cba6f7 +inactive_tab_foreground #cdd6f4 +inactive_tab_background #181825 +tab_bar_background #11111b + +# Colors for marks (marked text in the terminal) +mark1_foreground #1e1e2e +mark1_background #b4befe +mark2_foreground #1e1e2e +mark2_background #cba6f7 +mark3_foreground #1e1e2e +mark3_background #74c7ec + +# The 16 terminal colors + +# black +color0 #45475a +color8 #585b70 + +# red +color1 #f38ba8 +color9 #f38ba8 + +# green +color2 #a6e3a1 +color10 #a6e3a1 + +# yellow +color3 #f9e2af +color11 #f9e2af + +# blue +color4 #89b4fa +color12 #89b4fa + +# magenta +color5 #f5c2e7 +color13 #f5c2e7 + +# cyan +color6 #94e2d5 +color14 #94e2d5 + +# white +color7 #bac2de +color15 #a6adc8 diff --git a/packages/nvim/default.nix b/packages/nvim/default.nix new file mode 100644 index 0000000..fce43b1 --- /dev/null +++ b/packages/nvim/default.nix @@ -0,0 +1,19 @@ +{ pkgs, config, lib,... }: +{ + + home.packages = with pkgs; [ + + ]; + +programs.neovim.plugins = [ + pkgs.vimPlugins.nerdtree + pkgs.vimPlugins.lazy-nvim +]; + + home.file = + lib.mkMerge [ + { + ".config/nvim/init.lua".source = ./init.lua; + } + ]; +} diff --git a/packages/nvim/init.lua b/packages/nvim/init.lua new file mode 100644 index 0000000..db8fc73 --- /dev/null +++ b/packages/nvim/init.lua @@ -0,0 +1,149 @@ +-- Tab +vim.opt.tabstop = 2 -- number of visual spaces per TAB +vim.opt.softtabstop = 2 -- number of spacesin tab when editing +vim.opt.shiftwidth = 2 -- insert 4 spaces on a tab +vim.opt.expandtab = true -- tabs are spaces, mainly because of python + +-- UI config +vim.opt.number = true -- show absolute number +vim.opt.relativenumber = true -- add numbers to each line on the left side +vim.opt.splitbelow = true -- open new vertical split bottom +vim.opt.splitright = true -- open new horizontal splits right +-- vim.opt.termguicolors = true -- enabl 24-bit RGB color in the TUI +vim.opt.showmode = false -- we are experienced, wo don't need the "-- INSERT --" mode hint + +-- Searching +vim.opt.incsearch = true -- search as characters are entered +vim.opt.hlsearch = false -- do not highlight matches +vim.opt.ignorecase = true -- ignore case in searches by default +vim.opt.smartcase = true -- but make it case sensitive if an uppercase is entered + +-- -- Keybinds +vim.keymap.set('n', '', 'h', opts) +vim.keymap.set('n', '', 'j', opts) +vim.keymap.set('n', '', 'k', opts) +vim.keymap.set('n', '', 'l', opts) + +-- Resize with arrows +-- delta: 2 lines +vim.keymap.set('n', '', ':resize -2', opts) +vim.keymap.set('n', '', ':resize +2', opts) +vim.keymap.set('n', '', ':vertical resize -2', opts) +vim.keymap.set('n', '', ':vertical resize +2', opts) + + +vim.keymap.set('i', 'jk', '', opts) + +-- Lazy +-- Bootstrap lazy.nvim +local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" +if not (vim.uv or vim.loop).fs_stat(lazypath) then + local lazyrepo = "https://github.com/folke/lazy.nvim.git" + local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath }) + if vim.v.shell_error ~= 0 then + vim.api.nvim_echo({ + { "Failed to clone lazy.nvim:\n", "ErrorMsg" }, + { out, "WarningMsg" }, + { "\nPress any key to exit..." }, + }, true, {}) + vim.fn.getchar() + os.exit(1) + end +end +vim.opt.rtp:prepend(lazypath) + +-- Make sure to setup `mapleader` and `maplocalleader` before +-- loading lazy.nvim so that mappings are correct. +-- This is also a good place to setup other settings (vim.opt) +vim.g.mapleader = " " +vim.g.maplocalleader = "\\" + +-- Setup lazy.nvim +require("lazy").setup({ + spec = { + -- add your plugins here + -- + -- + -- +{ + 'nvim-flutter/flutter-tools.nvim', + lazy = false, + dependencies = { + 'nvim-lua/plenary.nvim', + 'stevearc/dressing.nvim', -- optional for vim.ui.select + }, + config = true, +}, + + + + +{ + "nvim-treesitter/nvim-treesitter", + build = ":TSUpdate", + config = function () + local configs = require("nvim-treesitter.configs") + + configs.setup({ + ensure_installed = { "c", "lua", "html" }, + sync_install = false, + highlight = { enable = true }, + indent = { enable = true }, + }) + end + }, + + + { + "neovim/nvim-lspconfig", -- REQUIRED: for native Neovim LSP integration + lazy = false, -- REQUIRED: tell lazy.nvim to start this plugin at startup + dependencies = { + -- main one + { "ms-jpq/coq_nvim", branch = "coq" }, + + -- 9000+ Snippets + { "ms-jpq/coq.artifacts", branch = "artifacts" }, + + -- lua & third party sources -- See https://github.com/ms-jpq/coq.thirdparty + -- Need to **configure separately** + { 'ms-jpq/coq.thirdparty', branch = "3p" } + -- - shell repl + -- - nvim lua api + -- - scientific calculator + -- - comment banner + -- - etc + }, + init = function() + vim.g.coq_settings = { + auto_start = true, -- if you want to start COQ at startup + -- Your COQ settings here + } + end, + config = function() + -- Your LSP settings here + end, +} + + +{ + 'nvim-flutter/flutter-tools.nvim', + lazy = false, + dependencies = { + 'nvim-lua/plenary.nvim', + 'stevearc/dressing.nvim', -- optional for vim.ui.select + }, + config = true, +} + + + }, + -- Configure any other settings here. See the documentation for more details. + -- colorscheme that will be used when installing plugins. + install = { colorscheme = { "habamax" } }, + -- automatically check for plugin updates + checker = { enabled = true }, +}) + +require("flutter-tools").setup {} -- use defaults + + diff --git a/packages/p10k/.p10k.zsh b/packages/p10k/.p10k.zsh new file mode 100644 index 0000000..59ebee2 --- /dev/null +++ b/packages/p10k/.p10k.zsh @@ -0,0 +1,1744 @@ +# Generated by Powerlevel10k configuration wizard on 2024-07-16 at 14:43 CEST. +# Based on romkatv/powerlevel10k/config/p10k-classic.zsh. +# Wizard options: powerline, classic, unicode, dark, 24h time, angled separators, +# sharp heads, flat tails, 2 lines, disconnected, full frame, sparse, fluent, +# transient_prompt, instant_prompt=verbose. +# Type `p10k configure` to generate another config. +# +# Config for Powerlevel10k with classic powerline prompt style. Type `p10k configure` to generate +# your own config based on it. +# +# Tip: Looking for a nice color? Here's a one-liner to print colormap. +# +# for i in {0..255}; do print -Pn "%K{$i} %k%F{$i}${(l:3::0:)i}%f " ${${(M)$((i%6)):#3}:+$'\n'}; done + +# Temporarily change options. +'builtin' 'local' '-a' 'p10k_config_opts' +[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases') +[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob') +[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand') +'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand' + +() { + emulate -L zsh -o extended_glob + + # Unset all configuration options. This allows you to apply configuration changes without + # restarting zsh. Edit ~/.p10k.zsh and type `source ~/.p10k.zsh`. + unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR' + + # Zsh >= 5.1 is required. + [[ $ZSH_VERSION == (5.<1->*|<6->.*) ]] || return + + # The list of segments shown on the left. Fill it with the most important segments. + typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=( + # =========================[ Line #1 ]========================= + # os_icon # os identifier + dir # current directory + vcs # git status + # =========================[ Line #2 ]========================= + newline # \n + # prompt_char # prompt symbol + ) + + # The list of segments shown on the right. Fill it with less important segments. + # Right prompt on the last prompt line (where you are typing your commands) gets + # automatically hidden when the input line reaches it. Right prompt above the + # last prompt line gets hidden if it would overlap with left prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=( + # =========================[ Line #1 ]========================= + status # exit code of the last command + command_execution_time # duration of the last command + background_jobs # presence of background jobs + direnv # direnv status (https://direnv.net/) + asdf # asdf version manager (https://github.com/asdf-vm/asdf) + virtualenv # python virtual environment (https://docs.python.org/3/library/venv.html) + anaconda # conda environment (https://conda.io/) + pyenv # python environment (https://github.com/pyenv/pyenv) + goenv # go environment (https://github.com/syndbg/goenv) + nodenv # node.js version from nodenv (https://github.com/nodenv/nodenv) + nvm # node.js version from nvm (https://github.com/nvm-sh/nvm) + nodeenv # node.js environment (https://github.com/ekalinin/nodeenv) + # node_version # node.js version + # go_version # go version (https://golang.org) + # rust_version # rustc version (https://www.rust-lang.org) + # dotnet_version # .NET version (https://dotnet.microsoft.com) + # php_version # php version (https://www.php.net/) + # laravel_version # laravel php framework version (https://laravel.com/) + # java_version # java version (https://www.java.com/) + # package # name@version from package.json (https://docs.npmjs.com/files/package.json) + rbenv # ruby version from rbenv (https://github.com/rbenv/rbenv) + rvm # ruby version from rvm (https://rvm.io) + fvm # flutter version management (https://github.com/leoafarias/fvm) + luaenv # lua version from luaenv (https://github.com/cehoffman/luaenv) + jenv # java version from jenv (https://github.com/jenv/jenv) + plenv # perl version from plenv (https://github.com/tokuhirom/plenv) + perlbrew # perl version from perlbrew (https://github.com/gugod/App-perlbrew) + phpenv # php version from phpenv (https://github.com/phpenv/phpenv) + scalaenv # scala version from scalaenv (https://github.com/scalaenv/scalaenv) + haskell_stack # haskell version from stack (https://haskellstack.org/) + kubecontext # current kubernetes context (https://kubernetes.io/) + terraform # terraform workspace (https://www.terraform.io) + # terraform_version # terraform version (https://www.terraform.io) + aws # aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) + aws_eb_env # aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) + azure # azure account name (https://docs.microsoft.com/en-us/cli/azure) + gcloud # google cloud cli account and project (https://cloud.google.com/) + google_app_cred # google application credentials (https://cloud.google.com/docs/authentication/production) + toolbox # toolbox name (https://github.com/containers/toolbox) + context # user@hostname + nordvpn # nordvpn connection status, linux only (https://nordvpn.com/) + ranger # ranger shell (https://github.com/ranger/ranger) + yazi # yazi shell (https://github.com/sxyazi/yazi) + nnn # nnn shell (https://github.com/jarun/nnn) + lf # lf shell (https://github.com/gokcehan/lf) + xplr # xplr shell (https://github.com/sayanarijit/xplr) + vim_shell # vim shell indicator (:sh) + midnight_commander # midnight commander shell (https://midnight-commander.org/) + nix_shell # nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) + chezmoi_shell # chezmoi shell (https://www.chezmoi.io/) + vi_mode # vi mode (you don't need this if you've enabled prompt_char) + # vpn_ip # virtual private network indicator + # load # CPU load + # disk_usage # disk usage + # ram # free RAM + # swap # used swap + todo # todo items (https://github.com/todotxt/todo.txt-cli) + timewarrior # timewarrior tracking status (https://timewarrior.net/) + taskwarrior # taskwarrior task count (https://taskwarrior.org/) + per_directory_history # Oh My Zsh per-directory-history local/global indicator + # cpu_arch # CPU architecture + time # current time + # =========================[ Line #2 ]========================= + newline # \n + # ip # ip address and bandwidth usage for a specified network interface + # public_ip # public IP address + # proxy # system-wide http/https/ftp proxy + # battery # internal battery + # wifi # wifi speed + # example # example user-defined segment (see prompt_example function below) + ) + + # Defines character set used by powerlevel10k. It's best to let `p10k configure` set it for you. + typeset -g POWERLEVEL9K_MODE=powerline + # When set to `moderate`, some icons will have an extra space after them. This is meant to avoid + # icon overlap when using non-monospace fonts. When set to `none`, spaces are not added. + typeset -g POWERLEVEL9K_ICON_PADDING=none + + # When set to true, icons appear before content on both sides of the prompt. When set + # to false, icons go after content. If empty or not set, icons go before content in the left + # prompt and after content in the right prompt. + # + # You can also override it for a specific segment: + # + # POWERLEVEL9K_STATUS_ICON_BEFORE_CONTENT=false + # + # Or for a specific segment in specific state: + # + # POWERLEVEL9K_DIR_NOT_WRITABLE_ICON_BEFORE_CONTENT=false + typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT= + + # Add an empty line before each prompt. + typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=true + + # Connect left prompt lines with these symbols. You'll probably want to use the same color + # as POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND below. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX='%240F╭─' + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX='%240F├─' + typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX='%240F╰─' + # Connect right prompt lines with these symbols. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX='%240F─╮' + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX='%240F─┤' + typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX='%240F─╯' + + # Filler between left and right prompt on the first prompt line. You can set it to ' ', '·' or + # '─'. The last two make it easier to see the alignment between left and right prompt and to + # separate prompt from command output. You might want to set POWERLEVEL9K_PROMPT_ADD_NEWLINE=false + # for more compact prompt if using this option. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' ' + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_BACKGROUND= + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_GAP_BACKGROUND= + if [[ $POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR != ' ' ]]; then + # The color of the filler. You'll probably want to match the color of POWERLEVEL9K_MULTILINE + # ornaments defined above. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND=240 + # Start filler from the edge of the screen if there are no left segments on the first line. + typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_FIRST_SEGMENT_END_SYMBOL='%{%}' + # End filler on the edge of the screen if there are no right segments on the first line. + typeset -g POWERLEVEL9K_EMPTY_LINE_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='%{%}' + fi + + # Default background color. + typeset -g POWERLEVEL9K_BACKGROUND=236 + + # Separator between same-color segments on the left. + typeset -g POWERLEVEL9K_LEFT_SUBSEGMENT_SEPARATOR='%244F\uE0B1' + # Separator between same-color segments on the right. + typeset -g POWERLEVEL9K_RIGHT_SUBSEGMENT_SEPARATOR='%244F\uE0B3' + # Separator between different-color segments on the left. + typeset -g POWERLEVEL9K_LEFT_SEGMENT_SEPARATOR='\uE0B0' + # Separator between different-color segments on the right. + typeset -g POWERLEVEL9K_RIGHT_SEGMENT_SEPARATOR='\uE0B2' + # To remove a separator between two segments, add "_joined" to the second segment name. + # For example: POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(os_icon context_joined) + + # The right end of left prompt. + typeset -g POWERLEVEL9K_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL='\uE0B0' + # The left end of right prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='\uE0B2' + # The left end of left prompt. + typeset -g POWERLEVEL9K_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL='' + # The right end of right prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_LAST_SEGMENT_END_SYMBOL='' + # Left prompt terminator for lines without any segments. + typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL= + + #################################[ os_icon: os identifier ]################################## + # OS identifier color. + typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND=255 + # Custom icon. + # typeset -g POWERLEVEL9K_OS_ICON_CONTENT_EXPANSION='⭐' + + ################################[ prompt_char: prompt symbol ]################################ + # Transparent background. + typeset -g POWERLEVEL9K_PROMPT_CHAR_BACKGROUND= + # Green prompt symbol if the last command succeeded. + typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=76 + # Red prompt symbol if the last command failed. + typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=196 + # Default prompt symbol. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯' + # Prompt symbol in command vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮' + # Prompt symbol in visual vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='V' + # Prompt symbol in overwrite vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION='▶' + typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true + # No line terminator if prompt_char is the last segment. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL= + # No line introducer if prompt_char is the first segment. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL= + # No surrounding whitespace. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_{LEFT,RIGHT}_WHITESPACE= + + ##################################[ dir: current directory ]################################## + # Default current directory color. + typeset -g POWERLEVEL9K_DIR_FOREGROUND=31 + # If directory is too long, shorten some of its segments to the shortest possible unique + # prefix. The shortened directory can be tab-completed to the original. + typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_unique + # Replace removed segment suffixes with this symbol. + typeset -g POWERLEVEL9K_SHORTEN_DELIMITER= + # Color of the shortened directory segments. + typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND=103 + # Color of the anchor directory segments. Anchor segments are never shortened. The first + # segment is always an anchor. + typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND=39 + # Display anchor directory segments in bold. + typeset -g POWERLEVEL9K_DIR_ANCHOR_BOLD=true + # Don't shorten directories that contain any of these files. They are anchors. + local anchor_files=( + .bzr + .citc + .git + .hg + .node-version + .python-version + .go-version + .ruby-version + .lua-version + .java-version + .perl-version + .php-version + .tool-versions + .shorten_folder_marker + .svn + .terraform + CVS + Cargo.toml + composer.json + go.mod + package.json + stack.yaml + ) + typeset -g POWERLEVEL9K_SHORTEN_FOLDER_MARKER="(${(j:|:)anchor_files})" + # If set to "first" ("last"), remove everything before the first (last) subdirectory that contains + # files matching $POWERLEVEL9K_SHORTEN_FOLDER_MARKER. For example, when the current directory is + # /foo/bar/git_repo/nested_git_repo/baz, prompt will display git_repo/nested_git_repo/baz (first) + # or nested_git_repo/baz (last). This assumes that git_repo and nested_git_repo contain markers + # and other directories don't. + # + # Optionally, "first" and "last" can be followed by ":" where is an integer. + # This moves the truncation point to the right (positive offset) or to the left (negative offset) + # relative to the marker. Plain "first" and "last" are equivalent to "first:0" and "last:0" + # respectively. + typeset -g POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER=false + # Don't shorten this many last directory segments. They are anchors. + typeset -g POWERLEVEL9K_SHORTEN_DIR_LENGTH=1 + # Shorten directory if it's longer than this even if there is space for it. The value can + # be either absolute (e.g., '80') or a percentage of terminal width (e.g, '50%'). If empty, + # directory will be shortened only when prompt doesn't fit or when other parameters demand it + # (see POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS and POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT below). + # If set to `0`, directory will always be shortened to its minimum length. + typeset -g POWERLEVEL9K_DIR_MAX_LENGTH=80 + # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least this + # many columns for typing commands. + typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS=40 + # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least + # COLUMNS * POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT * 0.01 columns for typing commands. + typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT=50 + # If set to true, embed a hyperlink into the directory. Useful for quickly + # opening a directory in the file manager simply by clicking the link. + # Can also be handy when the directory is shortened, as it allows you to see + # the full directory that was used in previous commands. + typeset -g POWERLEVEL9K_DIR_HYPERLINK=false + + # Enable special styling for non-writable and non-existent directories. See POWERLEVEL9K_LOCK_ICON + # and POWERLEVEL9K_DIR_CLASSES below. + typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=v3 + + # The default icon shown next to non-writable and non-existent directories when + # POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3. + typeset -g POWERLEVEL9K_LOCK_ICON='∅' + + # POWERLEVEL9K_DIR_CLASSES allows you to specify custom icons and colors for different + # directories. It must be an array with 3 * N elements. Each triplet consists of: + # + # 1. A pattern against which the current directory ($PWD) is matched. Matching is done with + # extended_glob option enabled. + # 2. Directory class for the purpose of styling. + # 3. An empty string. + # + # Triplets are tried in order. The first triplet whose pattern matches $PWD wins. + # + # If POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3, non-writable and non-existent directories + # acquire class suffix _NOT_WRITABLE and NON_EXISTENT respectively. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_DIR_CLASSES=( + # '~/work(|/*)' WORK '' + # '~(|/*)' HOME '' + # '*' DEFAULT '') + # + # Whenever the current directory is ~/work or a subdirectory of ~/work, it gets styled with one + # of the following classes depending on its writability and existence: WORK, WORK_NOT_WRITABLE or + # WORK_NON_EXISTENT. + # + # Simply assigning classes to directories doesn't have any visible effects. It merely gives you an + # option to define custom colors and icons for different directory classes. + # + # # Styling for WORK. + # typeset -g POWERLEVEL9K_DIR_WORK_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_ANCHOR_FOREGROUND=39 + # + # # Styling for WORK_NOT_WRITABLE. + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_ANCHOR_FOREGROUND=39 + # + # # Styling for WORK_NON_EXISTENT. + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_ANCHOR_FOREGROUND=39 + # + # If a styling parameter isn't explicitly defined for some class, it falls back to the classless + # parameter. For example, if POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND is not set, it falls + # back to POWERLEVEL9K_DIR_FOREGROUND. + # + typeset -g POWERLEVEL9K_DIR_CLASSES=() + + # Custom prefix. + # typeset -g POWERLEVEL9K_DIR_PREFIX='%246Fin ' + + #####################################[ vcs: git status ]###################################### + # Branch icon. Set this parameter to '\UE0A0 ' for the popular Powerline branch icon. + typeset -g POWERLEVEL9K_VCS_BRANCH_ICON= + + # Untracked files icon. It's really a question mark, your font isn't broken. + # Change the value of this parameter to show a different icon. + typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='?' + + # Formatter for Git status. + # + # Example output: master wip ⇣42⇡42 *42 merge ~42 +42 !42 ?42. + # + # You can edit the function to customize how Git status looks. + # + # VCS_STATUS_* parameters are set by gitstatus plugin. See reference: + # https://github.com/romkatv/gitstatus/blob/master/gitstatus.plugin.zsh. + function my_git_formatter() { + emulate -L zsh + + if [[ -n $P9K_CONTENT ]]; then + # If P9K_CONTENT is not empty, use it. It's either "loading" or from vcs_info (not from + # gitstatus plugin). VCS_STATUS_* parameters are not available in this case. + typeset -g my_git_format=$P9K_CONTENT + return + fi + + if (( $1 )); then + # Styling for up-to-date Git status. + local meta='%246F' # grey foreground + local clean='%76F' # green foreground + local modified='%178F' # yellow foreground + local untracked='%39F' # blue foreground + local conflicted='%196F' # red foreground + else + # Styling for incomplete and stale Git status. + local meta='%244F' # grey foreground + local clean='%244F' # grey foreground + local modified='%244F' # grey foreground + local untracked='%244F' # grey foreground + local conflicted='%244F' # grey foreground + fi + + local res + + if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then + local branch=${(V)VCS_STATUS_LOCAL_BRANCH} + # If local branch name is at most 32 characters long, show it in full. + # Otherwise show the first 12 … the last 12. + # Tip: To always show local branch name in full without truncation, delete the next line. + (( $#branch > 32 )) && branch[13,-13]="…" # <-- this line + res+="${clean}${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}${branch//\%/%%}" + fi + + if [[ -n $VCS_STATUS_TAG + # Show tag only if not on a branch. + # Tip: To always show tag, delete the next line. + && -z $VCS_STATUS_LOCAL_BRANCH # <-- this line + ]]; then + local tag=${(V)VCS_STATUS_TAG} + # If tag name is at most 32 characters long, show it in full. + # Otherwise show the first 12 … the last 12. + # Tip: To always show tag name in full without truncation, delete the next line. + (( $#tag > 32 )) && tag[13,-13]="…" # <-- this line + res+="${meta}#${clean}${tag//\%/%%}" + fi + + # Display the current Git commit if there is no branch and no tag. + # Tip: To always display the current Git commit, delete the next line. + [[ -z $VCS_STATUS_LOCAL_BRANCH && -z $VCS_STATUS_TAG ]] && # <-- this line + res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}" + + # Show tracking branch name if it differs from local branch. + if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then + res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}" + fi + + # Display "wip" if the latest commit's summary contains "wip" or "WIP". + if [[ $VCS_STATUS_COMMIT_SUMMARY == (|*[^[:alnum:]])(wip|WIP)(|[^[:alnum:]]*) ]]; then + res+=" ${modified}wip" + fi + + if (( VCS_STATUS_COMMITS_AHEAD || VCS_STATUS_COMMITS_BEHIND )); then + # ⇣42 if behind the remote. + (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}" + # ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42. + (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" " + (( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}" + elif [[ -n $VCS_STATUS_REMOTE_BRANCH ]]; then + # Tip: Uncomment the next line to display '=' if up to date with the remote. + # res+=" ${clean}=" + fi + + # ⇠42 if behind the push remote. + (( VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}" + (( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" " + # ⇢42 if ahead of the push remote; no leading space if also behind: ⇠42⇢42. + (( VCS_STATUS_PUSH_COMMITS_AHEAD )) && res+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}" + # *42 if have stashes. + (( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}" + # 'merge' if the repo is in an unusual state. + [[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}" + # ~42 if have merge conflicts. + (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}" + # +42 if have staged changes. + (( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}" + # !42 if have unstaged changes. + (( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}" + # ?42 if have untracked files. It's really a question mark, your font isn't broken. + # See POWERLEVEL9K_VCS_UNTRACKED_ICON above if you want to use a different icon. + # Remove the next line if you don't want to see untracked files at all. + (( VCS_STATUS_NUM_UNTRACKED )) && res+=" ${untracked}${(g::)POWERLEVEL9K_VCS_UNTRACKED_ICON}${VCS_STATUS_NUM_UNTRACKED}" + # "─" if the number of unstaged files is unknown. This can happen due to + # POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY (see below) being set to a non-negative number lower + # than the number of files in the Git index, or due to bash.showDirtyState being set to false + # in the repository config. The number of staged and untracked files may also be unknown + # in this case. + (( VCS_STATUS_HAS_UNSTAGED == -1 )) && res+=" ${modified}─" + + typeset -g my_git_format=$res + } + functions -M my_git_formatter 2>/dev/null + + # Don't count the number of unstaged, untracked and conflicted files in Git repositories with + # more than this many files in the index. Negative value means infinity. + # + # If you are working in Git repositories with tens of millions of files and seeing performance + # sagging, try setting POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY to a number lower than the output + # of `git ls-files | wc -l`. Alternatively, add `bash.showDirtyState = false` to the repository's + # config: `git config bash.showDirtyState false`. + typeset -g POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY=-1 + + # Don't show Git status in prompt for repositories whose workdir matches this pattern. + # For example, if set to '~', the Git repository at $HOME/.git will be ignored. + # Multiple patterns can be combined with '|': '~(|/foo)|/bar/baz/*'. + typeset -g POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~' + + # Disable the default Git status formatting. + typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true + # Install our own Git status formatter. + typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter(1)))+${my_git_format}}' + typeset -g POWERLEVEL9K_VCS_LOADING_CONTENT_EXPANSION='${$((my_git_formatter(0)))+${my_git_format}}' + # Enable counters for staged, unstaged, etc. + typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1 + + # Icon color. + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR=76 + typeset -g POWERLEVEL9K_VCS_LOADING_VISUAL_IDENTIFIER_COLOR=244 + # Custom icon. + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + typeset -g POWERLEVEL9K_VCS_PREFIX='%246Fon ' + + # Show status of repositories of these types. You can add svn and/or hg if you are + # using them. If you do, your prompt may become slow even when your current directory + # isn't in an svn or hg reposotiry. + typeset -g POWERLEVEL9K_VCS_BACKENDS=(git) + + # These settings are used for repositories other than Git or when gitstatusd fails and + # Powerlevel10k has to fall back to using vcs_info. + typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=76 + typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=76 + typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=178 + + ##########################[ status: exit code of the last command ]########################### + # Enable OK_PIPE, ERROR_PIPE and ERROR_SIGNAL status states to allow us to enable, disable and + # style them independently from the regular OK and ERROR state. + typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true + + # Status on success. No content, just an icon. No need to show it if prompt_char is enabled as + # it will signify success by turning green. + typeset -g POWERLEVEL9K_STATUS_OK=true + typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND=70 + typeset -g POWERLEVEL9K_STATUS_OK_VISUAL_IDENTIFIER_EXPANSION='✔' + + # Status when some part of a pipe command fails but the overall exit status is zero. It may look + # like this: 1|0. + typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND=70 + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION='✔' + + # Status when it's just an error code (e.g., '1'). No need to show it if prompt_char is enabled as + # it will signify error by turning red. + typeset -g POWERLEVEL9K_STATUS_ERROR=true + typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND=160 + typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION='✘' + + # Status when the last command was terminated by a signal. + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND=160 + # Use terse signal names: "INT" instead of "SIGINT(2)". + typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=false + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION='✘' + + # Status when some part of a pipe command fails and the overall exit status is also non-zero. + # It may look like this: 1|0. + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND=160 + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION='✘' + + ###################[ command_execution_time: duration of the last command ]################### + # Show duration of the last command if takes at least this many seconds. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3 + # Show this many fractional digits. Zero means round to seconds. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0 + # Execution time color. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=248 + # Duration format: 1d 2h 3m 4s. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s' + # Custom icon. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PREFIX='%246Ftook ' + + #######################[ background_jobs: presence of background jobs ]####################### + # Don't show the number of background jobs. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE=false + # Background jobs color. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_FOREGROUND=37 + # Custom icon. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VISUAL_IDENTIFIER_EXPANSION='≡' + + #######################[ direnv: direnv status (https://direnv.net/) ]######################## + # Direnv color. + typeset -g POWERLEVEL9K_DIRENV_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_DIRENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ asdf: asdf version manager (https://github.com/asdf-vm/asdf) ]############### + # Default asdf color. Only used to display tools for which there is no color override (see below). + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_FOREGROUND. + typeset -g POWERLEVEL9K_ASDF_FOREGROUND=66 + + # There are four parameters that can be used to hide asdf tools. Each parameter describes + # conditions under which a tool gets hidden. Parameters can hide tools but not unhide them. If at + # least one parameter decides to hide a tool, that tool gets hidden. If no parameter decides to + # hide a tool, it gets shown. + # + # Special note on the difference between POWERLEVEL9K_ASDF_SOURCES and + # POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW. Consider the effect of the following commands: + # + # asdf local python 3.8.1 + # asdf global python 3.8.1 + # + # After running both commands the current python version is 3.8.1 and its source is "local" as + # it takes precedence over "global". If POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW is set to false, + # it'll hide python version in this case because 3.8.1 is the same as the global version. + # POWERLEVEL9K_ASDF_SOURCES will hide python version only if the value of this parameter doesn't + # contain "local". + + # Hide tool versions that don't come from one of these sources. + # + # Available sources: + # + # - shell `asdf current` says "set by ASDF_${TOOL}_VERSION environment variable" + # - local `asdf current` says "set by /some/not/home/directory/file" + # - global `asdf current` says "set by /home/username/file" + # + # Note: If this parameter is set to (shell local global), it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SOURCES. + typeset -g POWERLEVEL9K_ASDF_SOURCES=(shell local global) + + # If set to false, hide tool versions that are the same as global. + # + # Note: The name of this parameter doesn't reflect its meaning at all. + # Note: If this parameter is set to true, it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_PROMPT_ALWAYS_SHOW. + typeset -g POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW=false + + # If set to false, hide tool versions that are equal to "system". + # + # Note: If this parameter is set to true, it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_SYSTEM. + typeset -g POWERLEVEL9K_ASDF_SHOW_SYSTEM=true + + # If set to non-empty value, hide tools unless there is a file matching the specified file pattern + # in the current directory, or its parent directory, or its grandparent directory, and so on. + # + # Note: If this parameter is set to empty value, it won't hide tools. + # Note: SHOW_ON_UPGLOB isn't specific to asdf. It works with all prompt segments. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_ON_UPGLOB. + # + # Example: Hide nodejs version when there is no package.json and no *.js files in the current + # directory, in `..`, in `../..` and so on. + # + # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.js|package.json' + typeset -g POWERLEVEL9K_ASDF_SHOW_ON_UPGLOB= + + # Ruby version from asdf. + typeset -g POWERLEVEL9K_ASDF_RUBY_FOREGROUND=168 + # typeset -g POWERLEVEL9K_ASDF_RUBY_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_RUBY_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Python version from asdf. + typeset -g POWERLEVEL9K_ASDF_PYTHON_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_PYTHON_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PYTHON_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Go version from asdf. + typeset -g POWERLEVEL9K_ASDF_GOLANG_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_GOLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_GOLANG_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Node.js version from asdf. + typeset -g POWERLEVEL9K_ASDF_NODEJS_FOREGROUND=70 + # typeset -g POWERLEVEL9K_ASDF_NODEJS_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Rust version from asdf. + typeset -g POWERLEVEL9K_ASDF_RUST_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_RUST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_RUST_SHOW_ON_UPGLOB='*.foo|*.bar' + + # .NET Core version from asdf. + typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_FOREGROUND=134 + # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Flutter version from asdf. + typeset -g POWERLEVEL9K_ASDF_FLUTTER_FOREGROUND=38 + # typeset -g POWERLEVEL9K_ASDF_FLUTTER_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_FLUTTER_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Lua version from asdf. + typeset -g POWERLEVEL9K_ASDF_LUA_FOREGROUND=32 + # typeset -g POWERLEVEL9K_ASDF_LUA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_LUA_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Java version from asdf. + typeset -g POWERLEVEL9K_ASDF_JAVA_FOREGROUND=32 + # typeset -g POWERLEVEL9K_ASDF_JAVA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_JAVA_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Perl version from asdf. + typeset -g POWERLEVEL9K_ASDF_PERL_FOREGROUND=67 + # typeset -g POWERLEVEL9K_ASDF_PERL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PERL_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Erlang version from asdf. + typeset -g POWERLEVEL9K_ASDF_ERLANG_FOREGROUND=125 + # typeset -g POWERLEVEL9K_ASDF_ERLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_ERLANG_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Elixir version from asdf. + typeset -g POWERLEVEL9K_ASDF_ELIXIR_FOREGROUND=129 + # typeset -g POWERLEVEL9K_ASDF_ELIXIR_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_ELIXIR_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Postgres version from asdf. + typeset -g POWERLEVEL9K_ASDF_POSTGRES_FOREGROUND=31 + # typeset -g POWERLEVEL9K_ASDF_POSTGRES_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_POSTGRES_SHOW_ON_UPGLOB='*.foo|*.bar' + + # PHP version from asdf. + typeset -g POWERLEVEL9K_ASDF_PHP_FOREGROUND=99 + # typeset -g POWERLEVEL9K_ASDF_PHP_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PHP_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Haskell version from asdf. + typeset -g POWERLEVEL9K_ASDF_HASKELL_FOREGROUND=172 + # typeset -g POWERLEVEL9K_ASDF_HASKELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_HASKELL_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Julia version from asdf. + typeset -g POWERLEVEL9K_ASDF_JULIA_FOREGROUND=70 + # typeset -g POWERLEVEL9K_ASDF_JULIA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_JULIA_SHOW_ON_UPGLOB='*.foo|*.bar' + + ##########[ nordvpn: nordvpn connection status, linux only (https://nordvpn.com/) ]########### + # NordVPN connection indicator color. + typeset -g POWERLEVEL9K_NORDVPN_FOREGROUND=39 + # Hide NordVPN connection indicator when not connected. + typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_CONTENT_EXPANSION= + typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_VISUAL_IDENTIFIER_EXPANSION= + # Custom icon. + typeset -g POWERLEVEL9K_NORDVPN_VISUAL_IDENTIFIER_EXPANSION='nord' + + #################[ ranger: ranger shell (https://github.com/ranger/ranger) ]################## + # Ranger shell color. + typeset -g POWERLEVEL9K_RANGER_FOREGROUND=178 + # Custom icon. + typeset -g POWERLEVEL9K_RANGER_VISUAL_IDENTIFIER_EXPANSION='▲' + + ####################[ yazi: yazi shell (https://github.com/sxyazi/yazi) ]##################### + # Yazi shell color. + typeset -g POWERLEVEL9K_YAZI_FOREGROUND=178 + # Custom icon. + typeset -g POWERLEVEL9K_YAZI_VISUAL_IDENTIFIER_EXPANSION='▲' + + ######################[ nnn: nnn shell (https://github.com/jarun/nnn) ]####################### + # Nnn shell color. + typeset -g POWERLEVEL9K_NNN_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_NNN_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################[ lf: lf shell (https://github.com/gokcehan/lf) ]####################### + # lf shell color. + typeset -g POWERLEVEL9K_LF_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_LF_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################[ xplr: xplr shell (https://github.com/sayanarijit/xplr) ]################## + # xplr shell color. + typeset -g POWERLEVEL9K_XPLR_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_XPLR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########################[ vim_shell: vim shell indicator (:sh) ]########################### + # Vim shell indicator color. + typeset -g POWERLEVEL9K_VIM_SHELL_FOREGROUND=34 + # Custom icon. + # typeset -g POWERLEVEL9K_VIM_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######[ midnight_commander: midnight commander shell (https://midnight-commander.org/) ]###### + # Midnight Commander shell color. + typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ nix_shell: nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) ]## + # Nix shell color. + typeset -g POWERLEVEL9K_NIX_SHELL_FOREGROUND=74 + + # Display the icon of nix_shell if PATH contains a subdirectory of /nix/store. + # typeset -g POWERLEVEL9K_NIX_SHELL_INFER_FROM_PATH=false + + # Tip: If you want to see just the icon without "pure" and "impure", uncomment the next line. + # typeset -g POWERLEVEL9K_NIX_SHELL_CONTENT_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_NIX_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################[ chezmoi_shell: chezmoi shell (https://www.chezmoi.io/) ]################## + # chezmoi shell color. + typeset -g POWERLEVEL9K_CHEZMOI_SHELL_FOREGROUND=33 + # Custom icon. + # typeset -g POWERLEVEL9K_CHEZMOI_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################################[ disk_usage: disk usage ]################################## + # Colors for different levels of disk usage. + typeset -g POWERLEVEL9K_DISK_USAGE_NORMAL_FOREGROUND=35 + typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_FOREGROUND=220 + typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_FOREGROUND=160 + # Thresholds for different levels of disk usage (percentage points). + typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=90 + typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=95 + # If set to true, hide disk usage when below $POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL percent. + typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=false + # Custom icon. + # typeset -g POWERLEVEL9K_DISK_USAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ vi_mode: vi mode (you don't need this if you've enabled prompt_char) ]########### + # Text and color for normal (a.k.a. command) vi mode. + typeset -g POWERLEVEL9K_VI_COMMAND_MODE_STRING=NORMAL + typeset -g POWERLEVEL9K_VI_MODE_NORMAL_FOREGROUND=106 + # Text and color for visual vi mode. + typeset -g POWERLEVEL9K_VI_VISUAL_MODE_STRING=VISUAL + typeset -g POWERLEVEL9K_VI_MODE_VISUAL_FOREGROUND=68 + # Text and color for overtype (a.k.a. overwrite and replace) vi mode. + typeset -g POWERLEVEL9K_VI_OVERWRITE_MODE_STRING=OVERTYPE + typeset -g POWERLEVEL9K_VI_MODE_OVERWRITE_FOREGROUND=172 + # Text and color for insert vi mode. + typeset -g POWERLEVEL9K_VI_INSERT_MODE_STRING= + typeset -g POWERLEVEL9K_VI_MODE_INSERT_FOREGROUND=66 + # Custom icon. + # typeset -g POWERLEVEL9K_VI_MODE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################################[ ram: free RAM ]####################################### + # RAM color. + typeset -g POWERLEVEL9K_RAM_FOREGROUND=66 + # Custom icon. + # typeset -g POWERLEVEL9K_RAM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################################[ swap: used swap ]###################################### + # Swap color. + typeset -g POWERLEVEL9K_SWAP_FOREGROUND=96 + # Custom icon. + # typeset -g POWERLEVEL9K_SWAP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################################[ load: CPU load ]###################################### + # Show average CPU load over this many last minutes. Valid values are 1, 5 and 15. + typeset -g POWERLEVEL9K_LOAD_WHICH=5 + # Load color when load is under 50%. + typeset -g POWERLEVEL9K_LOAD_NORMAL_FOREGROUND=66 + # Load color when load is between 50% and 70%. + typeset -g POWERLEVEL9K_LOAD_WARNING_FOREGROUND=178 + # Load color when load is over 70%. + typeset -g POWERLEVEL9K_LOAD_CRITICAL_FOREGROUND=166 + # Custom icon. + # typeset -g POWERLEVEL9K_LOAD_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ todo: todo items (https://github.com/todotxt/todo.txt-cli) ]################ + # Todo color. + typeset -g POWERLEVEL9K_TODO_FOREGROUND=110 + # Hide todo when the total number of tasks is zero. + typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_TOTAL=true + # Hide todo when the number of tasks after filtering is zero. + typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_FILTERED=false + + # Todo format. The following parameters are available within the expansion. + # + # - P9K_TODO_TOTAL_TASK_COUNT The total number of tasks. + # - P9K_TODO_FILTERED_TASK_COUNT The number of tasks after filtering. + # + # These variables correspond to the last line of the output of `todo.sh -p ls`: + # + # TODO: 24 of 42 tasks shown + # + # Here 24 is P9K_TODO_FILTERED_TASK_COUNT and 42 is P9K_TODO_TOTAL_TASK_COUNT. + # + # typeset -g POWERLEVEL9K_TODO_CONTENT_EXPANSION='$P9K_TODO_FILTERED_TASK_COUNT' + + # Custom icon. + # typeset -g POWERLEVEL9K_TODO_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ timewarrior: timewarrior tracking status (https://timewarrior.net/) ]############ + # Timewarrior color. + typeset -g POWERLEVEL9K_TIMEWARRIOR_FOREGROUND=110 + # If the tracked task is longer than 24 characters, truncate and append "…". + # Tip: To always display tasks without truncation, delete the following parameter. + # Tip: To hide task names and display just the icon when time tracking is enabled, set the + # value of the following parameter to "". + typeset -g POWERLEVEL9K_TIMEWARRIOR_CONTENT_EXPANSION='${P9K_CONTENT:0:24}${${P9K_CONTENT:24}:+…}' + + # Custom icon. + # typeset -g POWERLEVEL9K_TIMEWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############[ taskwarrior: taskwarrior task count (https://taskwarrior.org/) ]############## + # Taskwarrior color. + typeset -g POWERLEVEL9K_TASKWARRIOR_FOREGROUND=74 + + # Taskwarrior segment format. The following parameters are available within the expansion. + # + # - P9K_TASKWARRIOR_PENDING_COUNT The number of pending tasks: `task +PENDING count`. + # - P9K_TASKWARRIOR_OVERDUE_COUNT The number of overdue tasks: `task +OVERDUE count`. + # + # Zero values are represented as empty parameters. + # + # The default format: + # + # '${P9K_TASKWARRIOR_OVERDUE_COUNT:+"!$P9K_TASKWARRIOR_OVERDUE_COUNT/"}$P9K_TASKWARRIOR_PENDING_COUNT' + # + # typeset -g POWERLEVEL9K_TASKWARRIOR_CONTENT_EXPANSION='$P9K_TASKWARRIOR_PENDING_COUNT' + + # Custom icon. + # typeset -g POWERLEVEL9K_TASKWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######[ per_directory_history: Oh My Zsh per-directory-history local/global indicator ]####### + # Color when using local/global history. + typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_FOREGROUND=135 + typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_FOREGROUND=130 + + # Tip: Uncomment the next two lines to hide "local"/"global" text and leave just the icon. + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_CONTENT_EXPANSION='' + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_CONTENT_EXPANSION='' + + # Custom icon. + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################################[ cpu_arch: CPU architecture ]################################ + # CPU architecture color. + typeset -g POWERLEVEL9K_CPU_ARCH_FOREGROUND=172 + + # Hide the segment when on a specific CPU architecture. + # typeset -g POWERLEVEL9K_CPU_ARCH_X86_64_CONTENT_EXPANSION= + # typeset -g POWERLEVEL9K_CPU_ARCH_X86_64_VISUAL_IDENTIFIER_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_CPU_ARCH_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################################[ context: user@hostname ]################################## + # Context color when running with privileges. + typeset -g POWERLEVEL9K_CONTEXT_ROOT_FOREGROUND=178 + # Context color in SSH without privileges. + typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_FOREGROUND=180 + # Default context color (no privileges, no SSH). + typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND=180 + + # Context format when running with privileges: bold user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%B%n@%m' + # Context format when in SSH without privileges: user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_TEMPLATE='%n@%m' + # Default context format (no privileges, no SSH): user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%n@%m' + + # Don't show context unless running with privileges or in SSH. + # Tip: Remove the next line to always show context. + typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_{CONTENT,VISUAL_IDENTIFIER}_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_CONTEXT_VISUAL_IDENTIFIER_EXPANSION='⭐' + # Custom prefix. + typeset -g POWERLEVEL9K_CONTEXT_PREFIX='%246Fwith ' + + ###[ virtualenv: python virtual environment (https://docs.python.org/3/library/venv.html) ]### + # Python virtual environment color. + typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=37 + # Don't show Python version next to the virtual environment name. + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false + # If set to "false", won't show virtualenv if pyenv is already shown. + # If set to "if-different", won't show virtualenv if it's the same as pyenv. + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_WITH_PYENV=false + # Separate environment name from Python version only with a space. + typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER= + # Custom icon. + # typeset -g POWERLEVEL9K_VIRTUALENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################[ anaconda: conda environment (https://conda.io/) ]###################### + # Anaconda environment color. + typeset -g POWERLEVEL9K_ANACONDA_FOREGROUND=37 + + # Anaconda segment format. The following parameters are available within the expansion. + # + # - CONDA_PREFIX Absolute path to the active Anaconda/Miniconda environment. + # - CONDA_DEFAULT_ENV Name of the active Anaconda/Miniconda environment. + # - CONDA_PROMPT_MODIFIER Configurable prompt modifier (see below). + # - P9K_ANACONDA_PYTHON_VERSION Current python version (python --version). + # + # CONDA_PROMPT_MODIFIER can be configured with the following command: + # + # conda config --set env_prompt '({default_env}) ' + # + # The last argument is a Python format string that can use the following variables: + # + # - prefix The same as CONDA_PREFIX. + # - default_env The same as CONDA_DEFAULT_ENV. + # - name The last segment of CONDA_PREFIX. + # - stacked_env Comma-separated list of names in the environment stack. The first element is + # always the same as default_env. + # + # Note: '({default_env}) ' is the default value of env_prompt. + # + # The default value of POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION expands to $CONDA_PROMPT_MODIFIER + # without the surrounding parentheses, or to the last path component of CONDA_PREFIX if the former + # is empty. + typeset -g POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION='${${${${CONDA_PROMPT_MODIFIER#\(}% }%\)}:-${CONDA_PREFIX:t}}' + + # Custom icon. + # typeset -g POWERLEVEL9K_ANACONDA_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ pyenv: python environment (https://github.com/pyenv/pyenv) ]################ + # Pyenv color. + typeset -g POWERLEVEL9K_PYENV_FOREGROUND=37 + # Hide python version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PYENV_SOURCES=(shell local global) + # If set to false, hide python version if it's the same as global: + # $(pyenv version-name) == $(pyenv global). + typeset -g POWERLEVEL9K_PYENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide python version if it's equal to "system". + typeset -g POWERLEVEL9K_PYENV_SHOW_SYSTEM=true + + # Pyenv segment format. The following parameters are available within the expansion. + # + # - P9K_CONTENT Current pyenv environment (pyenv version-name). + # - P9K_PYENV_PYTHON_VERSION Current python version (python --version). + # + # The default format has the following logic: + # + # 1. Display just "$P9K_CONTENT" if it's equal to "$P9K_PYENV_PYTHON_VERSION" or + # starts with "$P9K_PYENV_PYTHON_VERSION/". + # 2. Otherwise display "$P9K_CONTENT $P9K_PYENV_PYTHON_VERSION". + typeset -g POWERLEVEL9K_PYENV_CONTENT_EXPANSION='${P9K_CONTENT}${${P9K_CONTENT:#$P9K_PYENV_PYTHON_VERSION(|/*)}:+ $P9K_PYENV_PYTHON_VERSION}' + + # Custom icon. + # typeset -g POWERLEVEL9K_PYENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ goenv: go environment (https://github.com/syndbg/goenv) ]################ + # Goenv color. + typeset -g POWERLEVEL9K_GOENV_FOREGROUND=37 + # Hide go version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_GOENV_SOURCES=(shell local global) + # If set to false, hide go version if it's the same as global: + # $(goenv version-name) == $(goenv global). + typeset -g POWERLEVEL9K_GOENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide go version if it's equal to "system". + typeset -g POWERLEVEL9K_GOENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_GOENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ nodenv: node.js version from nodenv (https://github.com/nodenv/nodenv) ]########## + # Nodenv color. + typeset -g POWERLEVEL9K_NODENV_FOREGROUND=70 + # Hide node version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_NODENV_SOURCES=(shell local global) + # If set to false, hide node version if it's the same as global: + # $(nodenv version-name) == $(nodenv global). + typeset -g POWERLEVEL9K_NODENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide node version if it's equal to "system". + typeset -g POWERLEVEL9K_NODENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_NODENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############[ nvm: node.js version from nvm (https://github.com/nvm-sh/nvm) ]############### + # Nvm color. + typeset -g POWERLEVEL9K_NVM_FOREGROUND=70 + # If set to false, hide node version if it's the same as default: + # $(nvm version current) == $(nvm version default). + typeset -g POWERLEVEL9K_NVM_PROMPT_ALWAYS_SHOW=false + # If set to false, hide node version if it's equal to "system". + typeset -g POWERLEVEL9K_NVM_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_NVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ############[ nodeenv: node.js environment (https://github.com/ekalinin/nodeenv) ]############ + # Nodeenv color. + typeset -g POWERLEVEL9K_NODEENV_FOREGROUND=70 + # Don't show Node version next to the environment name. + typeset -g POWERLEVEL9K_NODEENV_SHOW_NODE_VERSION=false + # Separate environment name from Node version only with a space. + typeset -g POWERLEVEL9K_NODEENV_{LEFT,RIGHT}_DELIMITER= + # Custom icon. + # typeset -g POWERLEVEL9K_NODEENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############################[ node_version: node.js version ]############################### + # Node version color. + typeset -g POWERLEVEL9K_NODE_VERSION_FOREGROUND=70 + # Show node version only when in a directory tree containing package.json. + typeset -g POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_NODE_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ go_version: go version (https://golang.org) ]######################## + # Go version color. + typeset -g POWERLEVEL9K_GO_VERSION_FOREGROUND=37 + # Show go version only when in a go project subdirectory. + typeset -g POWERLEVEL9K_GO_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_GO_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #################[ rust_version: rustc version (https://www.rust-lang.org) ]################## + # Rust version color. + typeset -g POWERLEVEL9K_RUST_VERSION_FOREGROUND=37 + # Show rust version only when in a rust project subdirectory. + typeset -g POWERLEVEL9K_RUST_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_RUST_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ dotnet_version: .NET version (https://dotnet.microsoft.com) ]################ + # .NET version color. + typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND=134 + # Show .NET version only when in a .NET project subdirectory. + typeset -g POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_DOTNET_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################[ php_version: php version (https://www.php.net/) ]###################### + # PHP version color. + typeset -g POWERLEVEL9K_PHP_VERSION_FOREGROUND=99 + # Show PHP version only when in a PHP project subdirectory. + typeset -g POWERLEVEL9K_PHP_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_PHP_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ laravel_version: laravel php framework version (https://laravel.com/) ]########### + # Laravel version color. + typeset -g POWERLEVEL9K_LARAVEL_VERSION_FOREGROUND=161 + # Custom icon. + # typeset -g POWERLEVEL9K_LARAVEL_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ####################[ java_version: java version (https://www.java.com/) ]#################### + # Java version color. + typeset -g POWERLEVEL9K_JAVA_VERSION_FOREGROUND=32 + # Show java version only when in a java project subdirectory. + typeset -g POWERLEVEL9K_JAVA_VERSION_PROJECT_ONLY=true + # Show brief version. + typeset -g POWERLEVEL9K_JAVA_VERSION_FULL=false + # Custom icon. + # typeset -g POWERLEVEL9K_JAVA_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###[ package: name@version from package.json (https://docs.npmjs.com/files/package.json) ]#### + # Package color. + typeset -g POWERLEVEL9K_PACKAGE_FOREGROUND=117 + # Package format. The following parameters are available within the expansion. + # + # - P9K_PACKAGE_NAME The value of `name` field in package.json. + # - P9K_PACKAGE_VERSION The value of `version` field in package.json. + # + # typeset -g POWERLEVEL9K_PACKAGE_CONTENT_EXPANSION='${P9K_PACKAGE_NAME//\%/%%}@${P9K_PACKAGE_VERSION//\%/%%}' + # Custom icon. + # typeset -g POWERLEVEL9K_PACKAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ rbenv: ruby version from rbenv (https://github.com/rbenv/rbenv) ]############## + # Rbenv color. + typeset -g POWERLEVEL9K_RBENV_FOREGROUND=168 + # Hide ruby version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_RBENV_SOURCES=(shell local global) + # If set to false, hide ruby version if it's the same as global: + # $(rbenv version-name) == $(rbenv global). + typeset -g POWERLEVEL9K_RBENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide ruby version if it's equal to "system". + typeset -g POWERLEVEL9K_RBENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_RBENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ rvm: ruby version from rvm (https://rvm.io) ]######################## + # Rvm color. + typeset -g POWERLEVEL9K_RVM_FOREGROUND=168 + # Don't show @gemset at the end. + typeset -g POWERLEVEL9K_RVM_SHOW_GEMSET=false + # Don't show ruby- at the front. + typeset -g POWERLEVEL9K_RVM_SHOW_PREFIX=false + # Custom icon. + # typeset -g POWERLEVEL9K_RVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ fvm: flutter version management (https://github.com/leoafarias/fvm) ]############ + # Fvm color. + typeset -g POWERLEVEL9K_FVM_FOREGROUND=38 + # Custom icon. + # typeset -g POWERLEVEL9K_FVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ luaenv: lua version from luaenv (https://github.com/cehoffman/luaenv) ]########### + # Lua color. + typeset -g POWERLEVEL9K_LUAENV_FOREGROUND=32 + # Hide lua version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_LUAENV_SOURCES=(shell local global) + # If set to false, hide lua version if it's the same as global: + # $(luaenv version-name) == $(luaenv global). + typeset -g POWERLEVEL9K_LUAENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide lua version if it's equal to "system". + typeset -g POWERLEVEL9K_LUAENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_LUAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ jenv: java version from jenv (https://github.com/jenv/jenv) ]################ + # Java color. + typeset -g POWERLEVEL9K_JENV_FOREGROUND=32 + # Hide java version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_JENV_SOURCES=(shell local global) + # If set to false, hide java version if it's the same as global: + # $(jenv version-name) == $(jenv global). + typeset -g POWERLEVEL9K_JENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide java version if it's equal to "system". + typeset -g POWERLEVEL9K_JENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_JENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ plenv: perl version from plenv (https://github.com/tokuhirom/plenv) ]############ + # Perl color. + typeset -g POWERLEVEL9K_PLENV_FOREGROUND=67 + # Hide perl version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PLENV_SOURCES=(shell local global) + # If set to false, hide perl version if it's the same as global: + # $(plenv version-name) == $(plenv global). + typeset -g POWERLEVEL9K_PLENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide perl version if it's equal to "system". + typeset -g POWERLEVEL9K_PLENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_PLENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ perlbrew: perl version from perlbrew (https://github.com/gugod/App-perlbrew) ]############ + # Perlbrew color. + typeset -g POWERLEVEL9K_PERLBREW_FOREGROUND=67 + # Show perlbrew version only when in a perl project subdirectory. + typeset -g POWERLEVEL9K_PERLBREW_PROJECT_ONLY=true + # Don't show "perl-" at the front. + typeset -g POWERLEVEL9K_PERLBREW_SHOW_PREFIX=false + # Custom icon. + # typeset -g POWERLEVEL9K_PERLBREW_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ############[ phpenv: php version from phpenv (https://github.com/phpenv/phpenv) ]############ + # PHP color. + typeset -g POWERLEVEL9K_PHPENV_FOREGROUND=99 + # Hide php version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PHPENV_SOURCES=(shell local global) + # If set to false, hide php version if it's the same as global: + # $(phpenv version-name) == $(phpenv global). + typeset -g POWERLEVEL9K_PHPENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide php version if it's equal to "system". + typeset -g POWERLEVEL9K_PHPENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_PHPENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######[ scalaenv: scala version from scalaenv (https://github.com/scalaenv/scalaenv) ]####### + # Scala color. + typeset -g POWERLEVEL9K_SCALAENV_FOREGROUND=160 + # Hide scala version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_SCALAENV_SOURCES=(shell local global) + # If set to false, hide scala version if it's the same as global: + # $(scalaenv version-name) == $(scalaenv global). + typeset -g POWERLEVEL9K_SCALAENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide scala version if it's equal to "system". + typeset -g POWERLEVEL9K_SCALAENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_SCALAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ haskell_stack: haskell version from stack (https://haskellstack.org/) ]########### + # Haskell color. + typeset -g POWERLEVEL9K_HASKELL_STACK_FOREGROUND=172 + # Hide haskell version if it doesn't come from one of these sources. + # + # shell: version is set by STACK_YAML + # local: version is set by stack.yaml up the directory tree + # global: version is set by the implicit global project (~/.stack/global-project/stack.yaml) + typeset -g POWERLEVEL9K_HASKELL_STACK_SOURCES=(shell local) + # If set to false, hide haskell version if it's the same as in the implicit global project. + typeset -g POWERLEVEL9K_HASKELL_STACK_ALWAYS_SHOW=true + # Custom icon. + # typeset -g POWERLEVEL9K_HASKELL_STACK_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ terraform: terraform workspace (https://www.terraform.io) ]################# + # Don't show terraform workspace if it's literally "default". + typeset -g POWERLEVEL9K_TERRAFORM_SHOW_DEFAULT=false + # POWERLEVEL9K_TERRAFORM_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current terraform workspace gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_TERRAFORM_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_TERRAFORM_CLASSES defines the workspace class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' OTHER) + # + # If your current terraform workspace is "project_test", its class is TEST because "project_test" + # doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' OTHER) + typeset -g POWERLEVEL9K_TERRAFORM_OTHER_FOREGROUND=38 + # typeset -g POWERLEVEL9K_TERRAFORM_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ terraform_version: terraform version (https://www.terraform.io) ]############## + # Terraform version color. + typeset -g POWERLEVEL9K_TERRAFORM_VERSION_FOREGROUND=38 + # Custom icon. + # typeset -g POWERLEVEL9K_TERRAFORM_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ kubecontext: current kubernetes context (https://kubernetes.io/) ]############# + # Show kubecontext only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show kubecontext. + typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND='kubectl|helm|kubens|kubectx|oc|istioctl|kogito|k9s|helmfile|flux|fluxctl|stern|kubeseal|skaffold|kubent|kubecolor|cmctl|sparkctl' + + # Kubernetes context classes for the purpose of using different colors, icons and expansions with + # different contexts. + # + # POWERLEVEL9K_KUBECONTEXT_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current kubernetes context gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_KUBECONTEXT_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_KUBECONTEXT_CLASSES defines the context class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' DEFAULT) + # + # If your current kubernetes context is "deathray-testing/default", its class is TEST + # because "deathray-testing/default" doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_FOREGROUND=134 + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='○' + + # Use POWERLEVEL9K_KUBECONTEXT_CONTENT_EXPANSION to specify the content displayed by kubecontext + # segment. Parameter expansions are very flexible and fast, too. See reference: + # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. + # + # Within the expansion the following parameters are always available: + # + # - P9K_CONTENT The content that would've been displayed if there was no content + # expansion defined. + # - P9K_KUBECONTEXT_NAME The current context's name. Corresponds to column NAME in the + # output of `kubectl config get-contexts`. + # - P9K_KUBECONTEXT_CLUSTER The current context's cluster. Corresponds to column CLUSTER in the + # output of `kubectl config get-contexts`. + # - P9K_KUBECONTEXT_NAMESPACE The current context's namespace. Corresponds to column NAMESPACE + # in the output of `kubectl config get-contexts`. If there is no + # namespace, the parameter is set to "default". + # - P9K_KUBECONTEXT_USER The current context's user. Corresponds to column AUTHINFO in the + # output of `kubectl config get-contexts`. + # + # If the context points to Google Kubernetes Engine (GKE) or Elastic Kubernetes Service (EKS), + # the following extra parameters are available: + # + # - P9K_KUBECONTEXT_CLOUD_NAME Either "gke" or "eks". + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT Account/project ID. + # - P9K_KUBECONTEXT_CLOUD_ZONE Availability zone. + # - P9K_KUBECONTEXT_CLOUD_CLUSTER Cluster. + # + # P9K_KUBECONTEXT_CLOUD_* parameters are derived from P9K_KUBECONTEXT_CLUSTER. For example, + # if P9K_KUBECONTEXT_CLUSTER is "gke_my-account_us-east1-a_my-cluster-01": + # + # - P9K_KUBECONTEXT_CLOUD_NAME=gke + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=my-account + # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east1-a + # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 + # + # If P9K_KUBECONTEXT_CLUSTER is "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster-01": + # + # - P9K_KUBECONTEXT_CLOUD_NAME=eks + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=123456789012 + # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east-1 + # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION= + # Show P9K_KUBECONTEXT_CLOUD_CLUSTER if it's not empty and fall back to P9K_KUBECONTEXT_NAME. + POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${P9K_KUBECONTEXT_CLOUD_CLUSTER:-${P9K_KUBECONTEXT_NAME}}' + # Append the current context's namespace if it's not "default". + POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${${:-/$P9K_KUBECONTEXT_NAMESPACE}:#/default}' + + # Custom prefix. + typeset -g POWERLEVEL9K_KUBECONTEXT_PREFIX='%246Fat ' + + #[ aws: aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) ]# + # Show aws only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show aws. + typeset -g POWERLEVEL9K_AWS_SHOW_ON_COMMAND='aws|awless|cdk|terraform|pulumi|terragrunt' + + # POWERLEVEL9K_AWS_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current AWS profile gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_AWS_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_AWS_CLASSES defines the profile class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_AWS_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' DEFAULT) + # + # If your current AWS profile is "company_test", its class is TEST + # because "company_test" doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_AWS_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_AWS_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_AWS_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_AWS_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_AWS_DEFAULT_FOREGROUND=208 + # typeset -g POWERLEVEL9K_AWS_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # AWS segment format. The following parameters are available within the expansion. + # + # - P9K_AWS_PROFILE The name of the current AWS profile. + # - P9K_AWS_REGION The region associated with the current AWS profile. + typeset -g POWERLEVEL9K_AWS_CONTENT_EXPANSION='${P9K_AWS_PROFILE//\%/%%}${P9K_AWS_REGION:+ ${P9K_AWS_REGION//\%/%%}}' + + #[ aws_eb_env: aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) ]# + # AWS Elastic Beanstalk environment color. + typeset -g POWERLEVEL9K_AWS_EB_ENV_FOREGROUND=70 + # Custom icon. + typeset -g POWERLEVEL9K_AWS_EB_ENV_VISUAL_IDENTIFIER_EXPANSION='eb' + + ##########[ azure: azure account name (https://docs.microsoft.com/en-us/cli/azure) ]########## + # Show azure only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show azure. + typeset -g POWERLEVEL9K_AZURE_SHOW_ON_COMMAND='az|terraform|pulumi|terragrunt' + + # POWERLEVEL9K_AZURE_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current azure account name gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_AZURE_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_AZURE_CLASSES defines the account class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_AZURE_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' OTHER) + # + # If your current azure account is "company_test", its class is TEST because "company_test" + # doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_AZURE_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_AZURE_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_AZURE_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_AZURE_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' OTHER) + + # Azure account name color. + typeset -g POWERLEVEL9K_AZURE_OTHER_FOREGROUND=32 + # Custom icon. + # typeset -g POWERLEVEL9K_AZURE_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ gcloud: google cloud account and project (https://cloud.google.com/) ]########### + # Show gcloud only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show gcloud. + typeset -g POWERLEVEL9K_GCLOUD_SHOW_ON_COMMAND='gcloud|gcs|gsutil' + # Google cloud color. + typeset -g POWERLEVEL9K_GCLOUD_FOREGROUND=32 + + # Google cloud format. Change the value of POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION and/or + # POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION if the default is too verbose or not informative + # enough. You can use the following parameters in the expansions. Each of them corresponds to the + # output of `gcloud` tool. + # + # Parameter | Source + # -------------------------|-------------------------------------------------------------------- + # P9K_GCLOUD_CONFIGURATION | gcloud config configurations list --format='value(name)' + # P9K_GCLOUD_ACCOUNT | gcloud config get-value account + # P9K_GCLOUD_PROJECT_ID | gcloud config get-value project + # P9K_GCLOUD_PROJECT_NAME | gcloud projects describe $P9K_GCLOUD_PROJECT_ID --format='value(name)' + # + # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced with '%%'. + # + # Obtaining project name requires sending a request to Google servers. This can take a long time + # and even fail. When project name is unknown, P9K_GCLOUD_PROJECT_NAME is not set and gcloud + # prompt segment is in state PARTIAL. When project name gets known, P9K_GCLOUD_PROJECT_NAME gets + # set and gcloud prompt segment transitions to state COMPLETE. + # + # You can customize the format, icon and colors of gcloud segment separately for states PARTIAL + # and COMPLETE. You can also hide gcloud in state PARTIAL by setting + # POWERLEVEL9K_GCLOUD_PARTIAL_VISUAL_IDENTIFIER_EXPANSION and + # POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION to empty. + typeset -g POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_ID//\%/%%}' + typeset -g POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_NAME//\%/%%}' + + # Send a request to Google (by means of `gcloud projects describe ...`) to obtain project name + # this often. Negative value disables periodic polling. In this mode project name is retrieved + # only when the current configuration, account or project id changes. + typeset -g POWERLEVEL9K_GCLOUD_REFRESH_PROJECT_NAME_SECONDS=60 + + # Custom icon. + # typeset -g POWERLEVEL9K_GCLOUD_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ google_app_cred: google application credentials (https://cloud.google.com/docs/authentication/production) ]# + # Show google_app_cred only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show google_app_cred. + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_SHOW_ON_COMMAND='terraform|pulumi|terragrunt' + + # Google application credentials classes for the purpose of using different colors, icons and + # expansions with different credentials. + # + # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES is an array with even number of elements. The first + # element in each pair defines a pattern against which the current kubernetes context gets + # matched. More specifically, it's P9K_CONTENT prior to the application of context expansion + # (see below) that gets matched. If you unset all POWERLEVEL9K_GOOGLE_APP_CRED_*CONTENT_EXPANSION + # parameters, you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES defines the context class. Patterns are tried in order. + # The first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( + # '*:*prod*:*' PROD + # '*:*test*:*' TEST + # '*' DEFAULT) + # + # If your current Google application credentials is "service_account deathray-testing x@y.com", + # its class is TEST because it doesn't match the pattern '* *prod* *' but does match '* *test* *'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_CONTENT_EXPANSION='$P9K_GOOGLE_APP_CRED_PROJECT_ID' + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( + # '*:*prod*:*' PROD # These values are examples that are unlikely + # '*:*test*:*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_FOREGROUND=32 + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use POWERLEVEL9K_GOOGLE_APP_CRED_CONTENT_EXPANSION to specify the content displayed by + # google_app_cred segment. Parameter expansions are very flexible and fast, too. See reference: + # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. + # + # You can use the following parameters in the expansion. Each of them corresponds to one of the + # fields in the JSON file pointed to by GOOGLE_APPLICATION_CREDENTIALS. + # + # Parameter | JSON key file field + # ---------------------------------+--------------- + # P9K_GOOGLE_APP_CRED_TYPE | type + # P9K_GOOGLE_APP_CRED_PROJECT_ID | project_id + # P9K_GOOGLE_APP_CRED_CLIENT_EMAIL | client_email + # + # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced by '%%'. + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_CONTENT_EXPANSION='${P9K_GOOGLE_APP_CRED_PROJECT_ID//\%/%%}' + + ##############[ toolbox: toolbox name (https://github.com/containers/toolbox) ]############### + # Toolbox color. + typeset -g POWERLEVEL9K_TOOLBOX_FOREGROUND=178 + # Don't display the name of the toolbox if it matches fedora-toolbox-*. + typeset -g POWERLEVEL9K_TOOLBOX_CONTENT_EXPANSION='${P9K_TOOLBOX_NAME:#fedora-toolbox-*}' + # Custom icon. + # typeset -g POWERLEVEL9K_TOOLBOX_VISUAL_IDENTIFIER_EXPANSION='⭐' + # Custom prefix. + typeset -g POWERLEVEL9K_TOOLBOX_PREFIX='%246Fin ' + + ###############################[ public_ip: public IP address ]############################### + # Public IP color. + typeset -g POWERLEVEL9K_PUBLIC_IP_FOREGROUND=94 + # Custom icon. + # typeset -g POWERLEVEL9K_PUBLIC_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ########################[ vpn_ip: virtual private network indicator ]######################### + # VPN IP color. + typeset -g POWERLEVEL9K_VPN_IP_FOREGROUND=81 + # When on VPN, show just an icon without the IP address. + # Tip: To display the private IP address when on VPN, remove the next line. + typeset -g POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION= + # Regular expression for the VPN network interface. Run `ifconfig` or `ip -4 a show` while on VPN + # to see the name of the interface. + typeset -g POWERLEVEL9K_VPN_IP_INTERFACE='(gpd|wg|(.*tun)|tailscale)[0-9]*|(zt.*)' + # If set to true, show one segment per matching network interface. If set to false, show only + # one segment corresponding to the first matching network interface. + # Tip: If you set it to true, you'll probably want to unset POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION. + typeset -g POWERLEVEL9K_VPN_IP_SHOW_ALL=false + # Custom icon. + # typeset -g POWERLEVEL9K_VPN_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ ip: ip address and bandwidth usage for a specified network interface ]########### + # IP color. + typeset -g POWERLEVEL9K_IP_FOREGROUND=38 + # The following parameters are accessible within the expansion: + # + # Parameter | Meaning + # ----------------------+------------------------------------------- + # P9K_IP_IP | IP address + # P9K_IP_INTERFACE | network interface + # P9K_IP_RX_BYTES | total number of bytes received + # P9K_IP_TX_BYTES | total number of bytes sent + # P9K_IP_RX_BYTES_DELTA | number of bytes received since last prompt + # P9K_IP_TX_BYTES_DELTA | number of bytes sent since last prompt + # P9K_IP_RX_RATE | receive rate (since last prompt) + # P9K_IP_TX_RATE | send rate (since last prompt) + typeset -g POWERLEVEL9K_IP_CONTENT_EXPANSION='${P9K_IP_RX_RATE:+%70F⇣$P9K_IP_RX_RATE }${P9K_IP_TX_RATE:+%215F⇡$P9K_IP_TX_RATE }%38F$P9K_IP_IP' + # Show information for the first network interface whose name matches this regular expression. + # Run `ifconfig` or `ip -4 a show` to see the names of all network interfaces. + typeset -g POWERLEVEL9K_IP_INTERFACE='[ew].*' + # Custom icon. + # typeset -g POWERLEVEL9K_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #########################[ proxy: system-wide http/https/ftp proxy ]########################## + # Proxy color. + typeset -g POWERLEVEL9K_PROXY_FOREGROUND=68 + # Custom icon. + # typeset -g POWERLEVEL9K_PROXY_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################################[ battery: internal battery ]################################# + # Show battery in red when it's below this level and not connected to power supply. + typeset -g POWERLEVEL9K_BATTERY_LOW_THRESHOLD=20 + typeset -g POWERLEVEL9K_BATTERY_LOW_FOREGROUND=160 + # Show battery in green when it's charging or fully charged. + typeset -g POWERLEVEL9K_BATTERY_{CHARGING,CHARGED}_FOREGROUND=70 + # Show battery in yellow when it's discharging. + typeset -g POWERLEVEL9K_BATTERY_DISCONNECTED_FOREGROUND=178 + # Battery pictograms going from low to high level of charge. + typeset -g POWERLEVEL9K_BATTERY_STAGES=('%K{232}▁' '%K{232}▂' '%K{232}▃' '%K{232}▄' '%K{232}▅' '%K{232}▆' '%K{232}▇' '%K{232}█') + # Don't show the remaining time to charge/discharge. + typeset -g POWERLEVEL9K_BATTERY_VERBOSE=false + + #####################################[ wifi: wifi speed ]##################################### + # WiFi color. + typeset -g POWERLEVEL9K_WIFI_FOREGROUND=68 + # Custom icon. + # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use different colors and icons depending on signal strength ($P9K_WIFI_BARS). + # + # # Wifi colors and icons for different signal strength levels (low to high). + # typeset -g my_wifi_fg=(68 68 68 68 68) # <-- change these values + # typeset -g my_wifi_icon=('WiFi' 'WiFi' 'WiFi' 'WiFi' 'WiFi') # <-- change these values + # + # typeset -g POWERLEVEL9K_WIFI_CONTENT_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}$P9K_WIFI_LAST_TX_RATE Mbps' + # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}${my_wifi_icon[P9K_WIFI_BARS+1]}' + # + # The following parameters are accessible within the expansions: + # + # Parameter | Meaning + # ----------------------+--------------- + # P9K_WIFI_SSID | service set identifier, a.k.a. network name + # P9K_WIFI_LINK_AUTH | authentication protocol such as "wpa2-psk" or "none"; empty if unknown + # P9K_WIFI_LAST_TX_RATE | wireless transmit rate in megabits per second + # P9K_WIFI_RSSI | signal strength in dBm, from -120 to 0 + # P9K_WIFI_NOISE | noise in dBm, from -120 to 0 + # P9K_WIFI_BARS | signal strength in bars, from 0 to 4 (derived from P9K_WIFI_RSSI and P9K_WIFI_NOISE) + + ####################################[ time: current time ]#################################### + # Current time color. + typeset -g POWERLEVEL9K_TIME_FOREGROUND=66 + # Format for the current time: 09:51:02. See `man 3 strftime`. + typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}' + # If set to true, time will update when you hit enter. This way prompts for the past + # commands will contain the start times of their commands as opposed to the default + # behavior where they contain the end times of their preceding commands. + typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false + # Custom icon. + typeset -g POWERLEVEL9K_TIME_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + typeset -g POWERLEVEL9K_TIME_PREFIX='%246Fat ' + + # Example of a user-defined prompt segment. Function prompt_example will be called on every + # prompt if `example` prompt segment is added to POWERLEVEL9K_LEFT_PROMPT_ELEMENTS or + # POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS. It displays an icon and orange text greeting the user. + # + # Type `p10k help segment` for documentation and a more sophisticated example. + function prompt_example() { + p10k segment -f 208 -i '⭐' -t 'hello, %n' + } + + # User-defined prompt segments may optionally provide an instant_prompt_* function. Its job + # is to generate the prompt segment for display in instant prompt. See + # https://github.com/romkatv/powerlevel10k#instant-prompt. + # + # Powerlevel10k will call instant_prompt_* at the same time as the regular prompt_* function + # and will record all `p10k segment` calls it makes. When displaying instant prompt, Powerlevel10k + # will replay these calls without actually calling instant_prompt_*. It is imperative that + # instant_prompt_* always makes the same `p10k segment` calls regardless of environment. If this + # rule is not observed, the content of instant prompt will be incorrect. + # + # Usually, you should either not define instant_prompt_* or simply call prompt_* from it. If + # instant_prompt_* is not defined for a segment, the segment won't be shown in instant prompt. + function instant_prompt_example() { + # Since prompt_example always makes the same `p10k segment` calls, we can call it from + # instant_prompt_example. This will give us the same `example` prompt segment in the instant + # and regular prompts. + prompt_example + } + + # User-defined prompt segments can be customized the same way as built-in segments. + # typeset -g POWERLEVEL9K_EXAMPLE_FOREGROUND=208 + # typeset -g POWERLEVEL9K_EXAMPLE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt + # when accepting a command line. Supported values: + # + # - off: Don't change prompt when accepting a command line. + # - always: Trim down prompt when accepting a command line. + # - same-dir: Trim down prompt when accepting a command line unless this is the first command + # typed after changing current working directory. + typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always + + # Instant prompt mode. + # + # - off: Disable instant prompt. Choose this if you've tried instant prompt and found + # it incompatible with your zsh configuration files. + # - quiet: Enable instant prompt and don't print warnings when detecting console output + # during zsh initialization. Choose this if you've read and understood + # https://github.com/romkatv/powerlevel10k#instant-prompt. + # - verbose: Enable instant prompt and print a warning when detecting console output during + # zsh initialization. Choose this if you've never tried instant prompt, haven't + # seen the warning, or if you are unsure what this all means. + typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose + + # Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized. + # For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload + # can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you + # really need it. + typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true + + # If p10k is already loaded, reload configuration. + # This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true. + (( ! $+functions[p10k] )) || p10k reload +} + +# Tell `p10k configure` which file it should overwrite. +typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a} + +(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]} +'builtin' 'unset' 'p10k_config_opts' diff --git a/packages/p10k/p10k.zsh b/packages/p10k/p10k.zsh new file mode 100644 index 0000000..59ebee2 --- /dev/null +++ b/packages/p10k/p10k.zsh @@ -0,0 +1,1744 @@ +# Generated by Powerlevel10k configuration wizard on 2024-07-16 at 14:43 CEST. +# Based on romkatv/powerlevel10k/config/p10k-classic.zsh. +# Wizard options: powerline, classic, unicode, dark, 24h time, angled separators, +# sharp heads, flat tails, 2 lines, disconnected, full frame, sparse, fluent, +# transient_prompt, instant_prompt=verbose. +# Type `p10k configure` to generate another config. +# +# Config for Powerlevel10k with classic powerline prompt style. Type `p10k configure` to generate +# your own config based on it. +# +# Tip: Looking for a nice color? Here's a one-liner to print colormap. +# +# for i in {0..255}; do print -Pn "%K{$i} %k%F{$i}${(l:3::0:)i}%f " ${${(M)$((i%6)):#3}:+$'\n'}; done + +# Temporarily change options. +'builtin' 'local' '-a' 'p10k_config_opts' +[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases') +[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob') +[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand') +'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand' + +() { + emulate -L zsh -o extended_glob + + # Unset all configuration options. This allows you to apply configuration changes without + # restarting zsh. Edit ~/.p10k.zsh and type `source ~/.p10k.zsh`. + unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR' + + # Zsh >= 5.1 is required. + [[ $ZSH_VERSION == (5.<1->*|<6->.*) ]] || return + + # The list of segments shown on the left. Fill it with the most important segments. + typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=( + # =========================[ Line #1 ]========================= + # os_icon # os identifier + dir # current directory + vcs # git status + # =========================[ Line #2 ]========================= + newline # \n + # prompt_char # prompt symbol + ) + + # The list of segments shown on the right. Fill it with less important segments. + # Right prompt on the last prompt line (where you are typing your commands) gets + # automatically hidden when the input line reaches it. Right prompt above the + # last prompt line gets hidden if it would overlap with left prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=( + # =========================[ Line #1 ]========================= + status # exit code of the last command + command_execution_time # duration of the last command + background_jobs # presence of background jobs + direnv # direnv status (https://direnv.net/) + asdf # asdf version manager (https://github.com/asdf-vm/asdf) + virtualenv # python virtual environment (https://docs.python.org/3/library/venv.html) + anaconda # conda environment (https://conda.io/) + pyenv # python environment (https://github.com/pyenv/pyenv) + goenv # go environment (https://github.com/syndbg/goenv) + nodenv # node.js version from nodenv (https://github.com/nodenv/nodenv) + nvm # node.js version from nvm (https://github.com/nvm-sh/nvm) + nodeenv # node.js environment (https://github.com/ekalinin/nodeenv) + # node_version # node.js version + # go_version # go version (https://golang.org) + # rust_version # rustc version (https://www.rust-lang.org) + # dotnet_version # .NET version (https://dotnet.microsoft.com) + # php_version # php version (https://www.php.net/) + # laravel_version # laravel php framework version (https://laravel.com/) + # java_version # java version (https://www.java.com/) + # package # name@version from package.json (https://docs.npmjs.com/files/package.json) + rbenv # ruby version from rbenv (https://github.com/rbenv/rbenv) + rvm # ruby version from rvm (https://rvm.io) + fvm # flutter version management (https://github.com/leoafarias/fvm) + luaenv # lua version from luaenv (https://github.com/cehoffman/luaenv) + jenv # java version from jenv (https://github.com/jenv/jenv) + plenv # perl version from plenv (https://github.com/tokuhirom/plenv) + perlbrew # perl version from perlbrew (https://github.com/gugod/App-perlbrew) + phpenv # php version from phpenv (https://github.com/phpenv/phpenv) + scalaenv # scala version from scalaenv (https://github.com/scalaenv/scalaenv) + haskell_stack # haskell version from stack (https://haskellstack.org/) + kubecontext # current kubernetes context (https://kubernetes.io/) + terraform # terraform workspace (https://www.terraform.io) + # terraform_version # terraform version (https://www.terraform.io) + aws # aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) + aws_eb_env # aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) + azure # azure account name (https://docs.microsoft.com/en-us/cli/azure) + gcloud # google cloud cli account and project (https://cloud.google.com/) + google_app_cred # google application credentials (https://cloud.google.com/docs/authentication/production) + toolbox # toolbox name (https://github.com/containers/toolbox) + context # user@hostname + nordvpn # nordvpn connection status, linux only (https://nordvpn.com/) + ranger # ranger shell (https://github.com/ranger/ranger) + yazi # yazi shell (https://github.com/sxyazi/yazi) + nnn # nnn shell (https://github.com/jarun/nnn) + lf # lf shell (https://github.com/gokcehan/lf) + xplr # xplr shell (https://github.com/sayanarijit/xplr) + vim_shell # vim shell indicator (:sh) + midnight_commander # midnight commander shell (https://midnight-commander.org/) + nix_shell # nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) + chezmoi_shell # chezmoi shell (https://www.chezmoi.io/) + vi_mode # vi mode (you don't need this if you've enabled prompt_char) + # vpn_ip # virtual private network indicator + # load # CPU load + # disk_usage # disk usage + # ram # free RAM + # swap # used swap + todo # todo items (https://github.com/todotxt/todo.txt-cli) + timewarrior # timewarrior tracking status (https://timewarrior.net/) + taskwarrior # taskwarrior task count (https://taskwarrior.org/) + per_directory_history # Oh My Zsh per-directory-history local/global indicator + # cpu_arch # CPU architecture + time # current time + # =========================[ Line #2 ]========================= + newline # \n + # ip # ip address and bandwidth usage for a specified network interface + # public_ip # public IP address + # proxy # system-wide http/https/ftp proxy + # battery # internal battery + # wifi # wifi speed + # example # example user-defined segment (see prompt_example function below) + ) + + # Defines character set used by powerlevel10k. It's best to let `p10k configure` set it for you. + typeset -g POWERLEVEL9K_MODE=powerline + # When set to `moderate`, some icons will have an extra space after them. This is meant to avoid + # icon overlap when using non-monospace fonts. When set to `none`, spaces are not added. + typeset -g POWERLEVEL9K_ICON_PADDING=none + + # When set to true, icons appear before content on both sides of the prompt. When set + # to false, icons go after content. If empty or not set, icons go before content in the left + # prompt and after content in the right prompt. + # + # You can also override it for a specific segment: + # + # POWERLEVEL9K_STATUS_ICON_BEFORE_CONTENT=false + # + # Or for a specific segment in specific state: + # + # POWERLEVEL9K_DIR_NOT_WRITABLE_ICON_BEFORE_CONTENT=false + typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT= + + # Add an empty line before each prompt. + typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=true + + # Connect left prompt lines with these symbols. You'll probably want to use the same color + # as POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND below. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX='%240F╭─' + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX='%240F├─' + typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX='%240F╰─' + # Connect right prompt lines with these symbols. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX='%240F─╮' + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX='%240F─┤' + typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX='%240F─╯' + + # Filler between left and right prompt on the first prompt line. You can set it to ' ', '·' or + # '─'. The last two make it easier to see the alignment between left and right prompt and to + # separate prompt from command output. You might want to set POWERLEVEL9K_PROMPT_ADD_NEWLINE=false + # for more compact prompt if using this option. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' ' + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_BACKGROUND= + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_GAP_BACKGROUND= + if [[ $POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR != ' ' ]]; then + # The color of the filler. You'll probably want to match the color of POWERLEVEL9K_MULTILINE + # ornaments defined above. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND=240 + # Start filler from the edge of the screen if there are no left segments on the first line. + typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_FIRST_SEGMENT_END_SYMBOL='%{%}' + # End filler on the edge of the screen if there are no right segments on the first line. + typeset -g POWERLEVEL9K_EMPTY_LINE_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='%{%}' + fi + + # Default background color. + typeset -g POWERLEVEL9K_BACKGROUND=236 + + # Separator between same-color segments on the left. + typeset -g POWERLEVEL9K_LEFT_SUBSEGMENT_SEPARATOR='%244F\uE0B1' + # Separator between same-color segments on the right. + typeset -g POWERLEVEL9K_RIGHT_SUBSEGMENT_SEPARATOR='%244F\uE0B3' + # Separator between different-color segments on the left. + typeset -g POWERLEVEL9K_LEFT_SEGMENT_SEPARATOR='\uE0B0' + # Separator between different-color segments on the right. + typeset -g POWERLEVEL9K_RIGHT_SEGMENT_SEPARATOR='\uE0B2' + # To remove a separator between two segments, add "_joined" to the second segment name. + # For example: POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(os_icon context_joined) + + # The right end of left prompt. + typeset -g POWERLEVEL9K_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL='\uE0B0' + # The left end of right prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='\uE0B2' + # The left end of left prompt. + typeset -g POWERLEVEL9K_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL='' + # The right end of right prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_LAST_SEGMENT_END_SYMBOL='' + # Left prompt terminator for lines without any segments. + typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL= + + #################################[ os_icon: os identifier ]################################## + # OS identifier color. + typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND=255 + # Custom icon. + # typeset -g POWERLEVEL9K_OS_ICON_CONTENT_EXPANSION='⭐' + + ################################[ prompt_char: prompt symbol ]################################ + # Transparent background. + typeset -g POWERLEVEL9K_PROMPT_CHAR_BACKGROUND= + # Green prompt symbol if the last command succeeded. + typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=76 + # Red prompt symbol if the last command failed. + typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=196 + # Default prompt symbol. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯' + # Prompt symbol in command vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮' + # Prompt symbol in visual vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='V' + # Prompt symbol in overwrite vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION='▶' + typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true + # No line terminator if prompt_char is the last segment. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL= + # No line introducer if prompt_char is the first segment. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL= + # No surrounding whitespace. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_{LEFT,RIGHT}_WHITESPACE= + + ##################################[ dir: current directory ]################################## + # Default current directory color. + typeset -g POWERLEVEL9K_DIR_FOREGROUND=31 + # If directory is too long, shorten some of its segments to the shortest possible unique + # prefix. The shortened directory can be tab-completed to the original. + typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_unique + # Replace removed segment suffixes with this symbol. + typeset -g POWERLEVEL9K_SHORTEN_DELIMITER= + # Color of the shortened directory segments. + typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND=103 + # Color of the anchor directory segments. Anchor segments are never shortened. The first + # segment is always an anchor. + typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND=39 + # Display anchor directory segments in bold. + typeset -g POWERLEVEL9K_DIR_ANCHOR_BOLD=true + # Don't shorten directories that contain any of these files. They are anchors. + local anchor_files=( + .bzr + .citc + .git + .hg + .node-version + .python-version + .go-version + .ruby-version + .lua-version + .java-version + .perl-version + .php-version + .tool-versions + .shorten_folder_marker + .svn + .terraform + CVS + Cargo.toml + composer.json + go.mod + package.json + stack.yaml + ) + typeset -g POWERLEVEL9K_SHORTEN_FOLDER_MARKER="(${(j:|:)anchor_files})" + # If set to "first" ("last"), remove everything before the first (last) subdirectory that contains + # files matching $POWERLEVEL9K_SHORTEN_FOLDER_MARKER. For example, when the current directory is + # /foo/bar/git_repo/nested_git_repo/baz, prompt will display git_repo/nested_git_repo/baz (first) + # or nested_git_repo/baz (last). This assumes that git_repo and nested_git_repo contain markers + # and other directories don't. + # + # Optionally, "first" and "last" can be followed by ":" where is an integer. + # This moves the truncation point to the right (positive offset) or to the left (negative offset) + # relative to the marker. Plain "first" and "last" are equivalent to "first:0" and "last:0" + # respectively. + typeset -g POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER=false + # Don't shorten this many last directory segments. They are anchors. + typeset -g POWERLEVEL9K_SHORTEN_DIR_LENGTH=1 + # Shorten directory if it's longer than this even if there is space for it. The value can + # be either absolute (e.g., '80') or a percentage of terminal width (e.g, '50%'). If empty, + # directory will be shortened only when prompt doesn't fit or when other parameters demand it + # (see POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS and POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT below). + # If set to `0`, directory will always be shortened to its minimum length. + typeset -g POWERLEVEL9K_DIR_MAX_LENGTH=80 + # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least this + # many columns for typing commands. + typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS=40 + # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least + # COLUMNS * POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT * 0.01 columns for typing commands. + typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT=50 + # If set to true, embed a hyperlink into the directory. Useful for quickly + # opening a directory in the file manager simply by clicking the link. + # Can also be handy when the directory is shortened, as it allows you to see + # the full directory that was used in previous commands. + typeset -g POWERLEVEL9K_DIR_HYPERLINK=false + + # Enable special styling for non-writable and non-existent directories. See POWERLEVEL9K_LOCK_ICON + # and POWERLEVEL9K_DIR_CLASSES below. + typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=v3 + + # The default icon shown next to non-writable and non-existent directories when + # POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3. + typeset -g POWERLEVEL9K_LOCK_ICON='∅' + + # POWERLEVEL9K_DIR_CLASSES allows you to specify custom icons and colors for different + # directories. It must be an array with 3 * N elements. Each triplet consists of: + # + # 1. A pattern against which the current directory ($PWD) is matched. Matching is done with + # extended_glob option enabled. + # 2. Directory class for the purpose of styling. + # 3. An empty string. + # + # Triplets are tried in order. The first triplet whose pattern matches $PWD wins. + # + # If POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3, non-writable and non-existent directories + # acquire class suffix _NOT_WRITABLE and NON_EXISTENT respectively. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_DIR_CLASSES=( + # '~/work(|/*)' WORK '' + # '~(|/*)' HOME '' + # '*' DEFAULT '') + # + # Whenever the current directory is ~/work or a subdirectory of ~/work, it gets styled with one + # of the following classes depending on its writability and existence: WORK, WORK_NOT_WRITABLE or + # WORK_NON_EXISTENT. + # + # Simply assigning classes to directories doesn't have any visible effects. It merely gives you an + # option to define custom colors and icons for different directory classes. + # + # # Styling for WORK. + # typeset -g POWERLEVEL9K_DIR_WORK_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_ANCHOR_FOREGROUND=39 + # + # # Styling for WORK_NOT_WRITABLE. + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_ANCHOR_FOREGROUND=39 + # + # # Styling for WORK_NON_EXISTENT. + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_ANCHOR_FOREGROUND=39 + # + # If a styling parameter isn't explicitly defined for some class, it falls back to the classless + # parameter. For example, if POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND is not set, it falls + # back to POWERLEVEL9K_DIR_FOREGROUND. + # + typeset -g POWERLEVEL9K_DIR_CLASSES=() + + # Custom prefix. + # typeset -g POWERLEVEL9K_DIR_PREFIX='%246Fin ' + + #####################################[ vcs: git status ]###################################### + # Branch icon. Set this parameter to '\UE0A0 ' for the popular Powerline branch icon. + typeset -g POWERLEVEL9K_VCS_BRANCH_ICON= + + # Untracked files icon. It's really a question mark, your font isn't broken. + # Change the value of this parameter to show a different icon. + typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='?' + + # Formatter for Git status. + # + # Example output: master wip ⇣42⇡42 *42 merge ~42 +42 !42 ?42. + # + # You can edit the function to customize how Git status looks. + # + # VCS_STATUS_* parameters are set by gitstatus plugin. See reference: + # https://github.com/romkatv/gitstatus/blob/master/gitstatus.plugin.zsh. + function my_git_formatter() { + emulate -L zsh + + if [[ -n $P9K_CONTENT ]]; then + # If P9K_CONTENT is not empty, use it. It's either "loading" or from vcs_info (not from + # gitstatus plugin). VCS_STATUS_* parameters are not available in this case. + typeset -g my_git_format=$P9K_CONTENT + return + fi + + if (( $1 )); then + # Styling for up-to-date Git status. + local meta='%246F' # grey foreground + local clean='%76F' # green foreground + local modified='%178F' # yellow foreground + local untracked='%39F' # blue foreground + local conflicted='%196F' # red foreground + else + # Styling for incomplete and stale Git status. + local meta='%244F' # grey foreground + local clean='%244F' # grey foreground + local modified='%244F' # grey foreground + local untracked='%244F' # grey foreground + local conflicted='%244F' # grey foreground + fi + + local res + + if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then + local branch=${(V)VCS_STATUS_LOCAL_BRANCH} + # If local branch name is at most 32 characters long, show it in full. + # Otherwise show the first 12 … the last 12. + # Tip: To always show local branch name in full without truncation, delete the next line. + (( $#branch > 32 )) && branch[13,-13]="…" # <-- this line + res+="${clean}${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}${branch//\%/%%}" + fi + + if [[ -n $VCS_STATUS_TAG + # Show tag only if not on a branch. + # Tip: To always show tag, delete the next line. + && -z $VCS_STATUS_LOCAL_BRANCH # <-- this line + ]]; then + local tag=${(V)VCS_STATUS_TAG} + # If tag name is at most 32 characters long, show it in full. + # Otherwise show the first 12 … the last 12. + # Tip: To always show tag name in full without truncation, delete the next line. + (( $#tag > 32 )) && tag[13,-13]="…" # <-- this line + res+="${meta}#${clean}${tag//\%/%%}" + fi + + # Display the current Git commit if there is no branch and no tag. + # Tip: To always display the current Git commit, delete the next line. + [[ -z $VCS_STATUS_LOCAL_BRANCH && -z $VCS_STATUS_TAG ]] && # <-- this line + res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}" + + # Show tracking branch name if it differs from local branch. + if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then + res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}" + fi + + # Display "wip" if the latest commit's summary contains "wip" or "WIP". + if [[ $VCS_STATUS_COMMIT_SUMMARY == (|*[^[:alnum:]])(wip|WIP)(|[^[:alnum:]]*) ]]; then + res+=" ${modified}wip" + fi + + if (( VCS_STATUS_COMMITS_AHEAD || VCS_STATUS_COMMITS_BEHIND )); then + # ⇣42 if behind the remote. + (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}" + # ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42. + (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" " + (( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}" + elif [[ -n $VCS_STATUS_REMOTE_BRANCH ]]; then + # Tip: Uncomment the next line to display '=' if up to date with the remote. + # res+=" ${clean}=" + fi + + # ⇠42 if behind the push remote. + (( VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}" + (( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" " + # ⇢42 if ahead of the push remote; no leading space if also behind: ⇠42⇢42. + (( VCS_STATUS_PUSH_COMMITS_AHEAD )) && res+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}" + # *42 if have stashes. + (( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}" + # 'merge' if the repo is in an unusual state. + [[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}" + # ~42 if have merge conflicts. + (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}" + # +42 if have staged changes. + (( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}" + # !42 if have unstaged changes. + (( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}" + # ?42 if have untracked files. It's really a question mark, your font isn't broken. + # See POWERLEVEL9K_VCS_UNTRACKED_ICON above if you want to use a different icon. + # Remove the next line if you don't want to see untracked files at all. + (( VCS_STATUS_NUM_UNTRACKED )) && res+=" ${untracked}${(g::)POWERLEVEL9K_VCS_UNTRACKED_ICON}${VCS_STATUS_NUM_UNTRACKED}" + # "─" if the number of unstaged files is unknown. This can happen due to + # POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY (see below) being set to a non-negative number lower + # than the number of files in the Git index, or due to bash.showDirtyState being set to false + # in the repository config. The number of staged and untracked files may also be unknown + # in this case. + (( VCS_STATUS_HAS_UNSTAGED == -1 )) && res+=" ${modified}─" + + typeset -g my_git_format=$res + } + functions -M my_git_formatter 2>/dev/null + + # Don't count the number of unstaged, untracked and conflicted files in Git repositories with + # more than this many files in the index. Negative value means infinity. + # + # If you are working in Git repositories with tens of millions of files and seeing performance + # sagging, try setting POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY to a number lower than the output + # of `git ls-files | wc -l`. Alternatively, add `bash.showDirtyState = false` to the repository's + # config: `git config bash.showDirtyState false`. + typeset -g POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY=-1 + + # Don't show Git status in prompt for repositories whose workdir matches this pattern. + # For example, if set to '~', the Git repository at $HOME/.git will be ignored. + # Multiple patterns can be combined with '|': '~(|/foo)|/bar/baz/*'. + typeset -g POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~' + + # Disable the default Git status formatting. + typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true + # Install our own Git status formatter. + typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter(1)))+${my_git_format}}' + typeset -g POWERLEVEL9K_VCS_LOADING_CONTENT_EXPANSION='${$((my_git_formatter(0)))+${my_git_format}}' + # Enable counters for staged, unstaged, etc. + typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1 + + # Icon color. + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR=76 + typeset -g POWERLEVEL9K_VCS_LOADING_VISUAL_IDENTIFIER_COLOR=244 + # Custom icon. + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + typeset -g POWERLEVEL9K_VCS_PREFIX='%246Fon ' + + # Show status of repositories of these types. You can add svn and/or hg if you are + # using them. If you do, your prompt may become slow even when your current directory + # isn't in an svn or hg reposotiry. + typeset -g POWERLEVEL9K_VCS_BACKENDS=(git) + + # These settings are used for repositories other than Git or when gitstatusd fails and + # Powerlevel10k has to fall back to using vcs_info. + typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=76 + typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=76 + typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=178 + + ##########################[ status: exit code of the last command ]########################### + # Enable OK_PIPE, ERROR_PIPE and ERROR_SIGNAL status states to allow us to enable, disable and + # style them independently from the regular OK and ERROR state. + typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true + + # Status on success. No content, just an icon. No need to show it if prompt_char is enabled as + # it will signify success by turning green. + typeset -g POWERLEVEL9K_STATUS_OK=true + typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND=70 + typeset -g POWERLEVEL9K_STATUS_OK_VISUAL_IDENTIFIER_EXPANSION='✔' + + # Status when some part of a pipe command fails but the overall exit status is zero. It may look + # like this: 1|0. + typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND=70 + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION='✔' + + # Status when it's just an error code (e.g., '1'). No need to show it if prompt_char is enabled as + # it will signify error by turning red. + typeset -g POWERLEVEL9K_STATUS_ERROR=true + typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND=160 + typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION='✘' + + # Status when the last command was terminated by a signal. + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND=160 + # Use terse signal names: "INT" instead of "SIGINT(2)". + typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=false + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION='✘' + + # Status when some part of a pipe command fails and the overall exit status is also non-zero. + # It may look like this: 1|0. + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND=160 + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION='✘' + + ###################[ command_execution_time: duration of the last command ]################### + # Show duration of the last command if takes at least this many seconds. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3 + # Show this many fractional digits. Zero means round to seconds. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0 + # Execution time color. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=248 + # Duration format: 1d 2h 3m 4s. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s' + # Custom icon. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PREFIX='%246Ftook ' + + #######################[ background_jobs: presence of background jobs ]####################### + # Don't show the number of background jobs. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE=false + # Background jobs color. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_FOREGROUND=37 + # Custom icon. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VISUAL_IDENTIFIER_EXPANSION='≡' + + #######################[ direnv: direnv status (https://direnv.net/) ]######################## + # Direnv color. + typeset -g POWERLEVEL9K_DIRENV_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_DIRENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ asdf: asdf version manager (https://github.com/asdf-vm/asdf) ]############### + # Default asdf color. Only used to display tools for which there is no color override (see below). + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_FOREGROUND. + typeset -g POWERLEVEL9K_ASDF_FOREGROUND=66 + + # There are four parameters that can be used to hide asdf tools. Each parameter describes + # conditions under which a tool gets hidden. Parameters can hide tools but not unhide them. If at + # least one parameter decides to hide a tool, that tool gets hidden. If no parameter decides to + # hide a tool, it gets shown. + # + # Special note on the difference between POWERLEVEL9K_ASDF_SOURCES and + # POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW. Consider the effect of the following commands: + # + # asdf local python 3.8.1 + # asdf global python 3.8.1 + # + # After running both commands the current python version is 3.8.1 and its source is "local" as + # it takes precedence over "global". If POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW is set to false, + # it'll hide python version in this case because 3.8.1 is the same as the global version. + # POWERLEVEL9K_ASDF_SOURCES will hide python version only if the value of this parameter doesn't + # contain "local". + + # Hide tool versions that don't come from one of these sources. + # + # Available sources: + # + # - shell `asdf current` says "set by ASDF_${TOOL}_VERSION environment variable" + # - local `asdf current` says "set by /some/not/home/directory/file" + # - global `asdf current` says "set by /home/username/file" + # + # Note: If this parameter is set to (shell local global), it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SOURCES. + typeset -g POWERLEVEL9K_ASDF_SOURCES=(shell local global) + + # If set to false, hide tool versions that are the same as global. + # + # Note: The name of this parameter doesn't reflect its meaning at all. + # Note: If this parameter is set to true, it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_PROMPT_ALWAYS_SHOW. + typeset -g POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW=false + + # If set to false, hide tool versions that are equal to "system". + # + # Note: If this parameter is set to true, it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_SYSTEM. + typeset -g POWERLEVEL9K_ASDF_SHOW_SYSTEM=true + + # If set to non-empty value, hide tools unless there is a file matching the specified file pattern + # in the current directory, or its parent directory, or its grandparent directory, and so on. + # + # Note: If this parameter is set to empty value, it won't hide tools. + # Note: SHOW_ON_UPGLOB isn't specific to asdf. It works with all prompt segments. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_ON_UPGLOB. + # + # Example: Hide nodejs version when there is no package.json and no *.js files in the current + # directory, in `..`, in `../..` and so on. + # + # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.js|package.json' + typeset -g POWERLEVEL9K_ASDF_SHOW_ON_UPGLOB= + + # Ruby version from asdf. + typeset -g POWERLEVEL9K_ASDF_RUBY_FOREGROUND=168 + # typeset -g POWERLEVEL9K_ASDF_RUBY_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_RUBY_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Python version from asdf. + typeset -g POWERLEVEL9K_ASDF_PYTHON_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_PYTHON_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PYTHON_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Go version from asdf. + typeset -g POWERLEVEL9K_ASDF_GOLANG_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_GOLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_GOLANG_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Node.js version from asdf. + typeset -g POWERLEVEL9K_ASDF_NODEJS_FOREGROUND=70 + # typeset -g POWERLEVEL9K_ASDF_NODEJS_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Rust version from asdf. + typeset -g POWERLEVEL9K_ASDF_RUST_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_RUST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_RUST_SHOW_ON_UPGLOB='*.foo|*.bar' + + # .NET Core version from asdf. + typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_FOREGROUND=134 + # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Flutter version from asdf. + typeset -g POWERLEVEL9K_ASDF_FLUTTER_FOREGROUND=38 + # typeset -g POWERLEVEL9K_ASDF_FLUTTER_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_FLUTTER_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Lua version from asdf. + typeset -g POWERLEVEL9K_ASDF_LUA_FOREGROUND=32 + # typeset -g POWERLEVEL9K_ASDF_LUA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_LUA_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Java version from asdf. + typeset -g POWERLEVEL9K_ASDF_JAVA_FOREGROUND=32 + # typeset -g POWERLEVEL9K_ASDF_JAVA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_JAVA_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Perl version from asdf. + typeset -g POWERLEVEL9K_ASDF_PERL_FOREGROUND=67 + # typeset -g POWERLEVEL9K_ASDF_PERL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PERL_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Erlang version from asdf. + typeset -g POWERLEVEL9K_ASDF_ERLANG_FOREGROUND=125 + # typeset -g POWERLEVEL9K_ASDF_ERLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_ERLANG_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Elixir version from asdf. + typeset -g POWERLEVEL9K_ASDF_ELIXIR_FOREGROUND=129 + # typeset -g POWERLEVEL9K_ASDF_ELIXIR_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_ELIXIR_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Postgres version from asdf. + typeset -g POWERLEVEL9K_ASDF_POSTGRES_FOREGROUND=31 + # typeset -g POWERLEVEL9K_ASDF_POSTGRES_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_POSTGRES_SHOW_ON_UPGLOB='*.foo|*.bar' + + # PHP version from asdf. + typeset -g POWERLEVEL9K_ASDF_PHP_FOREGROUND=99 + # typeset -g POWERLEVEL9K_ASDF_PHP_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PHP_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Haskell version from asdf. + typeset -g POWERLEVEL9K_ASDF_HASKELL_FOREGROUND=172 + # typeset -g POWERLEVEL9K_ASDF_HASKELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_HASKELL_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Julia version from asdf. + typeset -g POWERLEVEL9K_ASDF_JULIA_FOREGROUND=70 + # typeset -g POWERLEVEL9K_ASDF_JULIA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_JULIA_SHOW_ON_UPGLOB='*.foo|*.bar' + + ##########[ nordvpn: nordvpn connection status, linux only (https://nordvpn.com/) ]########### + # NordVPN connection indicator color. + typeset -g POWERLEVEL9K_NORDVPN_FOREGROUND=39 + # Hide NordVPN connection indicator when not connected. + typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_CONTENT_EXPANSION= + typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_VISUAL_IDENTIFIER_EXPANSION= + # Custom icon. + typeset -g POWERLEVEL9K_NORDVPN_VISUAL_IDENTIFIER_EXPANSION='nord' + + #################[ ranger: ranger shell (https://github.com/ranger/ranger) ]################## + # Ranger shell color. + typeset -g POWERLEVEL9K_RANGER_FOREGROUND=178 + # Custom icon. + typeset -g POWERLEVEL9K_RANGER_VISUAL_IDENTIFIER_EXPANSION='▲' + + ####################[ yazi: yazi shell (https://github.com/sxyazi/yazi) ]##################### + # Yazi shell color. + typeset -g POWERLEVEL9K_YAZI_FOREGROUND=178 + # Custom icon. + typeset -g POWERLEVEL9K_YAZI_VISUAL_IDENTIFIER_EXPANSION='▲' + + ######################[ nnn: nnn shell (https://github.com/jarun/nnn) ]####################### + # Nnn shell color. + typeset -g POWERLEVEL9K_NNN_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_NNN_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################[ lf: lf shell (https://github.com/gokcehan/lf) ]####################### + # lf shell color. + typeset -g POWERLEVEL9K_LF_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_LF_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################[ xplr: xplr shell (https://github.com/sayanarijit/xplr) ]################## + # xplr shell color. + typeset -g POWERLEVEL9K_XPLR_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_XPLR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########################[ vim_shell: vim shell indicator (:sh) ]########################### + # Vim shell indicator color. + typeset -g POWERLEVEL9K_VIM_SHELL_FOREGROUND=34 + # Custom icon. + # typeset -g POWERLEVEL9K_VIM_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######[ midnight_commander: midnight commander shell (https://midnight-commander.org/) ]###### + # Midnight Commander shell color. + typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ nix_shell: nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) ]## + # Nix shell color. + typeset -g POWERLEVEL9K_NIX_SHELL_FOREGROUND=74 + + # Display the icon of nix_shell if PATH contains a subdirectory of /nix/store. + # typeset -g POWERLEVEL9K_NIX_SHELL_INFER_FROM_PATH=false + + # Tip: If you want to see just the icon without "pure" and "impure", uncomment the next line. + # typeset -g POWERLEVEL9K_NIX_SHELL_CONTENT_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_NIX_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################[ chezmoi_shell: chezmoi shell (https://www.chezmoi.io/) ]################## + # chezmoi shell color. + typeset -g POWERLEVEL9K_CHEZMOI_SHELL_FOREGROUND=33 + # Custom icon. + # typeset -g POWERLEVEL9K_CHEZMOI_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################################[ disk_usage: disk usage ]################################## + # Colors for different levels of disk usage. + typeset -g POWERLEVEL9K_DISK_USAGE_NORMAL_FOREGROUND=35 + typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_FOREGROUND=220 + typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_FOREGROUND=160 + # Thresholds for different levels of disk usage (percentage points). + typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=90 + typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=95 + # If set to true, hide disk usage when below $POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL percent. + typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=false + # Custom icon. + # typeset -g POWERLEVEL9K_DISK_USAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ vi_mode: vi mode (you don't need this if you've enabled prompt_char) ]########### + # Text and color for normal (a.k.a. command) vi mode. + typeset -g POWERLEVEL9K_VI_COMMAND_MODE_STRING=NORMAL + typeset -g POWERLEVEL9K_VI_MODE_NORMAL_FOREGROUND=106 + # Text and color for visual vi mode. + typeset -g POWERLEVEL9K_VI_VISUAL_MODE_STRING=VISUAL + typeset -g POWERLEVEL9K_VI_MODE_VISUAL_FOREGROUND=68 + # Text and color for overtype (a.k.a. overwrite and replace) vi mode. + typeset -g POWERLEVEL9K_VI_OVERWRITE_MODE_STRING=OVERTYPE + typeset -g POWERLEVEL9K_VI_MODE_OVERWRITE_FOREGROUND=172 + # Text and color for insert vi mode. + typeset -g POWERLEVEL9K_VI_INSERT_MODE_STRING= + typeset -g POWERLEVEL9K_VI_MODE_INSERT_FOREGROUND=66 + # Custom icon. + # typeset -g POWERLEVEL9K_VI_MODE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################################[ ram: free RAM ]####################################### + # RAM color. + typeset -g POWERLEVEL9K_RAM_FOREGROUND=66 + # Custom icon. + # typeset -g POWERLEVEL9K_RAM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################################[ swap: used swap ]###################################### + # Swap color. + typeset -g POWERLEVEL9K_SWAP_FOREGROUND=96 + # Custom icon. + # typeset -g POWERLEVEL9K_SWAP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################################[ load: CPU load ]###################################### + # Show average CPU load over this many last minutes. Valid values are 1, 5 and 15. + typeset -g POWERLEVEL9K_LOAD_WHICH=5 + # Load color when load is under 50%. + typeset -g POWERLEVEL9K_LOAD_NORMAL_FOREGROUND=66 + # Load color when load is between 50% and 70%. + typeset -g POWERLEVEL9K_LOAD_WARNING_FOREGROUND=178 + # Load color when load is over 70%. + typeset -g POWERLEVEL9K_LOAD_CRITICAL_FOREGROUND=166 + # Custom icon. + # typeset -g POWERLEVEL9K_LOAD_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ todo: todo items (https://github.com/todotxt/todo.txt-cli) ]################ + # Todo color. + typeset -g POWERLEVEL9K_TODO_FOREGROUND=110 + # Hide todo when the total number of tasks is zero. + typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_TOTAL=true + # Hide todo when the number of tasks after filtering is zero. + typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_FILTERED=false + + # Todo format. The following parameters are available within the expansion. + # + # - P9K_TODO_TOTAL_TASK_COUNT The total number of tasks. + # - P9K_TODO_FILTERED_TASK_COUNT The number of tasks after filtering. + # + # These variables correspond to the last line of the output of `todo.sh -p ls`: + # + # TODO: 24 of 42 tasks shown + # + # Here 24 is P9K_TODO_FILTERED_TASK_COUNT and 42 is P9K_TODO_TOTAL_TASK_COUNT. + # + # typeset -g POWERLEVEL9K_TODO_CONTENT_EXPANSION='$P9K_TODO_FILTERED_TASK_COUNT' + + # Custom icon. + # typeset -g POWERLEVEL9K_TODO_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ timewarrior: timewarrior tracking status (https://timewarrior.net/) ]############ + # Timewarrior color. + typeset -g POWERLEVEL9K_TIMEWARRIOR_FOREGROUND=110 + # If the tracked task is longer than 24 characters, truncate and append "…". + # Tip: To always display tasks without truncation, delete the following parameter. + # Tip: To hide task names and display just the icon when time tracking is enabled, set the + # value of the following parameter to "". + typeset -g POWERLEVEL9K_TIMEWARRIOR_CONTENT_EXPANSION='${P9K_CONTENT:0:24}${${P9K_CONTENT:24}:+…}' + + # Custom icon. + # typeset -g POWERLEVEL9K_TIMEWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############[ taskwarrior: taskwarrior task count (https://taskwarrior.org/) ]############## + # Taskwarrior color. + typeset -g POWERLEVEL9K_TASKWARRIOR_FOREGROUND=74 + + # Taskwarrior segment format. The following parameters are available within the expansion. + # + # - P9K_TASKWARRIOR_PENDING_COUNT The number of pending tasks: `task +PENDING count`. + # - P9K_TASKWARRIOR_OVERDUE_COUNT The number of overdue tasks: `task +OVERDUE count`. + # + # Zero values are represented as empty parameters. + # + # The default format: + # + # '${P9K_TASKWARRIOR_OVERDUE_COUNT:+"!$P9K_TASKWARRIOR_OVERDUE_COUNT/"}$P9K_TASKWARRIOR_PENDING_COUNT' + # + # typeset -g POWERLEVEL9K_TASKWARRIOR_CONTENT_EXPANSION='$P9K_TASKWARRIOR_PENDING_COUNT' + + # Custom icon. + # typeset -g POWERLEVEL9K_TASKWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######[ per_directory_history: Oh My Zsh per-directory-history local/global indicator ]####### + # Color when using local/global history. + typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_FOREGROUND=135 + typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_FOREGROUND=130 + + # Tip: Uncomment the next two lines to hide "local"/"global" text and leave just the icon. + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_CONTENT_EXPANSION='' + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_CONTENT_EXPANSION='' + + # Custom icon. + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_LOCAL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_PER_DIRECTORY_HISTORY_GLOBAL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################################[ cpu_arch: CPU architecture ]################################ + # CPU architecture color. + typeset -g POWERLEVEL9K_CPU_ARCH_FOREGROUND=172 + + # Hide the segment when on a specific CPU architecture. + # typeset -g POWERLEVEL9K_CPU_ARCH_X86_64_CONTENT_EXPANSION= + # typeset -g POWERLEVEL9K_CPU_ARCH_X86_64_VISUAL_IDENTIFIER_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_CPU_ARCH_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################################[ context: user@hostname ]################################## + # Context color when running with privileges. + typeset -g POWERLEVEL9K_CONTEXT_ROOT_FOREGROUND=178 + # Context color in SSH without privileges. + typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_FOREGROUND=180 + # Default context color (no privileges, no SSH). + typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND=180 + + # Context format when running with privileges: bold user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%B%n@%m' + # Context format when in SSH without privileges: user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_TEMPLATE='%n@%m' + # Default context format (no privileges, no SSH): user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%n@%m' + + # Don't show context unless running with privileges or in SSH. + # Tip: Remove the next line to always show context. + typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_{CONTENT,VISUAL_IDENTIFIER}_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_CONTEXT_VISUAL_IDENTIFIER_EXPANSION='⭐' + # Custom prefix. + typeset -g POWERLEVEL9K_CONTEXT_PREFIX='%246Fwith ' + + ###[ virtualenv: python virtual environment (https://docs.python.org/3/library/venv.html) ]### + # Python virtual environment color. + typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=37 + # Don't show Python version next to the virtual environment name. + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false + # If set to "false", won't show virtualenv if pyenv is already shown. + # If set to "if-different", won't show virtualenv if it's the same as pyenv. + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_WITH_PYENV=false + # Separate environment name from Python version only with a space. + typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER= + # Custom icon. + # typeset -g POWERLEVEL9K_VIRTUALENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################[ anaconda: conda environment (https://conda.io/) ]###################### + # Anaconda environment color. + typeset -g POWERLEVEL9K_ANACONDA_FOREGROUND=37 + + # Anaconda segment format. The following parameters are available within the expansion. + # + # - CONDA_PREFIX Absolute path to the active Anaconda/Miniconda environment. + # - CONDA_DEFAULT_ENV Name of the active Anaconda/Miniconda environment. + # - CONDA_PROMPT_MODIFIER Configurable prompt modifier (see below). + # - P9K_ANACONDA_PYTHON_VERSION Current python version (python --version). + # + # CONDA_PROMPT_MODIFIER can be configured with the following command: + # + # conda config --set env_prompt '({default_env}) ' + # + # The last argument is a Python format string that can use the following variables: + # + # - prefix The same as CONDA_PREFIX. + # - default_env The same as CONDA_DEFAULT_ENV. + # - name The last segment of CONDA_PREFIX. + # - stacked_env Comma-separated list of names in the environment stack. The first element is + # always the same as default_env. + # + # Note: '({default_env}) ' is the default value of env_prompt. + # + # The default value of POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION expands to $CONDA_PROMPT_MODIFIER + # without the surrounding parentheses, or to the last path component of CONDA_PREFIX if the former + # is empty. + typeset -g POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION='${${${${CONDA_PROMPT_MODIFIER#\(}% }%\)}:-${CONDA_PREFIX:t}}' + + # Custom icon. + # typeset -g POWERLEVEL9K_ANACONDA_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ pyenv: python environment (https://github.com/pyenv/pyenv) ]################ + # Pyenv color. + typeset -g POWERLEVEL9K_PYENV_FOREGROUND=37 + # Hide python version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PYENV_SOURCES=(shell local global) + # If set to false, hide python version if it's the same as global: + # $(pyenv version-name) == $(pyenv global). + typeset -g POWERLEVEL9K_PYENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide python version if it's equal to "system". + typeset -g POWERLEVEL9K_PYENV_SHOW_SYSTEM=true + + # Pyenv segment format. The following parameters are available within the expansion. + # + # - P9K_CONTENT Current pyenv environment (pyenv version-name). + # - P9K_PYENV_PYTHON_VERSION Current python version (python --version). + # + # The default format has the following logic: + # + # 1. Display just "$P9K_CONTENT" if it's equal to "$P9K_PYENV_PYTHON_VERSION" or + # starts with "$P9K_PYENV_PYTHON_VERSION/". + # 2. Otherwise display "$P9K_CONTENT $P9K_PYENV_PYTHON_VERSION". + typeset -g POWERLEVEL9K_PYENV_CONTENT_EXPANSION='${P9K_CONTENT}${${P9K_CONTENT:#$P9K_PYENV_PYTHON_VERSION(|/*)}:+ $P9K_PYENV_PYTHON_VERSION}' + + # Custom icon. + # typeset -g POWERLEVEL9K_PYENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ goenv: go environment (https://github.com/syndbg/goenv) ]################ + # Goenv color. + typeset -g POWERLEVEL9K_GOENV_FOREGROUND=37 + # Hide go version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_GOENV_SOURCES=(shell local global) + # If set to false, hide go version if it's the same as global: + # $(goenv version-name) == $(goenv global). + typeset -g POWERLEVEL9K_GOENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide go version if it's equal to "system". + typeset -g POWERLEVEL9K_GOENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_GOENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ nodenv: node.js version from nodenv (https://github.com/nodenv/nodenv) ]########## + # Nodenv color. + typeset -g POWERLEVEL9K_NODENV_FOREGROUND=70 + # Hide node version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_NODENV_SOURCES=(shell local global) + # If set to false, hide node version if it's the same as global: + # $(nodenv version-name) == $(nodenv global). + typeset -g POWERLEVEL9K_NODENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide node version if it's equal to "system". + typeset -g POWERLEVEL9K_NODENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_NODENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############[ nvm: node.js version from nvm (https://github.com/nvm-sh/nvm) ]############### + # Nvm color. + typeset -g POWERLEVEL9K_NVM_FOREGROUND=70 + # If set to false, hide node version if it's the same as default: + # $(nvm version current) == $(nvm version default). + typeset -g POWERLEVEL9K_NVM_PROMPT_ALWAYS_SHOW=false + # If set to false, hide node version if it's equal to "system". + typeset -g POWERLEVEL9K_NVM_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_NVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ############[ nodeenv: node.js environment (https://github.com/ekalinin/nodeenv) ]############ + # Nodeenv color. + typeset -g POWERLEVEL9K_NODEENV_FOREGROUND=70 + # Don't show Node version next to the environment name. + typeset -g POWERLEVEL9K_NODEENV_SHOW_NODE_VERSION=false + # Separate environment name from Node version only with a space. + typeset -g POWERLEVEL9K_NODEENV_{LEFT,RIGHT}_DELIMITER= + # Custom icon. + # typeset -g POWERLEVEL9K_NODEENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############################[ node_version: node.js version ]############################### + # Node version color. + typeset -g POWERLEVEL9K_NODE_VERSION_FOREGROUND=70 + # Show node version only when in a directory tree containing package.json. + typeset -g POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_NODE_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ go_version: go version (https://golang.org) ]######################## + # Go version color. + typeset -g POWERLEVEL9K_GO_VERSION_FOREGROUND=37 + # Show go version only when in a go project subdirectory. + typeset -g POWERLEVEL9K_GO_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_GO_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #################[ rust_version: rustc version (https://www.rust-lang.org) ]################## + # Rust version color. + typeset -g POWERLEVEL9K_RUST_VERSION_FOREGROUND=37 + # Show rust version only when in a rust project subdirectory. + typeset -g POWERLEVEL9K_RUST_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_RUST_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ dotnet_version: .NET version (https://dotnet.microsoft.com) ]################ + # .NET version color. + typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND=134 + # Show .NET version only when in a .NET project subdirectory. + typeset -g POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_DOTNET_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################[ php_version: php version (https://www.php.net/) ]###################### + # PHP version color. + typeset -g POWERLEVEL9K_PHP_VERSION_FOREGROUND=99 + # Show PHP version only when in a PHP project subdirectory. + typeset -g POWERLEVEL9K_PHP_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_PHP_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ laravel_version: laravel php framework version (https://laravel.com/) ]########### + # Laravel version color. + typeset -g POWERLEVEL9K_LARAVEL_VERSION_FOREGROUND=161 + # Custom icon. + # typeset -g POWERLEVEL9K_LARAVEL_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ####################[ java_version: java version (https://www.java.com/) ]#################### + # Java version color. + typeset -g POWERLEVEL9K_JAVA_VERSION_FOREGROUND=32 + # Show java version only when in a java project subdirectory. + typeset -g POWERLEVEL9K_JAVA_VERSION_PROJECT_ONLY=true + # Show brief version. + typeset -g POWERLEVEL9K_JAVA_VERSION_FULL=false + # Custom icon. + # typeset -g POWERLEVEL9K_JAVA_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###[ package: name@version from package.json (https://docs.npmjs.com/files/package.json) ]#### + # Package color. + typeset -g POWERLEVEL9K_PACKAGE_FOREGROUND=117 + # Package format. The following parameters are available within the expansion. + # + # - P9K_PACKAGE_NAME The value of `name` field in package.json. + # - P9K_PACKAGE_VERSION The value of `version` field in package.json. + # + # typeset -g POWERLEVEL9K_PACKAGE_CONTENT_EXPANSION='${P9K_PACKAGE_NAME//\%/%%}@${P9K_PACKAGE_VERSION//\%/%%}' + # Custom icon. + # typeset -g POWERLEVEL9K_PACKAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ rbenv: ruby version from rbenv (https://github.com/rbenv/rbenv) ]############## + # Rbenv color. + typeset -g POWERLEVEL9K_RBENV_FOREGROUND=168 + # Hide ruby version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_RBENV_SOURCES=(shell local global) + # If set to false, hide ruby version if it's the same as global: + # $(rbenv version-name) == $(rbenv global). + typeset -g POWERLEVEL9K_RBENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide ruby version if it's equal to "system". + typeset -g POWERLEVEL9K_RBENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_RBENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ rvm: ruby version from rvm (https://rvm.io) ]######################## + # Rvm color. + typeset -g POWERLEVEL9K_RVM_FOREGROUND=168 + # Don't show @gemset at the end. + typeset -g POWERLEVEL9K_RVM_SHOW_GEMSET=false + # Don't show ruby- at the front. + typeset -g POWERLEVEL9K_RVM_SHOW_PREFIX=false + # Custom icon. + # typeset -g POWERLEVEL9K_RVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ fvm: flutter version management (https://github.com/leoafarias/fvm) ]############ + # Fvm color. + typeset -g POWERLEVEL9K_FVM_FOREGROUND=38 + # Custom icon. + # typeset -g POWERLEVEL9K_FVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ luaenv: lua version from luaenv (https://github.com/cehoffman/luaenv) ]########### + # Lua color. + typeset -g POWERLEVEL9K_LUAENV_FOREGROUND=32 + # Hide lua version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_LUAENV_SOURCES=(shell local global) + # If set to false, hide lua version if it's the same as global: + # $(luaenv version-name) == $(luaenv global). + typeset -g POWERLEVEL9K_LUAENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide lua version if it's equal to "system". + typeset -g POWERLEVEL9K_LUAENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_LUAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ jenv: java version from jenv (https://github.com/jenv/jenv) ]################ + # Java color. + typeset -g POWERLEVEL9K_JENV_FOREGROUND=32 + # Hide java version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_JENV_SOURCES=(shell local global) + # If set to false, hide java version if it's the same as global: + # $(jenv version-name) == $(jenv global). + typeset -g POWERLEVEL9K_JENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide java version if it's equal to "system". + typeset -g POWERLEVEL9K_JENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_JENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ plenv: perl version from plenv (https://github.com/tokuhirom/plenv) ]############ + # Perl color. + typeset -g POWERLEVEL9K_PLENV_FOREGROUND=67 + # Hide perl version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PLENV_SOURCES=(shell local global) + # If set to false, hide perl version if it's the same as global: + # $(plenv version-name) == $(plenv global). + typeset -g POWERLEVEL9K_PLENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide perl version if it's equal to "system". + typeset -g POWERLEVEL9K_PLENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_PLENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ perlbrew: perl version from perlbrew (https://github.com/gugod/App-perlbrew) ]############ + # Perlbrew color. + typeset -g POWERLEVEL9K_PERLBREW_FOREGROUND=67 + # Show perlbrew version only when in a perl project subdirectory. + typeset -g POWERLEVEL9K_PERLBREW_PROJECT_ONLY=true + # Don't show "perl-" at the front. + typeset -g POWERLEVEL9K_PERLBREW_SHOW_PREFIX=false + # Custom icon. + # typeset -g POWERLEVEL9K_PERLBREW_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ############[ phpenv: php version from phpenv (https://github.com/phpenv/phpenv) ]############ + # PHP color. + typeset -g POWERLEVEL9K_PHPENV_FOREGROUND=99 + # Hide php version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PHPENV_SOURCES=(shell local global) + # If set to false, hide php version if it's the same as global: + # $(phpenv version-name) == $(phpenv global). + typeset -g POWERLEVEL9K_PHPENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide php version if it's equal to "system". + typeset -g POWERLEVEL9K_PHPENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_PHPENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######[ scalaenv: scala version from scalaenv (https://github.com/scalaenv/scalaenv) ]####### + # Scala color. + typeset -g POWERLEVEL9K_SCALAENV_FOREGROUND=160 + # Hide scala version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_SCALAENV_SOURCES=(shell local global) + # If set to false, hide scala version if it's the same as global: + # $(scalaenv version-name) == $(scalaenv global). + typeset -g POWERLEVEL9K_SCALAENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide scala version if it's equal to "system". + typeset -g POWERLEVEL9K_SCALAENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_SCALAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ haskell_stack: haskell version from stack (https://haskellstack.org/) ]########### + # Haskell color. + typeset -g POWERLEVEL9K_HASKELL_STACK_FOREGROUND=172 + # Hide haskell version if it doesn't come from one of these sources. + # + # shell: version is set by STACK_YAML + # local: version is set by stack.yaml up the directory tree + # global: version is set by the implicit global project (~/.stack/global-project/stack.yaml) + typeset -g POWERLEVEL9K_HASKELL_STACK_SOURCES=(shell local) + # If set to false, hide haskell version if it's the same as in the implicit global project. + typeset -g POWERLEVEL9K_HASKELL_STACK_ALWAYS_SHOW=true + # Custom icon. + # typeset -g POWERLEVEL9K_HASKELL_STACK_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ terraform: terraform workspace (https://www.terraform.io) ]################# + # Don't show terraform workspace if it's literally "default". + typeset -g POWERLEVEL9K_TERRAFORM_SHOW_DEFAULT=false + # POWERLEVEL9K_TERRAFORM_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current terraform workspace gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_TERRAFORM_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_TERRAFORM_CLASSES defines the workspace class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' OTHER) + # + # If your current terraform workspace is "project_test", its class is TEST because "project_test" + # doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' OTHER) + typeset -g POWERLEVEL9K_TERRAFORM_OTHER_FOREGROUND=38 + # typeset -g POWERLEVEL9K_TERRAFORM_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ terraform_version: terraform version (https://www.terraform.io) ]############## + # Terraform version color. + typeset -g POWERLEVEL9K_TERRAFORM_VERSION_FOREGROUND=38 + # Custom icon. + # typeset -g POWERLEVEL9K_TERRAFORM_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ kubecontext: current kubernetes context (https://kubernetes.io/) ]############# + # Show kubecontext only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show kubecontext. + typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND='kubectl|helm|kubens|kubectx|oc|istioctl|kogito|k9s|helmfile|flux|fluxctl|stern|kubeseal|skaffold|kubent|kubecolor|cmctl|sparkctl' + + # Kubernetes context classes for the purpose of using different colors, icons and expansions with + # different contexts. + # + # POWERLEVEL9K_KUBECONTEXT_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current kubernetes context gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_KUBECONTEXT_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_KUBECONTEXT_CLASSES defines the context class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' DEFAULT) + # + # If your current kubernetes context is "deathray-testing/default", its class is TEST + # because "deathray-testing/default" doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_FOREGROUND=134 + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='○' + + # Use POWERLEVEL9K_KUBECONTEXT_CONTENT_EXPANSION to specify the content displayed by kubecontext + # segment. Parameter expansions are very flexible and fast, too. See reference: + # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. + # + # Within the expansion the following parameters are always available: + # + # - P9K_CONTENT The content that would've been displayed if there was no content + # expansion defined. + # - P9K_KUBECONTEXT_NAME The current context's name. Corresponds to column NAME in the + # output of `kubectl config get-contexts`. + # - P9K_KUBECONTEXT_CLUSTER The current context's cluster. Corresponds to column CLUSTER in the + # output of `kubectl config get-contexts`. + # - P9K_KUBECONTEXT_NAMESPACE The current context's namespace. Corresponds to column NAMESPACE + # in the output of `kubectl config get-contexts`. If there is no + # namespace, the parameter is set to "default". + # - P9K_KUBECONTEXT_USER The current context's user. Corresponds to column AUTHINFO in the + # output of `kubectl config get-contexts`. + # + # If the context points to Google Kubernetes Engine (GKE) or Elastic Kubernetes Service (EKS), + # the following extra parameters are available: + # + # - P9K_KUBECONTEXT_CLOUD_NAME Either "gke" or "eks". + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT Account/project ID. + # - P9K_KUBECONTEXT_CLOUD_ZONE Availability zone. + # - P9K_KUBECONTEXT_CLOUD_CLUSTER Cluster. + # + # P9K_KUBECONTEXT_CLOUD_* parameters are derived from P9K_KUBECONTEXT_CLUSTER. For example, + # if P9K_KUBECONTEXT_CLUSTER is "gke_my-account_us-east1-a_my-cluster-01": + # + # - P9K_KUBECONTEXT_CLOUD_NAME=gke + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=my-account + # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east1-a + # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 + # + # If P9K_KUBECONTEXT_CLUSTER is "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster-01": + # + # - P9K_KUBECONTEXT_CLOUD_NAME=eks + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=123456789012 + # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east-1 + # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION= + # Show P9K_KUBECONTEXT_CLOUD_CLUSTER if it's not empty and fall back to P9K_KUBECONTEXT_NAME. + POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${P9K_KUBECONTEXT_CLOUD_CLUSTER:-${P9K_KUBECONTEXT_NAME}}' + # Append the current context's namespace if it's not "default". + POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${${:-/$P9K_KUBECONTEXT_NAMESPACE}:#/default}' + + # Custom prefix. + typeset -g POWERLEVEL9K_KUBECONTEXT_PREFIX='%246Fat ' + + #[ aws: aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) ]# + # Show aws only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show aws. + typeset -g POWERLEVEL9K_AWS_SHOW_ON_COMMAND='aws|awless|cdk|terraform|pulumi|terragrunt' + + # POWERLEVEL9K_AWS_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current AWS profile gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_AWS_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_AWS_CLASSES defines the profile class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_AWS_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' DEFAULT) + # + # If your current AWS profile is "company_test", its class is TEST + # because "company_test" doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_AWS_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_AWS_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_AWS_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_AWS_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_AWS_DEFAULT_FOREGROUND=208 + # typeset -g POWERLEVEL9K_AWS_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # AWS segment format. The following parameters are available within the expansion. + # + # - P9K_AWS_PROFILE The name of the current AWS profile. + # - P9K_AWS_REGION The region associated with the current AWS profile. + typeset -g POWERLEVEL9K_AWS_CONTENT_EXPANSION='${P9K_AWS_PROFILE//\%/%%}${P9K_AWS_REGION:+ ${P9K_AWS_REGION//\%/%%}}' + + #[ aws_eb_env: aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) ]# + # AWS Elastic Beanstalk environment color. + typeset -g POWERLEVEL9K_AWS_EB_ENV_FOREGROUND=70 + # Custom icon. + typeset -g POWERLEVEL9K_AWS_EB_ENV_VISUAL_IDENTIFIER_EXPANSION='eb' + + ##########[ azure: azure account name (https://docs.microsoft.com/en-us/cli/azure) ]########## + # Show azure only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show azure. + typeset -g POWERLEVEL9K_AZURE_SHOW_ON_COMMAND='az|terraform|pulumi|terragrunt' + + # POWERLEVEL9K_AZURE_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current azure account name gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_AZURE_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_AZURE_CLASSES defines the account class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_AZURE_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' OTHER) + # + # If your current azure account is "company_test", its class is TEST because "company_test" + # doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_AZURE_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_AZURE_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_AZURE_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_AZURE_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' OTHER) + + # Azure account name color. + typeset -g POWERLEVEL9K_AZURE_OTHER_FOREGROUND=32 + # Custom icon. + # typeset -g POWERLEVEL9K_AZURE_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ gcloud: google cloud account and project (https://cloud.google.com/) ]########### + # Show gcloud only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show gcloud. + typeset -g POWERLEVEL9K_GCLOUD_SHOW_ON_COMMAND='gcloud|gcs|gsutil' + # Google cloud color. + typeset -g POWERLEVEL9K_GCLOUD_FOREGROUND=32 + + # Google cloud format. Change the value of POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION and/or + # POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION if the default is too verbose or not informative + # enough. You can use the following parameters in the expansions. Each of them corresponds to the + # output of `gcloud` tool. + # + # Parameter | Source + # -------------------------|-------------------------------------------------------------------- + # P9K_GCLOUD_CONFIGURATION | gcloud config configurations list --format='value(name)' + # P9K_GCLOUD_ACCOUNT | gcloud config get-value account + # P9K_GCLOUD_PROJECT_ID | gcloud config get-value project + # P9K_GCLOUD_PROJECT_NAME | gcloud projects describe $P9K_GCLOUD_PROJECT_ID --format='value(name)' + # + # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced with '%%'. + # + # Obtaining project name requires sending a request to Google servers. This can take a long time + # and even fail. When project name is unknown, P9K_GCLOUD_PROJECT_NAME is not set and gcloud + # prompt segment is in state PARTIAL. When project name gets known, P9K_GCLOUD_PROJECT_NAME gets + # set and gcloud prompt segment transitions to state COMPLETE. + # + # You can customize the format, icon and colors of gcloud segment separately for states PARTIAL + # and COMPLETE. You can also hide gcloud in state PARTIAL by setting + # POWERLEVEL9K_GCLOUD_PARTIAL_VISUAL_IDENTIFIER_EXPANSION and + # POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION to empty. + typeset -g POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_ID//\%/%%}' + typeset -g POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_NAME//\%/%%}' + + # Send a request to Google (by means of `gcloud projects describe ...`) to obtain project name + # this often. Negative value disables periodic polling. In this mode project name is retrieved + # only when the current configuration, account or project id changes. + typeset -g POWERLEVEL9K_GCLOUD_REFRESH_PROJECT_NAME_SECONDS=60 + + # Custom icon. + # typeset -g POWERLEVEL9K_GCLOUD_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ google_app_cred: google application credentials (https://cloud.google.com/docs/authentication/production) ]# + # Show google_app_cred only when the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show google_app_cred. + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_SHOW_ON_COMMAND='terraform|pulumi|terragrunt' + + # Google application credentials classes for the purpose of using different colors, icons and + # expansions with different credentials. + # + # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES is an array with even number of elements. The first + # element in each pair defines a pattern against which the current kubernetes context gets + # matched. More specifically, it's P9K_CONTENT prior to the application of context expansion + # (see below) that gets matched. If you unset all POWERLEVEL9K_GOOGLE_APP_CRED_*CONTENT_EXPANSION + # parameters, you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES defines the context class. Patterns are tried in order. + # The first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( + # '*:*prod*:*' PROD + # '*:*test*:*' TEST + # '*' DEFAULT) + # + # If your current Google application credentials is "service_account deathray-testing x@y.com", + # its class is TEST because it doesn't match the pattern '* *prod* *' but does match '* *test* *'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_CONTENT_EXPANSION='$P9K_GOOGLE_APP_CRED_PROJECT_ID' + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( + # '*:*prod*:*' PROD # These values are examples that are unlikely + # '*:*test*:*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_FOREGROUND=32 + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use POWERLEVEL9K_GOOGLE_APP_CRED_CONTENT_EXPANSION to specify the content displayed by + # google_app_cred segment. Parameter expansions are very flexible and fast, too. See reference: + # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. + # + # You can use the following parameters in the expansion. Each of them corresponds to one of the + # fields in the JSON file pointed to by GOOGLE_APPLICATION_CREDENTIALS. + # + # Parameter | JSON key file field + # ---------------------------------+--------------- + # P9K_GOOGLE_APP_CRED_TYPE | type + # P9K_GOOGLE_APP_CRED_PROJECT_ID | project_id + # P9K_GOOGLE_APP_CRED_CLIENT_EMAIL | client_email + # + # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced by '%%'. + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_CONTENT_EXPANSION='${P9K_GOOGLE_APP_CRED_PROJECT_ID//\%/%%}' + + ##############[ toolbox: toolbox name (https://github.com/containers/toolbox) ]############### + # Toolbox color. + typeset -g POWERLEVEL9K_TOOLBOX_FOREGROUND=178 + # Don't display the name of the toolbox if it matches fedora-toolbox-*. + typeset -g POWERLEVEL9K_TOOLBOX_CONTENT_EXPANSION='${P9K_TOOLBOX_NAME:#fedora-toolbox-*}' + # Custom icon. + # typeset -g POWERLEVEL9K_TOOLBOX_VISUAL_IDENTIFIER_EXPANSION='⭐' + # Custom prefix. + typeset -g POWERLEVEL9K_TOOLBOX_PREFIX='%246Fin ' + + ###############################[ public_ip: public IP address ]############################### + # Public IP color. + typeset -g POWERLEVEL9K_PUBLIC_IP_FOREGROUND=94 + # Custom icon. + # typeset -g POWERLEVEL9K_PUBLIC_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ########################[ vpn_ip: virtual private network indicator ]######################### + # VPN IP color. + typeset -g POWERLEVEL9K_VPN_IP_FOREGROUND=81 + # When on VPN, show just an icon without the IP address. + # Tip: To display the private IP address when on VPN, remove the next line. + typeset -g POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION= + # Regular expression for the VPN network interface. Run `ifconfig` or `ip -4 a show` while on VPN + # to see the name of the interface. + typeset -g POWERLEVEL9K_VPN_IP_INTERFACE='(gpd|wg|(.*tun)|tailscale)[0-9]*|(zt.*)' + # If set to true, show one segment per matching network interface. If set to false, show only + # one segment corresponding to the first matching network interface. + # Tip: If you set it to true, you'll probably want to unset POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION. + typeset -g POWERLEVEL9K_VPN_IP_SHOW_ALL=false + # Custom icon. + # typeset -g POWERLEVEL9K_VPN_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ ip: ip address and bandwidth usage for a specified network interface ]########### + # IP color. + typeset -g POWERLEVEL9K_IP_FOREGROUND=38 + # The following parameters are accessible within the expansion: + # + # Parameter | Meaning + # ----------------------+------------------------------------------- + # P9K_IP_IP | IP address + # P9K_IP_INTERFACE | network interface + # P9K_IP_RX_BYTES | total number of bytes received + # P9K_IP_TX_BYTES | total number of bytes sent + # P9K_IP_RX_BYTES_DELTA | number of bytes received since last prompt + # P9K_IP_TX_BYTES_DELTA | number of bytes sent since last prompt + # P9K_IP_RX_RATE | receive rate (since last prompt) + # P9K_IP_TX_RATE | send rate (since last prompt) + typeset -g POWERLEVEL9K_IP_CONTENT_EXPANSION='${P9K_IP_RX_RATE:+%70F⇣$P9K_IP_RX_RATE }${P9K_IP_TX_RATE:+%215F⇡$P9K_IP_TX_RATE }%38F$P9K_IP_IP' + # Show information for the first network interface whose name matches this regular expression. + # Run `ifconfig` or `ip -4 a show` to see the names of all network interfaces. + typeset -g POWERLEVEL9K_IP_INTERFACE='[ew].*' + # Custom icon. + # typeset -g POWERLEVEL9K_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #########################[ proxy: system-wide http/https/ftp proxy ]########################## + # Proxy color. + typeset -g POWERLEVEL9K_PROXY_FOREGROUND=68 + # Custom icon. + # typeset -g POWERLEVEL9K_PROXY_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################################[ battery: internal battery ]################################# + # Show battery in red when it's below this level and not connected to power supply. + typeset -g POWERLEVEL9K_BATTERY_LOW_THRESHOLD=20 + typeset -g POWERLEVEL9K_BATTERY_LOW_FOREGROUND=160 + # Show battery in green when it's charging or fully charged. + typeset -g POWERLEVEL9K_BATTERY_{CHARGING,CHARGED}_FOREGROUND=70 + # Show battery in yellow when it's discharging. + typeset -g POWERLEVEL9K_BATTERY_DISCONNECTED_FOREGROUND=178 + # Battery pictograms going from low to high level of charge. + typeset -g POWERLEVEL9K_BATTERY_STAGES=('%K{232}▁' '%K{232}▂' '%K{232}▃' '%K{232}▄' '%K{232}▅' '%K{232}▆' '%K{232}▇' '%K{232}█') + # Don't show the remaining time to charge/discharge. + typeset -g POWERLEVEL9K_BATTERY_VERBOSE=false + + #####################################[ wifi: wifi speed ]##################################### + # WiFi color. + typeset -g POWERLEVEL9K_WIFI_FOREGROUND=68 + # Custom icon. + # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use different colors and icons depending on signal strength ($P9K_WIFI_BARS). + # + # # Wifi colors and icons for different signal strength levels (low to high). + # typeset -g my_wifi_fg=(68 68 68 68 68) # <-- change these values + # typeset -g my_wifi_icon=('WiFi' 'WiFi' 'WiFi' 'WiFi' 'WiFi') # <-- change these values + # + # typeset -g POWERLEVEL9K_WIFI_CONTENT_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}$P9K_WIFI_LAST_TX_RATE Mbps' + # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}${my_wifi_icon[P9K_WIFI_BARS+1]}' + # + # The following parameters are accessible within the expansions: + # + # Parameter | Meaning + # ----------------------+--------------- + # P9K_WIFI_SSID | service set identifier, a.k.a. network name + # P9K_WIFI_LINK_AUTH | authentication protocol such as "wpa2-psk" or "none"; empty if unknown + # P9K_WIFI_LAST_TX_RATE | wireless transmit rate in megabits per second + # P9K_WIFI_RSSI | signal strength in dBm, from -120 to 0 + # P9K_WIFI_NOISE | noise in dBm, from -120 to 0 + # P9K_WIFI_BARS | signal strength in bars, from 0 to 4 (derived from P9K_WIFI_RSSI and P9K_WIFI_NOISE) + + ####################################[ time: current time ]#################################### + # Current time color. + typeset -g POWERLEVEL9K_TIME_FOREGROUND=66 + # Format for the current time: 09:51:02. See `man 3 strftime`. + typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}' + # If set to true, time will update when you hit enter. This way prompts for the past + # commands will contain the start times of their commands as opposed to the default + # behavior where they contain the end times of their preceding commands. + typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false + # Custom icon. + typeset -g POWERLEVEL9K_TIME_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + typeset -g POWERLEVEL9K_TIME_PREFIX='%246Fat ' + + # Example of a user-defined prompt segment. Function prompt_example will be called on every + # prompt if `example` prompt segment is added to POWERLEVEL9K_LEFT_PROMPT_ELEMENTS or + # POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS. It displays an icon and orange text greeting the user. + # + # Type `p10k help segment` for documentation and a more sophisticated example. + function prompt_example() { + p10k segment -f 208 -i '⭐' -t 'hello, %n' + } + + # User-defined prompt segments may optionally provide an instant_prompt_* function. Its job + # is to generate the prompt segment for display in instant prompt. See + # https://github.com/romkatv/powerlevel10k#instant-prompt. + # + # Powerlevel10k will call instant_prompt_* at the same time as the regular prompt_* function + # and will record all `p10k segment` calls it makes. When displaying instant prompt, Powerlevel10k + # will replay these calls without actually calling instant_prompt_*. It is imperative that + # instant_prompt_* always makes the same `p10k segment` calls regardless of environment. If this + # rule is not observed, the content of instant prompt will be incorrect. + # + # Usually, you should either not define instant_prompt_* or simply call prompt_* from it. If + # instant_prompt_* is not defined for a segment, the segment won't be shown in instant prompt. + function instant_prompt_example() { + # Since prompt_example always makes the same `p10k segment` calls, we can call it from + # instant_prompt_example. This will give us the same `example` prompt segment in the instant + # and regular prompts. + prompt_example + } + + # User-defined prompt segments can be customized the same way as built-in segments. + # typeset -g POWERLEVEL9K_EXAMPLE_FOREGROUND=208 + # typeset -g POWERLEVEL9K_EXAMPLE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt + # when accepting a command line. Supported values: + # + # - off: Don't change prompt when accepting a command line. + # - always: Trim down prompt when accepting a command line. + # - same-dir: Trim down prompt when accepting a command line unless this is the first command + # typed after changing current working directory. + typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always + + # Instant prompt mode. + # + # - off: Disable instant prompt. Choose this if you've tried instant prompt and found + # it incompatible with your zsh configuration files. + # - quiet: Enable instant prompt and don't print warnings when detecting console output + # during zsh initialization. Choose this if you've read and understood + # https://github.com/romkatv/powerlevel10k#instant-prompt. + # - verbose: Enable instant prompt and print a warning when detecting console output during + # zsh initialization. Choose this if you've never tried instant prompt, haven't + # seen the warning, or if you are unsure what this all means. + typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose + + # Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized. + # For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload + # can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you + # really need it. + typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true + + # If p10k is already loaded, reload configuration. + # This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true. + (( ! $+functions[p10k] )) || p10k reload +} + +# Tell `p10k configure` which file it should overwrite. +typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a} + +(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]} +'builtin' 'unset' 'p10k_config_opts' diff --git a/packages/vscode/default.nix b/packages/vscode/default.nix new file mode 100644 index 0000000..0b36bad --- /dev/null +++ b/packages/vscode/default.nix @@ -0,0 +1,10 @@ +{ inputs, pkgs, lib, config, ... }: +{ + programs.vscode = { + enable = true; + package = pkgs.vscodium; + extensions = with pkgs.vscode-extensions; [ + vscodevim.vim + ]; + }; +} diff --git a/packages/waybar/config b/packages/waybar/config new file mode 100644 index 0000000..87ec24b --- /dev/null +++ b/packages/waybar/config @@ -0,0 +1,50 @@ +{ + "layer": "bottom", + "position": "top", + "height": 35, + "spacing": 0, + "modules-left": ["clock", "sway/workspaces","sway/mode"], + "modules-center": ["sway/window"], + "modules-right": ["network","battery", "pulseaudio", "tray"], + + "cpu": { + "interval": 5, + "format": "CPU: {usage}% {avg_frequency} GHz" + }, + + "memory": { + "interval": 5, + "format": "RAM: {used} GiB" + }, + + "battery": { + "bat": "BAT0", + "states": { + "good": 90, + "warning": 20, + "critical": 7 + }, + "format": "{icon} {capacity}%", + "format-charging": " {capacity}%", + "format-plugged": " {capacity}%", + "format-alt": "{time} {icon}", + "format-icons": ["", "", "" , "", ""] + }, + + "pulseaudio": { + "format": "{icon} {volume}%", + "format-muted": " Muted", + "format-icons": ["", "", ""] + }, + + "clock": { + "format": "{:%d.%m.%Y | %H:%M}" + }, + + "network": { + "format-wifi": " {essid} {signalStrength}%", + "format-disconnected": " No Connection" + } + + +} diff --git a/packages/waybar/default.nix b/packages/waybar/default.nix new file mode 100644 index 0000000..4dde40d --- /dev/null +++ b/packages/waybar/default.nix @@ -0,0 +1,15 @@ +{ pkgs, config, lib,... }: +{ + home.packages = with pkgs; [ + + ]; + + home.file = + lib.mkMerge [ + { + ".config/waybar/config".source = ./config; + ".config/waybar/style.css".source = ./style.css; + ".config/waybar/mocha.css".source = ./mocha.css; + } + ]; +} diff --git a/packages/waybar/mocha.css b/packages/waybar/mocha.css new file mode 100644 index 0000000..0eb6a82 --- /dev/null +++ b/packages/waybar/mocha.css @@ -0,0 +1,26 @@ +@define-color rosewater #f5e0dc; +@define-color flamingo #f2cdcd; +@define-color pink #f5c2e7; +@define-color mauve #cba6f7; +@define-color red #f38ba8; +@define-color maroon #eba0ac; +@define-color peach #fab387; +@define-color yellow #f9e2af; +@define-color green #a6e3a1; +@define-color teal #94e2d5; +@define-color sky #89dceb; +@define-color sapphire #74c7ec; +@define-color blue #89b4fa; +@define-color lavender #b4befe; +@define-color text #cdd6f4; +@define-color subtext1 #bac2de; +@define-color subtext0 #a6adc8; +@define-color overlay2 #9399b2; +@define-color overlay1 #7f849c; +@define-color overlay0 #6c7086; +@define-color surface2 #585b70; +@define-color surface1 #45475a; +@define-color surface0 #313244; +@define-color base #1e1e2e; +@define-color mantle #181825; +@define-color crust #11111b; diff --git a/packages/waybar/style.css b/packages/waybar/style.css new file mode 100644 index 0000000..1ca7a76 --- /dev/null +++ b/packages/waybar/style.css @@ -0,0 +1,78 @@ +@import "mocha.css"; + +* { + border: none; + border-radius: 0; + font-family: "Fira Code"; + font-weight: bold; + font-size: 13px; + min-height: 0; +} + +window#waybar { +/* background: rgba(0,130,130,0); */ + background: @base; +} + +#clock, +#workspaces, +#window, +#cpu, +#memory, +#network, +#pulseaudio, +#battery{ + background: rgba(10,10,10,100); + opacity: 0.8; +/* color: #00ffff; */ + color: @text; + padding: 0px 10px ; + margin: 3px 0px; +} + +#workspaces button{ + color: #00ffff; + border-radius: 7px; + margin: 3px 0px; + margin-left: 6px; +} + +#workspaces button.focused{ + background: #00ffff; + color: #000000; + opacity: 0.8; + transition: 0.3s; +} + +#workspaces button:hover{ + background: #ffffff; + color: #000000; + opacity: 0.8; + transition: 0.5s; +} + +#window{ + border-radius: 10px; +} + +#waybar.empty #window { + background: none; +} + +#network{ + border-radius: 10px 0px 0px 10px; +} + +#pulseaudio{ + border-radius: 0px 10px 10px 0px; + margin-right: 5px +} + +#workspaces{ + border-radius: 0px 10px 10px 0px; +} + +#clock{ + border-radius: 10px 0px 0px 10px; + margin-left: 5px +} diff --git a/packages/wpaperd/config.toml b/packages/wpaperd/config.toml new file mode 100644 index 0000000..91e2f67 --- /dev/null +++ b/packages/wpaperd/config.toml @@ -0,0 +1,4 @@ +[any] +duration = "10m" +mode = "center" +path = "/home/n/Documents/Wallpapers" diff --git a/packages/wpaperd/default.nix b/packages/wpaperd/default.nix new file mode 100644 index 0000000..bda3240 --- /dev/null +++ b/packages/wpaperd/default.nix @@ -0,0 +1,12 @@ +{ pkgs, config, lib,... }: +{ + home.packages = with pkgs; [ + ]; + + home.file = + lib.mkMerge [ + { + ".config/wpaperd/config.toml".source = ./config.toml; + } + ]; +}