refactor: make the whole thing more generic
29
modules/home-manager/clean-home-dir.nix
Normal file
@@ -0,0 +1,29 @@
|
||||
{ config, ... }: {
|
||||
xdg.enable = true;
|
||||
xdg.userDirs.enable = true;
|
||||
home.sessionVariables = {
|
||||
HISTFILE = "${config.xdg.stateHome}/bash/history";
|
||||
NPM_CONFIG_USERCONFIG = "${config.xdg.configHome}/npm/config";
|
||||
NPM_CONFIG_CACHE = "${config.xdg.cacheHome}/npm";
|
||||
NPM_CONFIG_TMP = "${config.xdg.stateHome}/npm";
|
||||
WINEPREFIX = "${config.xdg.configHome}/wineprefixes/default";
|
||||
_JAVA_OPTGRADLE_USER_HOMEIONS =
|
||||
''-Djava.util.prefs.userRoot="${config.xdg.configHome}"/java'';
|
||||
GRADLE_USER_HOME = "${config.xdg.configHome}/gradle";
|
||||
DVDCSS_CACHE = "${config.xdg.cacheHome}/dvdcss";
|
||||
DOCKER_CONFIG = "${config.xdg.configHome}/docker";
|
||||
PYTHON_HISTORY = "${config.xdg.stateHome}/python/history";
|
||||
PYTHONCACHEPREFIX = "${config.xdg.cacheHome}/python";
|
||||
PYTHONUSERBASE = "${config.xdg.dataHome}/python";
|
||||
WGETRC = "${config.xdg.configHome}/wgetrc";
|
||||
XCOMPOSEFILE = "${config.xdg.configHome}/X11/xcompose";
|
||||
XCOMPOSECACHE = "${config.xdg.cacheHome}/X11/xcompose";
|
||||
};
|
||||
gtk.gtk2.configLocation = "${config.xdg.configHome}/gtk-2.0/gtkrc";
|
||||
programs.gpg.homedir = "${config.xdg.configHome}/gnupg";
|
||||
programs.zsh.dotDir = "${config.xdg.configHome}/zsh";
|
||||
programs.zsh.history.path = "${config.xdg.stateHome}/zsh/history";
|
||||
xdg.configFile.wgetrc.text = ''
|
||||
hsts-file="${config.xdg.cacheHome}/wget-hsts"
|
||||
'';
|
||||
}
|
||||
677
modules/home-manager/default-apps.nix
Normal file
@@ -0,0 +1,677 @@
|
||||
{ config, ... }: {
|
||||
xdg.mimeApps = {
|
||||
enable = true;
|
||||
defaultApplications = {
|
||||
.3dm x-world/x-3dmf
|
||||
.3dmf x-world/x-3dmf
|
||||
.7z application/x-7z-compressed
|
||||
.a application/octet-stream
|
||||
.aab application/x-authorware-bin
|
||||
.aam application/x-authorware-map
|
||||
.aas application/x-authorware-seg
|
||||
.abc text/vnd.abc
|
||||
.acgi text/html
|
||||
.afl video/animaflex
|
||||
.ai application/postscript
|
||||
.aif audio/aiff
|
||||
.aif audio/x-aiff
|
||||
.aifc audio/aiff
|
||||
.aifc audio/x-aiff
|
||||
.aiff audio/aiff
|
||||
.aiff audio/x-aiff
|
||||
.aim application/x-aim
|
||||
.aip text/x-audiosoft-intra
|
||||
.ani application/x-navi-animation
|
||||
.aos application/x-nokia-9000-communicator-add-on-software
|
||||
.aps application/mime
|
||||
.arc application/octet-stream
|
||||
.arj application/arj
|
||||
.arj application/octet-stream
|
||||
.art image/x-jg
|
||||
.asf video/x-ms-asf
|
||||
.asm text/x-asm
|
||||
.asp text/asp
|
||||
.asx application/x-mplayer2
|
||||
.asx video/x-ms-asf
|
||||
.asx video/x-ms-asf-plugin
|
||||
.au audio/basic
|
||||
.au audio/x-au
|
||||
.avi application/x-troff-msvideo
|
||||
.avi video/avi
|
||||
.avi video/msvideo
|
||||
.avi video/x-msvideo
|
||||
.avs video/avs-video
|
||||
.bcpio application/x-bcpio
|
||||
.bin application/mac-binary
|
||||
.bin application/macbinary
|
||||
.bin application/octet-stream
|
||||
.bin application/x-binary
|
||||
.bin application/x-macbinary
|
||||
.bm image/bmp
|
||||
.bmp image/bmp
|
||||
.bmp image/x-windows-bmp
|
||||
.boo application/book
|
||||
.book application/book
|
||||
.boz application/x-bzip2
|
||||
.bsh application/x-bsh
|
||||
.bz application/x-bzip
|
||||
.bz2 application/x-bzip2
|
||||
.c text/plain
|
||||
.c text/x-c
|
||||
.c++ text/plain
|
||||
.cat application/vnd.ms-pki.seccat
|
||||
.cc text/plain
|
||||
.cc text/x-c
|
||||
.ccad application/clariscad
|
||||
.cco application/x-cocoa
|
||||
.cdf application/cdf
|
||||
.cdf application/x-cdf
|
||||
.cdf application/x-netcdf
|
||||
.cer application/pkix-cert
|
||||
.cer application/x-x509-ca-cert
|
||||
.cha application/x-chat
|
||||
.chat application/x-chat
|
||||
.class application/java
|
||||
.class application/java-byte-code
|
||||
.class application/x-java-class
|
||||
.com application/octet-stream
|
||||
.com text/plain
|
||||
.conf text/plain
|
||||
.cpio application/x-cpio
|
||||
.cpp text/x-c
|
||||
.cpt application/mac-compactpro
|
||||
.cpt application/x-compactpro
|
||||
.cpt application/x-cpt
|
||||
.crl application/pkcs-crl
|
||||
.crl application/pkix-crl
|
||||
.crt application/pkix-cert
|
||||
.crt application/x-x509-ca-cert
|
||||
.crt application/x-x509-user-cert
|
||||
.csh application/x-csh
|
||||
.csh text/x-script.csh
|
||||
.css application/x-pointplus
|
||||
.css text/css
|
||||
.csv text/csv
|
||||
.cxx text/plain
|
||||
.dcr application/x-director
|
||||
.deepv application/x-deepv
|
||||
.def text/plain
|
||||
.der application/x-x509-ca-cert
|
||||
.dif video/x-dv
|
||||
.dir application/x-director
|
||||
.dl video/dl
|
||||
.dl video/x-dl
|
||||
.doc application/msword
|
||||
.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
.dot application/msword
|
||||
.dp application/commonground
|
||||
.drw application/drafting
|
||||
.dump application/octet-stream
|
||||
.dv video/x-dv
|
||||
.dvi application/x-dvi
|
||||
.dwf drawing/x-dwf (old)
|
||||
.dwf model/vnd.dwf
|
||||
.dwg application/acad
|
||||
.dwg image/vnd.dwg
|
||||
.dwg image/x-dwg
|
||||
.dxf application/dxf
|
||||
.dxf image/vnd.dwg
|
||||
.dxf image/x-dwg
|
||||
.dxr application/x-director
|
||||
.el text/x-script.elisp
|
||||
.elc application/x-bytecode.elisp (compiled elisp)
|
||||
.elc application/x-elc
|
||||
.env application/x-envoy
|
||||
.eot application/vnd.ms-fontobject
|
||||
.eps application/postscript
|
||||
.es application/x-esrehber
|
||||
.etx text/x-setext
|
||||
.evy application/envoy
|
||||
.evy application/x-envoy
|
||||
.exe application/octet-stream
|
||||
.f text/plain
|
||||
.f text/x-fortran
|
||||
.f77 text/x-fortran
|
||||
.f90 text/plain
|
||||
.f90 text/x-fortran
|
||||
.fdf application/vnd.fdf
|
||||
.fif application/fractals
|
||||
.fif image/fif
|
||||
.flac audio/flac
|
||||
.fli video/fli
|
||||
.fli video/x-fli
|
||||
.flo image/florian
|
||||
.flx text/vnd.fmi.flexstor
|
||||
.fmf video/x-atomic3d-feature
|
||||
.for text/plain
|
||||
.for text/x-fortran
|
||||
.fpx image/vnd.fpx
|
||||
.fpx image/vnd.net-fpx
|
||||
.frl application/freeloader
|
||||
.funk audio/make
|
||||
.g text/plain
|
||||
.g3 image/g3fax
|
||||
.gif image/gif
|
||||
.gl video/gl
|
||||
.gl video/x-gl
|
||||
.gsd audio/x-gsm
|
||||
.gsm audio/x-gsm
|
||||
.gsp application/x-gsp
|
||||
.gss application/x-gss
|
||||
.gtar application/x-gtar
|
||||
.gz application/x-compressed
|
||||
.gz application/x-gzip
|
||||
.gzip application/x-gzip
|
||||
.gzip multipart/x-gzip
|
||||
.h text/plain
|
||||
.h text/x-h
|
||||
.hdf application/x-hdf
|
||||
.help application/x-helpfile
|
||||
.hgl application/vnd.hp-hpgl
|
||||
.hh text/plain
|
||||
.hh text/x-h
|
||||
.hlb text/x-script
|
||||
.hlp application/hlp
|
||||
.hlp application/x-helpfile
|
||||
.hlp application/x-winhelp
|
||||
.hpg application/vnd.hp-hpgl
|
||||
.hpgl application/vnd.hp-hpgl
|
||||
.hqx application/binhex
|
||||
.hqx application/binhex4
|
||||
.hqx application/mac-binhex
|
||||
.hqx application/mac-binhex40
|
||||
.hqx application/x-binhex40
|
||||
.hqx application/x-mac-binhex40
|
||||
.hta application/hta
|
||||
.htc text/x-component
|
||||
.htm text/html
|
||||
.html text/html
|
||||
.htmls text/html
|
||||
.htt text/webviewhtml
|
||||
.htx text/html
|
||||
.ice x-conference/x-cooltalk
|
||||
.ico image/x-icon
|
||||
.ics text/calendar
|
||||
.idc text/plain
|
||||
.ief image/ief
|
||||
.iefs image/ief
|
||||
.iges application/iges
|
||||
.iges model/iges
|
||||
.igs application/iges
|
||||
.igs model/iges
|
||||
.ima application/x-ima
|
||||
.imap application/x-httpd-imap
|
||||
.inf application/inf
|
||||
.ins application/x-internett-signup
|
||||
.ip application/x-ip2
|
||||
.isu video/x-isvideo
|
||||
.it audio/it
|
||||
.iv application/x-inventor
|
||||
.ivr i-world/i-vrml
|
||||
.ivy application/x-livescreen
|
||||
.jam audio/x-jam
|
||||
.jav text/plain
|
||||
.jav text/x-java-source
|
||||
.java text/plain
|
||||
.java text/x-java-source
|
||||
.jcm application/x-java-commerce
|
||||
.jfif image/jpeg
|
||||
.jfif image/pjpeg
|
||||
.jfif-tbnl image/jpeg
|
||||
.jpe image/jpeg
|
||||
.jpe image/pjpeg
|
||||
.jpeg image/jpeg
|
||||
.jpeg image/pjpeg
|
||||
.jpg image/jpeg
|
||||
.jpg image/pjpeg
|
||||
.jps image/x-jps
|
||||
.js application/x-javascript
|
||||
.js application/javascript
|
||||
.js application/ecmascript
|
||||
.js text/javascript
|
||||
.js text/ecmascript
|
||||
.json application/json
|
||||
.jut image/jutvision
|
||||
.kar audio/midi
|
||||
.kar music/x-karaoke
|
||||
.ksh application/x-ksh
|
||||
.ksh text/x-script.ksh
|
||||
.la audio/nspaudio
|
||||
.la audio/x-nspaudio
|
||||
.lam audio/x-liveaudio
|
||||
.latex application/x-latex
|
||||
.lha application/lha
|
||||
.lha application/octet-stream
|
||||
.lha application/x-lha
|
||||
.lhx application/octet-stream
|
||||
.list text/plain
|
||||
.lma audio/nspaudio
|
||||
.lma audio/x-nspaudio
|
||||
.log text/plain
|
||||
.lsp application/x-lisp
|
||||
.lsp text/x-script.lisp
|
||||
.lst text/plain
|
||||
.lsx text/x-la-asf
|
||||
.ltx application/x-latex
|
||||
.lzh application/octet-stream
|
||||
.lzh application/x-lzh
|
||||
.lzx application/lzx
|
||||
.lzx application/octet-stream
|
||||
.lzx application/x-lzx
|
||||
.m text/plain
|
||||
.m text/x-m
|
||||
.m1v video/mpeg
|
||||
.m2a audio/mpeg
|
||||
.m2v video/mpeg
|
||||
.m3u audio/x-mpequrl
|
||||
.man application/x-troff-man
|
||||
.map application/x-navimap
|
||||
.mar text/plain
|
||||
.mbd application/mbedlet
|
||||
.mc$ application/x-magic-cap-package-1.0
|
||||
.mcd application/mcad
|
||||
.mcd application/x-mathcad
|
||||
.mcf image/vasa
|
||||
.mcf text/mcf
|
||||
.mcp application/netmc
|
||||
.me application/x-troff-me
|
||||
.mht message/rfc822
|
||||
.mhtml message/rfc822
|
||||
.mid application/x-midi
|
||||
.mid audio/midi
|
||||
.mid audio/x-mid
|
||||
.mid audio/x-midi
|
||||
.mid music/crescendo
|
||||
.mid x-music/x-midi
|
||||
.midi application/x-midi
|
||||
.midi audio/midi
|
||||
.midi audio/x-mid
|
||||
.midi audio/x-midi
|
||||
.midi music/crescendo
|
||||
.midi x-music/x-midi
|
||||
.mif application/x-frame
|
||||
.mif application/x-mif
|
||||
.mime message/rfc822
|
||||
.mime www/mime
|
||||
.mjf audio/x-vnd.audioexplosion.mjuicemediafile
|
||||
.mjpg video/x-motion-jpeg
|
||||
.mka audio/x-matroska
|
||||
.mkv video/x-matroska
|
||||
.mm application/base64
|
||||
.mm application/x-meme
|
||||
.mme application/base64
|
||||
.mod audio/mod
|
||||
.mod audio/x-mod
|
||||
.moov video/quicktime
|
||||
.mov video/quicktime
|
||||
.movie video/x-sgi-movie
|
||||
.mp2 audio/mpeg
|
||||
.mp2 audio/x-mpeg
|
||||
.mp2 video/mpeg
|
||||
.mp2 video/x-mpeg
|
||||
.mp2 video/x-mpeq2a
|
||||
.mp3 audio/mpeg3
|
||||
.mp3 audio/x-mpeg-3
|
||||
.mp3 video/mpeg
|
||||
.mp3 video/x-mpeg
|
||||
.mp4 video/mp4
|
||||
.mpa audio/mpeg
|
||||
.mpa video/mpeg
|
||||
.mpc application/x-project
|
||||
.mpe video/mpeg
|
||||
.mpeg video/mpeg
|
||||
.mpg audio/mpeg
|
||||
.mpg video/mpeg
|
||||
.mpga audio/mpeg
|
||||
.mpp application/vnd.ms-project
|
||||
.mpt application/x-project
|
||||
.mpv application/x-project
|
||||
.mpx application/x-project
|
||||
.mrc application/marc
|
||||
.ms application/x-troff-ms
|
||||
.mv video/x-sgi-movie
|
||||
.my audio/make
|
||||
.mzz application/x-vnd.audioexplosion.mzz
|
||||
.nap image/naplps
|
||||
.naplps image/naplps
|
||||
.nc application/x-netcdf
|
||||
.ncm application/vnd.nokia.configuration-message
|
||||
.nif image/x-niff
|
||||
.niff image/x-niff
|
||||
.nix application/x-mix-transfer
|
||||
.nsc application/x-conference
|
||||
.nvd application/x-navidoc
|
||||
.o application/octet-stream
|
||||
.oda application/oda
|
||||
.ogg audio/ogg
|
||||
.ogg video/ogg
|
||||
.omc application/x-omc
|
||||
.omcd application/x-omcdatamaker
|
||||
.omcr application/x-omcregerator
|
||||
.otf font/otf
|
||||
.p text/x-pascal
|
||||
.p10 application/pkcs10
|
||||
.p10 application/x-pkcs10
|
||||
.p12 application/pkcs-12
|
||||
.p12 application/x-pkcs12
|
||||
.p7a application/x-pkcs7-signature
|
||||
.p7c application/pkcs7-mime
|
||||
.p7c application/x-pkcs7-mime
|
||||
.p7m application/pkcs7-mime
|
||||
.p7m application/x-pkcs7-mime
|
||||
.p7r application/x-pkcs7-certreqresp
|
||||
.p7s application/pkcs7-signature
|
||||
.part application/pro_eng
|
||||
.pas text/pascal
|
||||
.pbm image/x-portable-bitmap
|
||||
.pcl application/vnd.hp-pcl
|
||||
.pcl application/x-pcl
|
||||
.pct image/x-pict
|
||||
.pcx image/x-pcx
|
||||
.pdb chemical/x-pdb
|
||||
.pdf application/pdf
|
||||
.pfunk audio/make
|
||||
.pfunk audio/make.my.funk
|
||||
.pgm image/x-portable-graymap
|
||||
.pgm image/x-portable-greymap
|
||||
.pic image/pict
|
||||
.pict image/pict
|
||||
.pkg application/x-newton-compatible-pkg
|
||||
.pko application/vnd.ms-pki.pko
|
||||
.pl text/plain
|
||||
.pl text/x-script.perl
|
||||
.plx application/x-pixclscript
|
||||
.pm image/x-xpixmap
|
||||
.pm text/x-script.perl-module
|
||||
.pm4 application/x-pagemaker
|
||||
.pm5 application/x-pagemaker
|
||||
.png image/png
|
||||
.pnm application/x-portable-anymap
|
||||
.pnm image/x-portable-anymap
|
||||
.pot application/mspowerpoint
|
||||
.pot application/vnd.ms-powerpoint
|
||||
.pov model/x-pov
|
||||
.ppa application/vnd.ms-powerpoint
|
||||
.ppm image/x-portable-pixmap
|
||||
.pps application/mspowerpoint
|
||||
.pps application/vnd.ms-powerpoint
|
||||
.ppt application/mspowerpoint
|
||||
.ppt application/powerpoint
|
||||
.ppt application/vnd.ms-powerpoint
|
||||
.ppt application/x-mspowerpoint
|
||||
.pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
.ppz application/mspowerpoint
|
||||
.pre application/x-freelance
|
||||
.prt application/pro_eng
|
||||
.ps application/postscript
|
||||
.psd application/octet-stream
|
||||
.pvu paleovu/x-pv
|
||||
.pwz application/vnd.ms-powerpoint
|
||||
.py text/x-script.phyton
|
||||
.pyc application/x-bytecode.python
|
||||
.qcp audio/vnd.qcelp
|
||||
.qd3 x-world/x-3dmf
|
||||
.qd3d x-world/x-3dmf
|
||||
.qif image/x-quicktime
|
||||
.qt video/quicktime
|
||||
.qtc video/x-qtc
|
||||
.qti image/x-quicktime
|
||||
.qtif image/x-quicktime
|
||||
.ra audio/x-pn-realaudio
|
||||
.ra audio/x-pn-realaudio-plugin
|
||||
.ra audio/x-realaudio
|
||||
.ram audio/x-pn-realaudio
|
||||
.ras application/x-cmu-raster
|
||||
.ras image/cmu-raster
|
||||
.ras image/x-cmu-raster
|
||||
.rast image/cmu-raster
|
||||
.rar application/vnd.rar
|
||||
.rexx text/x-script.rexx
|
||||
.rf image/vnd.rn-realflash
|
||||
.rgb image/x-rgb
|
||||
.rm application/vnd.rn-realmedia
|
||||
.rm audio/x-pn-realaudio
|
||||
.rmi audio/mid
|
||||
.rmm audio/x-pn-realaudio
|
||||
.rmp audio/x-pn-realaudio
|
||||
.rmp audio/x-pn-realaudio-plugin
|
||||
.rng application/ringing-tones
|
||||
.rng application/vnd.nokia.ringing-tone
|
||||
.rnx application/vnd.rn-realplayer
|
||||
.roff application/x-troff
|
||||
.rp image/vnd.rn-realpix
|
||||
.rpm audio/x-pn-realaudio-plugin
|
||||
.rt text/richtext
|
||||
.rt text/vnd.rn-realtext
|
||||
.rtf application/rtf
|
||||
.rtf application/x-rtf
|
||||
.rtf text/richtext
|
||||
.rtx application/rtf
|
||||
.rtx text/richtext
|
||||
.rv video/vnd.rn-realvideo
|
||||
.s text/x-asm
|
||||
.s3m audio/s3m
|
||||
.saveme application/octet-stream
|
||||
.sbk application/x-tbook
|
||||
.scm application/x-lotusscreencam
|
||||
.scm text/x-script.guile
|
||||
.scm text/x-script.scheme
|
||||
.scm video/x-scm
|
||||
.sdml text/plain
|
||||
.sdp application/sdp
|
||||
.sdp application/x-sdp
|
||||
.sdr application/sounder
|
||||
.sea application/sea
|
||||
.sea application/x-sea
|
||||
.set application/set
|
||||
.sgm text/sgml
|
||||
.sgm text/x-sgml
|
||||
.sgml text/sgml
|
||||
.sgml text/x-sgml
|
||||
.sh application/x-bsh
|
||||
.sh application/x-sh
|
||||
.sh application/x-shar
|
||||
.sh text/x-script.sh
|
||||
.shar application/x-bsh
|
||||
.shar application/x-shar
|
||||
.shtml text/html
|
||||
.shtml text/x-server-parsed-html
|
||||
.sid audio/x-psid
|
||||
.sit application/x-sit
|
||||
.sit application/x-stuffit
|
||||
.skd application/x-koan
|
||||
.skm application/x-koan
|
||||
.skp application/x-koan
|
||||
.skt application/x-koan
|
||||
.sl application/x-seelogo
|
||||
.smi application/smil
|
||||
.smil application/smil
|
||||
.snd audio/basic
|
||||
.snd audio/x-adpcm
|
||||
.sol application/solids
|
||||
.spc application/x-pkcs7-certificates
|
||||
.spc text/x-speech
|
||||
.spl application/futuresplash
|
||||
.spr application/x-sprite
|
||||
.sprite application/x-sprite
|
||||
.src application/x-wais-source
|
||||
.ssi text/x-server-parsed-html
|
||||
.ssm application/streamingmedia
|
||||
.sst application/vnd.ms-pki.certstore
|
||||
.step application/step
|
||||
.stl application/sla
|
||||
.stl application/vnd.ms-pki.stl
|
||||
.stl application/x-navistyle
|
||||
.stp application/step
|
||||
.sv4cpio application/x-sv4cpio
|
||||
.sv4crc application/x-sv4crc
|
||||
.svf image/vnd.dwg
|
||||
.svf image/x-dwg
|
||||
.svg image/svg+xml
|
||||
.svr application/x-world
|
||||
.svr x-world/x-svr
|
||||
.swf application/x-shockwave-flash
|
||||
.t application/x-troff
|
||||
.talk text/x-speech
|
||||
.tar application/x-tar
|
||||
.tbk application/toolbook
|
||||
.tbk application/x-tbook
|
||||
.tcl application/x-tcl
|
||||
.tcl text/x-script.tcl
|
||||
.tcsh text/x-script.tcsh
|
||||
.tex application/x-tex
|
||||
.texi application/x-texinfo
|
||||
.texinfo application/x-texinfo
|
||||
.text application/plain
|
||||
.text text/plain
|
||||
.tgz application/gnutar
|
||||
.tgz application/x-compressed
|
||||
.tif image/tiff
|
||||
.tif image/x-tiff
|
||||
.tiff image/tiff
|
||||
.tiff image/x-tiff
|
||||
.tr application/x-troff
|
||||
.ts video/mp2t
|
||||
.tsi audio/tsp-audio
|
||||
.tsp application/dsptype
|
||||
.tsp audio/tsplayer
|
||||
.tsv text/tab-separated-values
|
||||
.turbot image/florian
|
||||
.txt text/plain
|
||||
.uil text/x-uil
|
||||
.uni text/uri-list
|
||||
.unis text/uri-list
|
||||
.unv application/i-deas
|
||||
.uri text/uri-list
|
||||
.uris text/uri-list
|
||||
.ustar application/x-ustar
|
||||
.ustar multipart/x-ustar
|
||||
.uu application/octet-stream
|
||||
.uu text/x-uuencode
|
||||
.uue text/x-uuencode
|
||||
.vcd application/x-cdlink
|
||||
.vcs text/x-vcalendar
|
||||
.vda application/vda
|
||||
.vdo video/vdo
|
||||
.vew application/groupwise
|
||||
.viv video/vivo
|
||||
.viv video/vnd.vivo
|
||||
.vivo video/vivo
|
||||
.vivo video/vnd.vivo
|
||||
.vmd application/vocaltec-media-desc
|
||||
.vmf application/vocaltec-media-file
|
||||
.voc audio/voc
|
||||
.voc audio/x-voc
|
||||
.vos video/vosaic
|
||||
.vox audio/voxware
|
||||
.vqe audio/x-twinvq-plugin
|
||||
.vqf audio/x-twinvq
|
||||
.vql audio/x-twinvq-plugin
|
||||
.vrml application/x-vrml
|
||||
.vrml model/vrml
|
||||
.vrml x-world/x-vrml
|
||||
.vrt x-world/x-vrt
|
||||
.vsd application/x-visio
|
||||
.vst application/x-visio
|
||||
.vsw application/x-visio
|
||||
.w60 application/wordperfect6.0
|
||||
.w61 application/wordperfect6.1
|
||||
.w6w application/msword
|
||||
.wav audio/wav
|
||||
.wav audio/x-wav
|
||||
.wb1 application/x-qpro
|
||||
.wbmp image/vnd.wap.wbmp
|
||||
.web application/vnd.xara
|
||||
.webm video/webm
|
||||
.webp image/webp
|
||||
.wiz application/msword
|
||||
.wk1 application/x-123
|
||||
.wmf windows/metafile
|
||||
.wml text/vnd.wap.wml
|
||||
.wmlc application/vnd.wap.wmlc
|
||||
.wmls text/vnd.wap.wmlscript
|
||||
.wmlsc application/vnd.wap.wmlscriptc
|
||||
.word application/msword
|
||||
.woff font/woff
|
||||
.woff2 font/woff2
|
||||
.wp application/wordperfect
|
||||
.wp5 application/wordperfect
|
||||
.wp5 application/wordperfect6.0
|
||||
.wp6 application/wordperfect
|
||||
.wpd application/wordperfect
|
||||
.wpd application/x-wpwin
|
||||
.wq1 application/x-lotus
|
||||
.wri application/mswrite
|
||||
.wri application/x-wri
|
||||
.wrl application/x-world
|
||||
.wrl model/vrml
|
||||
.wrl x-world/x-vrml
|
||||
.wrz model/vrml
|
||||
.wrz x-world/x-vrml
|
||||
.wsc text/scriplet
|
||||
.wsrc application/x-wais-source
|
||||
.wtk application/x-wintalk
|
||||
.xbm image/x-xbitmap
|
||||
.xbm image/x-xbm
|
||||
.xbm image/xbm
|
||||
.xdr video/x-amt-demorun
|
||||
.xgz xgl/drawing
|
||||
.xif image/vnd.xiff
|
||||
.xl application/excel
|
||||
.xla application/excel
|
||||
.xla application/x-excel
|
||||
.xla application/x-msexcel
|
||||
.xlb application/excel
|
||||
.xlb application/vnd.ms-excel
|
||||
.xlb application/x-excel
|
||||
.xlc application/excel
|
||||
.xlc application/vnd.ms-excel
|
||||
.xlc application/x-excel
|
||||
.xld application/excel
|
||||
.xld application/x-excel
|
||||
.xlk application/excel
|
||||
.xlk application/x-excel
|
||||
.xll application/excel
|
||||
.xll application/vnd.ms-excel
|
||||
.xll application/x-excel
|
||||
.xlm application/excel
|
||||
.xlm application/vnd.ms-excel
|
||||
.xlm application/x-excel
|
||||
.xls application/excel
|
||||
.xls application/vnd.ms-excel
|
||||
.xls application/x-excel
|
||||
.xls application/x-msexcel
|
||||
.xlt application/excel
|
||||
.xlt application/x-excel
|
||||
.xlv application/excel
|
||||
.xlv application/x-excel
|
||||
.xlw application/excel
|
||||
.xlw application/vnd.ms-excel
|
||||
.xlw application/x-excel
|
||||
.xlw application/x-msexcel
|
||||
.xm audio/xm
|
||||
.xml application/xml
|
||||
.xml text/xml
|
||||
.xmz xgl/movie
|
||||
.xpix application/x-vnd.ls-xpix
|
||||
.xpm image/x-xpixmap
|
||||
.xpm image/xpm
|
||||
.x-png image/png
|
||||
.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
.xsr video/x-amt-showrun
|
||||
.xwd image/x-xwd
|
||||
.xwd image/x-xwindowdump
|
||||
.xyz chemical/x-pdb
|
||||
.yaml application/x-yaml
|
||||
.yml application/x-yaml
|
||||
.z application/x-compress
|
||||
.z application/x-compressed
|
||||
.zip application/x-compressed
|
||||
.zip application/x-zip-compressed
|
||||
.zip application/zip
|
||||
.zip multipart/x-zip
|
||||
.zoo application/octet-stream
|
||||
.zsh text/x-script.zsh
|
||||
};
|
||||
};
|
||||
}
|
||||
19
modules/home-manager/default.nix
Normal file
@@ -0,0 +1,19 @@
|
||||
{ username, desktop, stateVersion, osConfig, inputs, ... }: {
|
||||
home = {
|
||||
stateVersion = osConfig.system.stateVersion;
|
||||
inherit username;
|
||||
homeDirectory = "/home/${username}";
|
||||
};
|
||||
imports = [
|
||||
inputs.ags.homeManagerModules.default
|
||||
inputs.nixvim.homeManagerModules.nixvim
|
||||
inputs.anyrun.homeManagerModules.default
|
||||
./clean-home-dir.nix
|
||||
./programs/neovide.nix
|
||||
# ./default-apps.nix
|
||||
./packages
|
||||
./programs
|
||||
./services
|
||||
./desktops/hyprland
|
||||
];
|
||||
}
|
||||
77
modules/home-manager/desktops/hyprland/ags/.eslintrc.yml
Normal file
@@ -0,0 +1,77 @@
|
||||
env:
|
||||
es2021: true
|
||||
extends: eslint:recommended
|
||||
overrides: []
|
||||
parserOptions:
|
||||
ecmaVersion: latest
|
||||
sourceType: "module"
|
||||
rules:
|
||||
arrow-parens:
|
||||
- error
|
||||
- as-needed
|
||||
comma-dangle:
|
||||
- error
|
||||
- always-multiline
|
||||
comma-spacing:
|
||||
- error
|
||||
- before: false
|
||||
after: true
|
||||
comma-style:
|
||||
- error
|
||||
- last
|
||||
curly:
|
||||
- error
|
||||
- multi-or-nest
|
||||
- consistent
|
||||
dot-location:
|
||||
- error
|
||||
- property
|
||||
eol-last: error
|
||||
indent:
|
||||
- error
|
||||
- 4
|
||||
- SwitchCase: 1
|
||||
keyword-spacing:
|
||||
- error
|
||||
- before: true
|
||||
lines-between-class-members:
|
||||
- error
|
||||
- always
|
||||
- exceptAfterSingleLine: true
|
||||
padded-blocks:
|
||||
- error
|
||||
- never
|
||||
- allowSingleLineBlocks: false
|
||||
prefer-const: error
|
||||
quotes:
|
||||
- error
|
||||
- single
|
||||
- avoidEscape: true
|
||||
semi:
|
||||
- error
|
||||
- always
|
||||
nonblock-statement-body-position:
|
||||
- error
|
||||
- below
|
||||
no-trailing-spaces:
|
||||
- error
|
||||
array-bracket-spacing:
|
||||
- error
|
||||
- never
|
||||
key-spacing:
|
||||
- error
|
||||
- beforeColon: false
|
||||
afterColon: true
|
||||
object-curly-spacing:
|
||||
- error
|
||||
- always
|
||||
no-useless-escape:
|
||||
- off
|
||||
globals:
|
||||
pkg: readonly
|
||||
ags: readonly
|
||||
ARGV: readonly
|
||||
imports: readonly
|
||||
print: readonly
|
||||
console: readonly
|
||||
logError: readonly
|
||||
5
modules/home-manager/desktops/hyprland/ags/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
types
|
||||
package-lock.json
|
||||
weather_key
|
||||
setup.sh
|
||||
14
modules/home-manager/desktops/hyprland/ags/.stylelintrc.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
extends: stylelint-config-standard-scss
|
||||
ignoreFiles:
|
||||
- "**/*.js"
|
||||
- "**/*.ts"
|
||||
rules:
|
||||
selector-type-no-unknown: null
|
||||
declaration-empty-line-before: null
|
||||
no-descending-specificity: null
|
||||
selector-pseudo-class-no-unknown: null
|
||||
color-function-notation: legacy
|
||||
alpha-value-notation: number
|
||||
scss/operator-no-unspaced: null
|
||||
scss/no-global-function-names: null
|
||||
scss/dollar-variable-empty-line-before: null
|
||||
BIN
modules/home-manager/desktops/hyprland/ags/assets/aylur.jpg
Normal file
|
After Width: | Height: | Size: 428 KiB |
BIN
modules/home-manager/desktops/hyprland/ags/assets/ivory.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
modules/home-manager/desktops/hyprland/ags/assets/kitty.jpeg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
modules/home-manager/desktops/hyprland/ags/assets/kittybl.jpeg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
modules/home-manager/desktops/hyprland/ags/assets/leaves.jpg
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
modules/home-manager/desktops/hyprland/ags/assets/space.jpg
Normal file
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 3.9 MiB |
|
After Width: | Height: | Size: 3.9 MiB |
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 7.2 MiB |
|
After Width: | Height: | Size: 7.2 MiB |
|
After Width: | Height: | Size: 3.5 MiB |
|
After Width: | Height: | Size: 4.7 MiB |
2
modules/home-manager/desktops/hyprland/ags/config.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import { default as main } from "./js/main.js";
|
||||
export default main;
|
||||
96
modules/home-manager/desktops/hyprland/ags/js/about/about.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import PopupWindow from "../misc/PopupWindow.js";
|
||||
import icons from "../icons.js";
|
||||
|
||||
const pkg = JSON.parse(Utils.readFile(App.configDir + "/package.json"));
|
||||
const show = JSON.parse(
|
||||
Utils.readFile(Utils.CACHE_DIR + "/show_about") || "true",
|
||||
);
|
||||
const dontShow = () =>
|
||||
Utils.writeFile("false", Utils.CACHE_DIR + "/show_about");
|
||||
const avatar = App.configDir + "/assets/aylur.jpg";
|
||||
|
||||
/**
|
||||
* @param {Object} o
|
||||
* @param {string} o.label
|
||||
* @param {string} o.link
|
||||
*/
|
||||
const LinkButton = ({ label, link }) =>
|
||||
Widget.Button({
|
||||
on_clicked: () => Utils.execAsync(["xdg-open", link]),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Label({ label, hexpand: true, xalign: 0 }),
|
||||
Widget.Icon(icons.ui.link),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
export default () =>
|
||||
PopupWindow({
|
||||
name: "about",
|
||||
transition: "slide_down",
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
class_name: "window-content",
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "avatar",
|
||||
hpack: "center",
|
||||
css: `background-image: url('${avatar}');`,
|
||||
}),
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
class_name: "labels vertical",
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "title",
|
||||
label: pkg.description,
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "author",
|
||||
label: pkg.author,
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "version",
|
||||
hpack: "center",
|
||||
label: pkg.version,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "buttons",
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
vpack: "end",
|
||||
children: [
|
||||
LinkButton({
|
||||
label: "Support me on Ko-fi",
|
||||
link: pkg.kofi,
|
||||
}),
|
||||
LinkButton({
|
||||
label: "Report an Issue",
|
||||
link: pkg.bugs.url,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: "dont-show",
|
||||
on_clicked: () => {
|
||||
dontShow();
|
||||
App.toggleWindow("about");
|
||||
},
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Label("Don't show again"),
|
||||
Widget.Box({ hexpand: true }),
|
||||
Widget.Icon(icons.ui.close),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
export function showAbout(force = false) {
|
||||
if (show || force) App.toggleWindow("about");
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import options from "../options.js";
|
||||
|
||||
/** @param {import('resource:///com/github/Aylur/ags/service/applications.js').Application} app */
|
||||
export default (app) => {
|
||||
const title = Widget.Label({
|
||||
class_name: "title",
|
||||
label: app.name,
|
||||
xalign: 0,
|
||||
vpack: "center",
|
||||
truncate: "end",
|
||||
});
|
||||
|
||||
const description = Widget.Label({
|
||||
class_name: "description",
|
||||
label: app.description || "",
|
||||
wrap: true,
|
||||
xalign: 0,
|
||||
justification: "left",
|
||||
vpack: "center",
|
||||
});
|
||||
|
||||
const icon = Widget.Icon({
|
||||
icon: Utils.lookUpIcon(app.icon_name || "") ? app.icon_name || "" : "",
|
||||
size: options.applauncher.icon_size.bind("value"),
|
||||
});
|
||||
|
||||
const textBox = Widget.Box({
|
||||
vertical: true,
|
||||
vpack: "center",
|
||||
children: app.description ? [title, description] : [title],
|
||||
});
|
||||
|
||||
return Widget.Button({
|
||||
class_name: "app-item",
|
||||
attribute: app,
|
||||
child: Widget.Box({
|
||||
children: [icon, textBox],
|
||||
}),
|
||||
on_clicked: () => {
|
||||
App.closeWindow("applauncher");
|
||||
app.launch();
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
import Applications from "resource:///com/github/Aylur/ags/service/applications.js";
|
||||
import PopupWindow from "../misc/PopupWindow.js";
|
||||
import AppItem from "./AppItem.js";
|
||||
import icons from "../icons.js";
|
||||
import { launchApp } from "../utils.js";
|
||||
import options from "../options.js";
|
||||
|
||||
const WINDOW_NAME = "applauncher";
|
||||
|
||||
const Applauncher = () => {
|
||||
const mkItems = () => [
|
||||
Widget.Separator({ hexpand: true }),
|
||||
...Applications.query("").flatMap((app) =>
|
||||
Widget.Revealer({
|
||||
setup: (w) => (w.attribute = { app, revealer: w }),
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Separator({ hexpand: true }),
|
||||
AppItem(app),
|
||||
Widget.Separator({ hexpand: true }),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
),
|
||||
Widget.Separator({ hexpand: true }),
|
||||
];
|
||||
|
||||
let items = mkItems();
|
||||
|
||||
const list = Widget.Box({
|
||||
class_name: "app-list",
|
||||
vertical: true,
|
||||
children: items,
|
||||
});
|
||||
|
||||
const entry = Widget.Entry({
|
||||
hexpand: true,
|
||||
primary_icon_name: icons.apps.search,
|
||||
|
||||
// set some text so on-change works the first time
|
||||
text: "-",
|
||||
on_accept: ({ text }) => {
|
||||
const list = Applications.query(text || "");
|
||||
if (list[0]) {
|
||||
App.toggleWindow(WINDOW_NAME);
|
||||
launchApp(list[0]);
|
||||
}
|
||||
},
|
||||
on_change: ({ text }) =>
|
||||
items.map((item) => {
|
||||
if (item.attribute) {
|
||||
const { app, revealer } = item.attribute;
|
||||
revealer.reveal_child = app.match(text);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
return Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
entry,
|
||||
Widget.Scrollable({
|
||||
hscroll: "never",
|
||||
child: list,
|
||||
}),
|
||||
],
|
||||
setup: (self) =>
|
||||
self.hook(App, (_, win, visible) => {
|
||||
if (win !== WINDOW_NAME) return;
|
||||
|
||||
entry.text = "-";
|
||||
entry.text = "";
|
||||
if (visible) {
|
||||
entry.grab_focus();
|
||||
} else {
|
||||
items = mkItems();
|
||||
list.children = items;
|
||||
}
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
export default () =>
|
||||
PopupWindow({
|
||||
name: WINDOW_NAME,
|
||||
transition: "slide_down",
|
||||
child: Applauncher(),
|
||||
anchor: options.applauncher.anchor.bind("value"),
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @typedef {Object} PanelButtonProps
|
||||
* @property {import('types/widgets/button').ButtonProps['child']} content
|
||||
* @property {string=} window
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {import('types/widgets/button').ButtonProps & PanelButtonProps} o
|
||||
*/
|
||||
export default ({ class_name, content, window = "", setup, ...rest }) =>
|
||||
Widget.Button({
|
||||
class_name: `panel-button ${class_name}`,
|
||||
child: Widget.Box({ children: [content] }),
|
||||
setup: (self) => {
|
||||
let open = false;
|
||||
|
||||
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,
|
||||
});
|
||||
113
modules/home-manager/desktops/hyprland/ags/js/bar/TopBar.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import SystemTray from "resource:///com/github/Aylur/ags/service/systemtray.js";
|
||||
import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
|
||||
import Mpris from "resource:///com/github/Aylur/ags/service/mpris.js";
|
||||
import Battery from "resource:///com/github/Aylur/ags/service/battery.js";
|
||||
import OverviewButton from "./buttons/OverviewButton.js";
|
||||
import Workspaces from "./buttons/Workspaces.js";
|
||||
import FocusedClient from "./buttons/FocusedClient.js";
|
||||
import MediaIndicator from "./buttons/MediaIndicator.js";
|
||||
import DateButton from "./buttons/DateButton.js";
|
||||
import NotificationIndicator from "./buttons/NotificationIndicator.js";
|
||||
import SysTray from "./buttons/SysTray.js";
|
||||
import ColorPicker from "./buttons/ColorPicker.js";
|
||||
import SystemIndicators from "./buttons/SystemIndicators.js";
|
||||
import PowerMenu from "./buttons/PowerMenu.js";
|
||||
import ScreenRecord from "./buttons/ScreenRecord.js";
|
||||
import BatteryBar from "./buttons/BatteryBar.js";
|
||||
import SubMenu from "./buttons/SubMenu.js";
|
||||
import Recorder from "../services/screenrecord.js";
|
||||
// import * as System from './buttons/System.js';
|
||||
// import Taskbar from './buttons/Taskbar.js';
|
||||
import options from "../options.js";
|
||||
|
||||
const submenuItems = Variable(1);
|
||||
SystemTray.connect("changed", () => {
|
||||
submenuItems.setValue(SystemTray.items.length + 1);
|
||||
});
|
||||
|
||||
/**
|
||||
* @template {import('types/service').default} T
|
||||
* @param {T=} service
|
||||
* @param {(service: T) => boolean=} condition
|
||||
*/
|
||||
const SeparatorDot = (service, condition) =>
|
||||
Widget.Separator({
|
||||
vpack: "center",
|
||||
setup: (self) => {
|
||||
const visibility = () => {
|
||||
if (!options.bar.separators.value) return (self.visible = false);
|
||||
|
||||
self.visible =
|
||||
condition && service
|
||||
? condition(service)
|
||||
: options.bar.separators.value;
|
||||
};
|
||||
|
||||
if (service && condition) self.hook(service, visibility);
|
||||
|
||||
self.on("draw", visibility);
|
||||
self.bind("visible", options.bar.separators);
|
||||
},
|
||||
});
|
||||
|
||||
const Start = () =>
|
||||
Widget.Box({
|
||||
class_name: "start",
|
||||
children: [
|
||||
OverviewButton(),
|
||||
SeparatorDot(),
|
||||
Workspaces(),
|
||||
SeparatorDot(),
|
||||
FocusedClient(),
|
||||
Widget.Box({ hexpand: true }),
|
||||
NotificationIndicator(),
|
||||
SeparatorDot(Notifications, (n) => n.notifications.length > 0 || n.dnd),
|
||||
],
|
||||
});
|
||||
|
||||
const Center = () =>
|
||||
Widget.Box({
|
||||
class_name: "center",
|
||||
children: [DateButton()],
|
||||
});
|
||||
|
||||
const End = () =>
|
||||
Widget.Box({
|
||||
class_name: "end",
|
||||
children: [
|
||||
SeparatorDot(Mpris, (m) => m.players.length > 0),
|
||||
MediaIndicator(),
|
||||
Widget.Box({ hexpand: true }),
|
||||
|
||||
SubMenu({
|
||||
items: submenuItems,
|
||||
children: [SysTray(), ColorPicker()],
|
||||
}),
|
||||
|
||||
SeparatorDot(),
|
||||
ScreenRecord(),
|
||||
SeparatorDot(Recorder, (r) => r.recording),
|
||||
SystemIndicators(),
|
||||
SeparatorDot(Battery, (b) => b.available),
|
||||
SeparatorDot(),
|
||||
PowerMenu(),
|
||||
],
|
||||
});
|
||||
|
||||
/** @param {number} monitor */
|
||||
export default (monitor) =>
|
||||
Widget.Window({
|
||||
name: `bar${monitor}`,
|
||||
class_name: "transparent",
|
||||
exclusivity: "exclusive",
|
||||
monitor,
|
||||
anchor: options.bar.position
|
||||
.bind("value")
|
||||
.transform((pos) => [pos, "left", "right"]),
|
||||
child: Widget.CenterBox({
|
||||
class_name: "panel",
|
||||
start_widget: Start(),
|
||||
center_widget: Center(),
|
||||
end_widget: End(),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Battery from "resource:///com/github/Aylur/ags/service/battery.js";
|
||||
import icons from "../../icons.js";
|
||||
import FontIcon from "../../misc/FontIcon.js";
|
||||
import options from "../../options.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
|
||||
const Indicator = () =>
|
||||
Widget.Stack({
|
||||
children: {
|
||||
false: Widget.Icon({ icon: Battery.bind("icon_name") }),
|
||||
true: FontIcon(icons.battery.charging),
|
||||
},
|
||||
visible: options.battery.bar.show_icon.bind("value"),
|
||||
setup: (self) =>
|
||||
self.hook(Battery, () => {
|
||||
self.shown = `${Battery.charging || Battery.charged}`;
|
||||
}),
|
||||
});
|
||||
|
||||
const PercentLabel = () =>
|
||||
Widget.Revealer({
|
||||
transition: "slide_right",
|
||||
reveal_child: options.battery.show_percentage.bind("value"),
|
||||
child: Widget.Label({
|
||||
label: Battery.bind("percent").transform((p) => `${p}%`),
|
||||
}),
|
||||
});
|
||||
|
||||
const LevelBar = () =>
|
||||
Widget.LevelBar({
|
||||
value: Battery.bind("percent").transform((p) => p / 100),
|
||||
setup: (self) =>
|
||||
self.hook(options.battery.bar.full, () => {
|
||||
const full = options.battery.bar.full.value;
|
||||
self.vpack = full ? "fill" : "center";
|
||||
self.hpack = full ? "fill" : "center";
|
||||
}),
|
||||
});
|
||||
|
||||
const WholeButton = () =>
|
||||
Widget.Overlay({
|
||||
class_name: "whole-button",
|
||||
child: LevelBar(),
|
||||
pass_through: true,
|
||||
overlays: [
|
||||
Widget.Box({
|
||||
hpack: "center",
|
||||
children: [
|
||||
FontIcon({
|
||||
icon: icons.battery.charging,
|
||||
visible: Battery.bind("charging"),
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: "center",
|
||||
vpack: "center",
|
||||
child: PercentLabel(),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export default () =>
|
||||
PanelButton({
|
||||
class_name: "battery-bar",
|
||||
on_clicked: () => {
|
||||
const v = options.battery.show_percentage.value;
|
||||
options.battery.show_percentage.value = !v;
|
||||
},
|
||||
content: Widget.Box({
|
||||
visible: Battery.bind("available"),
|
||||
children: options.battery.bar.full
|
||||
.bind("value")
|
||||
.transform((full) =>
|
||||
full ? [WholeButton()] : [Indicator(), PercentLabel(), LevelBar()],
|
||||
),
|
||||
setup: (self) =>
|
||||
self.hook(Battery, (w) => {
|
||||
w.toggleClassName("charging", Battery.charging || Battery.charged);
|
||||
w.toggleClassName(
|
||||
"medium",
|
||||
Battery.percent < options.battery.medium.value,
|
||||
);
|
||||
w.toggleClassName("low", Battery.percent < options.battery.low.value);
|
||||
w.toggleClassName("half", Battery.percent < 48);
|
||||
}),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Colors from "../../services/colorpicker.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
import Gdk from "gi://Gdk";
|
||||
|
||||
export default () =>
|
||||
PanelButton({
|
||||
class_name: "color-picker",
|
||||
content: Widget.Icon("color-select-symbolic"),
|
||||
tooltip_text: Colors.bind("colors").transform((v) => `${v.length} colors`),
|
||||
on_clicked: () => Colors.pick(),
|
||||
on_secondary_click: (btn) => {
|
||||
if (Colors.colors.length === 0) return;
|
||||
|
||||
Widget.Menu({
|
||||
class_name: "colorpicker",
|
||||
children: Colors.colors.map((color) =>
|
||||
Widget.MenuItem({
|
||||
child: Widget.Label(color),
|
||||
css: `background-color: ${color}`,
|
||||
on_activate: () => Colors.wlCopy(color),
|
||||
}),
|
||||
),
|
||||
}).popup_at_widget(btn, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, null);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Clock from "../../misc/Clock.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
|
||||
export default ({ format = "%R - %x" } = {}) =>
|
||||
PanelButton({
|
||||
class_name: "dashboard panel-button",
|
||||
on_clicked: () => App.toggleWindow("dashboard"),
|
||||
window: "dashboard",
|
||||
content: Clock({ format }),
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
import options from "../../options.js";
|
||||
import { substitute } from "../../utils.js";
|
||||
|
||||
export const ClientLabel = () =>
|
||||
Widget.Label({
|
||||
label: Hyprland.active.client.bind("class").transform((c) => {
|
||||
const { titles } = options.substitutions;
|
||||
return substitute(titles, c);
|
||||
}),
|
||||
});
|
||||
|
||||
export const ClientIcon = () =>
|
||||
Widget.Icon({
|
||||
setup: (self) =>
|
||||
self.hook(Hyprland.active.client, () => {
|
||||
const { icons } = options.substitutions;
|
||||
const { client } = Hyprland.active;
|
||||
|
||||
const classIcon = substitute(icons, client.class) + "-symbolic";
|
||||
const titleIcon = substitute(icons, client.class) + "-symbolic";
|
||||
|
||||
const hasTitleIcon = Utils.lookUpIcon(titleIcon);
|
||||
const hasClassIcon = Utils.lookUpIcon(classIcon);
|
||||
|
||||
if (hasClassIcon) self.icon = classIcon;
|
||||
|
||||
if (hasTitleIcon) self.icon = titleIcon;
|
||||
|
||||
self.visible = !!(hasTitleIcon || hasClassIcon);
|
||||
}),
|
||||
});
|
||||
|
||||
export default () =>
|
||||
PanelButton({
|
||||
class_name: "focused-client",
|
||||
content: Widget.Box({
|
||||
tooltip_text: Hyprland.active.bind("client").transform((c) => c.title),
|
||||
children: [ClientIcon(), ClientLabel()],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Mpris from "resource:///com/github/Aylur/ags/service/mpris.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import HoverRevealer from "../../misc/HoverRevealer.js";
|
||||
import * as mpris from "../../misc/mpris.js";
|
||||
import options from "../../options.js";
|
||||
|
||||
export const getPlayer = (name = options.mpris.preferred.value) =>
|
||||
Mpris.getPlayer(name) || Mpris.players[0] || null;
|
||||
|
||||
/**
|
||||
* @param {Object} o
|
||||
* @param {import('types/service/mpris').MprisPlayer} o.player
|
||||
* @param {import('../../misc/HoverRevealer').HoverRevealProps['direction']=} o.direction
|
||||
*/
|
||||
const Indicator = ({ player, direction = "right" }) =>
|
||||
HoverRevealer({
|
||||
class_name: `media panel-button ${player.name}`,
|
||||
direction,
|
||||
on_primary_click: () => player.playPause(),
|
||||
on_scroll_up: () => player.next(),
|
||||
on_scroll_down: () => player.previous(),
|
||||
on_secondary_click: () => player.playPause(),
|
||||
indicator: mpris.PlayerIcon(player),
|
||||
child: Widget.Label({
|
||||
vexpand: true,
|
||||
truncate: "end",
|
||||
max_width_chars: 40,
|
||||
label: player
|
||||
.bind("track_title")
|
||||
.transform(
|
||||
() => `${player.track_artists.join(", ")} - ${player.track_title}`,
|
||||
),
|
||||
}),
|
||||
setupRevealer: (self) => {
|
||||
let current = "";
|
||||
self.hook(player, () => {
|
||||
if (current === player.track_title) return;
|
||||
|
||||
current = player.track_title;
|
||||
self.reveal_child = true;
|
||||
Utils.timeout(3000, () => {
|
||||
self.reveal_child = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {Object} o
|
||||
* @param {import('../../misc/HoverRevealer').HoverRevealProps['direction']=} o.direction
|
||||
*/
|
||||
export default ({ direction = "right" } = {}) => {
|
||||
let current = null;
|
||||
|
||||
const update = (box) => {
|
||||
const player = getPlayer();
|
||||
box.visible = !!player;
|
||||
|
||||
if (!player) {
|
||||
current = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (current === player) return;
|
||||
|
||||
current = player;
|
||||
box.children = [Indicator({ player, direction })];
|
||||
};
|
||||
|
||||
return Widget.Box()
|
||||
.hook(options.mpris.preferred, update)
|
||||
.hook(Mpris, update, "notify::players");
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import icons from "../../icons.js";
|
||||
import HoverRevealer from "../../misc/HoverRevealer.js";
|
||||
|
||||
/**
|
||||
* @param {Object} o
|
||||
* @param {import('../../misc/HoverRevealer').HoverRevealProps['direction']=} o.direction
|
||||
*/
|
||||
export default ({ direction = "left" } = {}) =>
|
||||
HoverRevealer({
|
||||
class_name: "notifications panel-button",
|
||||
setupEventBox: (box) =>
|
||||
box
|
||||
.on("button-press-event", () => App.openWindow("dashboard"))
|
||||
.hook(
|
||||
Notifications,
|
||||
() =>
|
||||
(box.visible =
|
||||
Notifications.notifications.length > 0 || Notifications.dnd),
|
||||
),
|
||||
|
||||
setupRevealer: (self) =>
|
||||
self.hook(Notifications, () => {
|
||||
let title = "";
|
||||
const summary = Notifications.notifications[0]?.summary;
|
||||
if (title === summary) return;
|
||||
|
||||
title = summary;
|
||||
self.reveal_child = true;
|
||||
Utils.timeout(3000, () => {
|
||||
self.reveal_child = false;
|
||||
});
|
||||
}),
|
||||
direction,
|
||||
indicator: Widget.Icon({
|
||||
icon: Notifications.bind("dnd").transform(
|
||||
(dnd) => icons.notifications[dnd ? "silent" : "noisy"],
|
||||
),
|
||||
}),
|
||||
child: Widget.Label({
|
||||
truncate: "end",
|
||||
max_width_chars: 40,
|
||||
label: Notifications.bind("notifications").transform(
|
||||
(n) => n.reverse()[0]?.summary || "",
|
||||
),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
import FontIcon from "../../misc/FontIcon.js";
|
||||
import { distroIcon } from "../../variables.js";
|
||||
import options from "../../options.js";
|
||||
|
||||
export default () =>
|
||||
PanelButton({
|
||||
class_name: "overview",
|
||||
window: "overview",
|
||||
on_clicked: () => App.toggleWindow("overview"),
|
||||
content: FontIcon({
|
||||
label: options.bar.icon.bind("value").transform((v) => {
|
||||
return v === "distro-icon" ? distroIcon : v;
|
||||
}),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import icons from "../../icons.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
|
||||
export default () =>
|
||||
PanelButton({
|
||||
class_name: "powermenu",
|
||||
content: Widget.Icon(icons.powermenu.shutdown),
|
||||
on_clicked: () => App.openWindow("powermenu"),
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
import Recorder from "../../services/screenrecord.js";
|
||||
import icons from "../../icons.js";
|
||||
|
||||
export default () =>
|
||||
PanelButton({
|
||||
class_name: "recorder",
|
||||
on_clicked: () => Recorder.stop(),
|
||||
visible: Recorder.bind("recording"),
|
||||
content: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon(icons.recorder.recording),
|
||||
Widget.Label({
|
||||
label: Recorder.bind("timer").transform((time) => {
|
||||
const sec = time % 60;
|
||||
const min = Math.floor(time / 60);
|
||||
return `${min}:${sec < 10 ? "0" + sec : sec}`;
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Variable from "resource:///com/github/Aylur/ags/variable.js";
|
||||
import icons from "../../icons.js";
|
||||
import options from "../../options.js";
|
||||
|
||||
/**
|
||||
* @param {import('types/widgets/revealer').default} revealer
|
||||
* @param {'left' | 'right' | 'up' | 'down'} direction
|
||||
* @param {import('types/variable').Variable<number>} items
|
||||
*/
|
||||
const Arrow = (revealer, direction, items) => {
|
||||
let deg = 0;
|
||||
|
||||
const icon = Widget.Icon({
|
||||
icon: icons.ui.arrow[direction],
|
||||
});
|
||||
|
||||
const animate = () => {
|
||||
const t = options.transition.value / 20;
|
||||
const step = revealer.reveal_child ? 10 : -10;
|
||||
for (let i = 0; i < 18; ++i) {
|
||||
Utils.timeout(t * i, () => {
|
||||
deg += step;
|
||||
icon.setCss(`-gtk-icon-transform: rotate(${deg}deg);`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return Widget.Button({
|
||||
class_name: "panel-button sub-menu",
|
||||
tooltip_text: items.bind().transform((v) => `${v} Items`),
|
||||
on_clicked: () => {
|
||||
animate();
|
||||
revealer.reveal_child = !revealer.reveal_child;
|
||||
},
|
||||
child: icon,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} o
|
||||
* @param {import('types/widgets/box').default['children']} o.children
|
||||
* @param {'left' | 'right' | 'up' | 'down'=} o.direction
|
||||
* @param {import('types/variable').Variable} o.items
|
||||
*/
|
||||
export default ({ children, direction = "left", items = Variable(0) }) => {
|
||||
const posStart = direction === "up" || direction === "left";
|
||||
const posEnd = direction === "down" || direction === "right";
|
||||
const revealer = Widget.Revealer({
|
||||
transition: `slide_${direction}`,
|
||||
child: Widget.Box({
|
||||
children,
|
||||
}),
|
||||
});
|
||||
|
||||
return Widget.Box({
|
||||
vertical: direction === "up" || direction === "down",
|
||||
children: [
|
||||
posStart && revealer,
|
||||
Arrow(revealer, direction, items),
|
||||
posEnd && revealer,
|
||||
],
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import SystemTray from "resource:///com/github/Aylur/ags/service/systemtray.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
import Gdk from "gi://Gdk";
|
||||
|
||||
/** @param {import('types/service/systemtray').TrayItem} item */
|
||||
const SysTrayItem = (item) =>
|
||||
PanelButton({
|
||||
class_name: "tray-item",
|
||||
content: Widget.Icon({ icon: item.bind("icon") }),
|
||||
tooltip_markup: item.bind("tooltip_markup"),
|
||||
setup: (self) => {
|
||||
const id = item.menu?.connect("popped-up", (menu) => {
|
||||
self.toggleClassName("active");
|
||||
menu.connect("notify::visible", (menu) => {
|
||||
self.toggleClassName("active", menu.visible);
|
||||
});
|
||||
menu.disconnect(id);
|
||||
});
|
||||
|
||||
if (id) self.connect("destroy", () => item.menu?.disconnect(id));
|
||||
},
|
||||
|
||||
// @ts-expect-error popup_at_widget missing from types?
|
||||
on_primary_click: (btn) =>
|
||||
item.menu?.popup_at_widget(
|
||||
btn,
|
||||
Gdk.Gravity.SOUTH,
|
||||
Gdk.Gravity.NORTH,
|
||||
null,
|
||||
),
|
||||
|
||||
// @ts-expect-error popup_at_widget missing from types?
|
||||
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.map(SysTrayItem));
|
||||
@@ -0,0 +1,50 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
import * as variables from "../../variables.js";
|
||||
import icons from "../../icons.js";
|
||||
|
||||
/** @param {'cpu' | 'ram'} type */
|
||||
const System = (type) => {
|
||||
const icon = Widget.Icon({
|
||||
class_name: "icon",
|
||||
icon: icons.system[type],
|
||||
});
|
||||
|
||||
const progress = Widget.Box({
|
||||
class_name: "progress",
|
||||
child: Widget.CircularProgress({
|
||||
value: variables[type].bind(),
|
||||
}),
|
||||
});
|
||||
|
||||
const revealer = Widget.Revealer({
|
||||
transition: "slide_right",
|
||||
child: Widget.Label({
|
||||
label: variables[type].bind("value").transform((v) => {
|
||||
return ` ${type}: ${Math.round(v * 100)}%`;
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
return PanelButton({
|
||||
class_name: `system ${type}`,
|
||||
on_clicked: () => (revealer.reveal_child = !revealer.reveal_child),
|
||||
content: Widget.EventBox({
|
||||
on_hover: () => (revealer.reveal_child = true),
|
||||
on_hover_lost: () => (revealer.reveal_child = false),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
icon,
|
||||
Widget.Box({
|
||||
class_name: "revealer",
|
||||
child: revealer,
|
||||
}),
|
||||
progress,
|
||||
],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
export const CPU = () => System("cpu");
|
||||
export const RAM = () => System("ram");
|
||||
@@ -0,0 +1,134 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
|
||||
import Bluetooth from "resource:///com/github/Aylur/ags/service/bluetooth.js";
|
||||
import Audio from "resource:///com/github/Aylur/ags/service/audio.js";
|
||||
import Network from "resource:///com/github/Aylur/ags/service/network.js";
|
||||
import HoverRevealer from "../../misc/HoverRevealer.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
import Asusctl from "../../services/asusctl.js";
|
||||
import Indicator from "../../services/onScreenIndicator.js";
|
||||
import icons from "../../icons.js";
|
||||
import FontIcon from "../../misc/FontIcon.js";
|
||||
|
||||
const ProfileIndicator = () =>
|
||||
Widget.Icon()
|
||||
.bind("visible", Asusctl, "profile", (p) => p !== "Balanced")
|
||||
.bind("icon", Asusctl, "profile", (i) => icons.asusctl.profile[i]);
|
||||
|
||||
const ModeIndicator = () =>
|
||||
FontIcon()
|
||||
.bind("visible", Asusctl, "mode", (m) => m !== "Hybrid")
|
||||
.bind("icon", Asusctl, "mode", (i) => icons.asusctl.mode[i]);
|
||||
|
||||
const MicrophoneIndicator = () =>
|
||||
Widget.Icon().hook(
|
||||
Audio,
|
||||
(icon) => {
|
||||
if (!Audio.microphone) return;
|
||||
|
||||
const { muted, low, medium, high } = icons.audio.mic;
|
||||
|
||||
/** @type {Array<[number, string]>} */
|
||||
const cons = [
|
||||
[67, high],
|
||||
[34, medium],
|
||||
[1, low],
|
||||
[0, muted],
|
||||
];
|
||||
icon.icon =
|
||||
cons.find(([n]) => n <= Audio.microphone.volume * 100)?.[1] || "";
|
||||
|
||||
icon.visible = Audio.recorders.length > 0 || Audio.microphone.is_muted;
|
||||
},
|
||||
"microphone-changed",
|
||||
);
|
||||
|
||||
const DNDIndicator = () =>
|
||||
Widget.Icon({
|
||||
visible: Notifications.bind("dnd"),
|
||||
icon: icons.notifications.silent,
|
||||
});
|
||||
|
||||
const BluetoothDevicesIndicator = () =>
|
||||
Widget.Box().hook(
|
||||
Bluetooth,
|
||||
(box) => {
|
||||
box.children = Bluetooth.connectedDevices.map(({ iconName, name }) =>
|
||||
HoverRevealer({
|
||||
indicator: Widget.Icon(iconName + "-symbolic"),
|
||||
child: Widget.Label(name),
|
||||
}),
|
||||
);
|
||||
|
||||
box.visible = Bluetooth.connectedDevices.length > 0;
|
||||
},
|
||||
"notify::connected-devices",
|
||||
);
|
||||
|
||||
const BluetoothIndicator = () =>
|
||||
Widget.Icon({
|
||||
class_name: "bluetooth",
|
||||
icon: icons.bluetooth.enabled,
|
||||
visible: Bluetooth.bind("enabled"),
|
||||
});
|
||||
|
||||
const NetworkIndicator = () =>
|
||||
Widget.Icon().hook(Network, (self) => {
|
||||
const icon = Network[Network.primary || "wifi"]?.iconName;
|
||||
self.icon = icon || "";
|
||||
self.visible = !!icon;
|
||||
});
|
||||
|
||||
const AudioIndicator = () =>
|
||||
Widget.Icon().hook(
|
||||
Audio,
|
||||
(self) => {
|
||||
if (!Audio.speaker) return;
|
||||
|
||||
const { muted, low, medium, high, overamplified } = icons.audio.volume;
|
||||
if (Audio.speaker.is_muted) return (self.icon = muted);
|
||||
|
||||
/** @type {Array<[number, string]>} */
|
||||
const cons = [
|
||||
[101, overamplified],
|
||||
[67, high],
|
||||
[34, medium],
|
||||
[1, low],
|
||||
[0, muted],
|
||||
];
|
||||
self.icon =
|
||||
cons.find(([n]) => n <= Audio.speaker.volume * 100)?.[1] || "";
|
||||
},
|
||||
"speaker-changed",
|
||||
);
|
||||
|
||||
export default () =>
|
||||
PanelButton({
|
||||
class_name: "quicksettings panel-button",
|
||||
on_clicked: () => App.toggleWindow("quicksettings"),
|
||||
setup: (self) =>
|
||||
self.hook(App, (_, win, visible) => {
|
||||
self.toggleClassName("active", win === "quicksettings" && visible);
|
||||
}),
|
||||
on_scroll_up: () => {
|
||||
Audio.speaker.volume += 0.02;
|
||||
Indicator.speaker();
|
||||
},
|
||||
on_scroll_down: () => {
|
||||
Audio.speaker.volume -= 0.02;
|
||||
Indicator.speaker();
|
||||
},
|
||||
content: Widget.Box({
|
||||
children: [
|
||||
Asusctl?.available && ProfileIndicator(),
|
||||
Asusctl?.available && ModeIndicator(),
|
||||
DNDIndicator(),
|
||||
BluetoothDevicesIndicator(),
|
||||
BluetoothIndicator(),
|
||||
NetworkIndicator(),
|
||||
AudioIndicator(),
|
||||
MicrophoneIndicator(),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import Applications from "resource:///com/github/Aylur/ags/service/applications.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import PanelButton from "../PanelButton.js";
|
||||
import { launchApp } from "../../utils.js";
|
||||
import icons from "../../icons.js";
|
||||
|
||||
const focus = ({ address }) =>
|
||||
Hyprland.sendMessage(`dispatch focuswindow address:${address}`);
|
||||
|
||||
/** @param {import('types/widgets/box').default} box */
|
||||
const setChildren = (box) =>
|
||||
(box.children = Hyprland.clients.map((client) => {
|
||||
if (Hyprland.active.workspace.id !== client.workspace.id) return;
|
||||
|
||||
for (const app of Applications.list) {
|
||||
if (client.class && app.match(client.class)) {
|
||||
return PanelButton({
|
||||
content: Widget.Icon(app.icon_name || icons.fallback.executable),
|
||||
tooltip_text: app.name,
|
||||
on_primary_click: () => focus(client),
|
||||
on_middle_click: () => launchApp(app),
|
||||
});
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
export default () =>
|
||||
Widget.Box()
|
||||
.hook(Hyprland, setChildren, "notify::clients")
|
||||
.hook(Hyprland, setChildren, "notify::active");
|
||||
@@ -0,0 +1,58 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import options from "../../options.js";
|
||||
import { range } from "../../utils.js";
|
||||
|
||||
/** @param {any} arg */
|
||||
const dispatch = (arg) => Utils.execAsync(`hyprctl dispatch workspace ${arg}`);
|
||||
|
||||
const Workspaces = () => {
|
||||
const ws = options.workspaces.value;
|
||||
return Widget.Box({
|
||||
children: range(ws || 20).map((i) =>
|
||||
Widget.Button({
|
||||
attribute: i,
|
||||
on_clicked: () => dispatch(i),
|
||||
child: Widget.Label({
|
||||
label: `${i}`,
|
||||
class_name: "indicator",
|
||||
vpack: "center",
|
||||
}),
|
||||
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 () =>
|
||||
Widget.EventBox({
|
||||
class_name: "workspaces panel-button",
|
||||
child: Widget.Box({
|
||||
// its nested like this to keep it consistent with other PanelButton widgets
|
||||
child: Widget.EventBox({
|
||||
on_scroll_up: () => dispatch("m+1"),
|
||||
on_scroll_down: () => dispatch("m-1"),
|
||||
class_name: "eventbox",
|
||||
child: options.workspaces.bind("value").transform(Workspaces),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import DateColumn from "./DateColumn.js";
|
||||
import NotificationColumn from "./NotificationColumn.js";
|
||||
import PopupWindow from "../misc/PopupWindow.js";
|
||||
import options from "../options.js";
|
||||
|
||||
export default () =>
|
||||
PopupWindow({
|
||||
name: "dashboard",
|
||||
setup: (self) =>
|
||||
self.hook(options.bar.position, () => {
|
||||
self.anchor = [options.bar.position.value];
|
||||
if (options.bar.position.value === "top")
|
||||
self.transition = "slide_down";
|
||||
|
||||
if (options.bar.position.value === "bottom")
|
||||
self.transition = "slide_up";
|
||||
}),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
NotificationColumn(),
|
||||
Widget.Separator({ orientation: 1 }),
|
||||
DateColumn(),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import icons from "../icons.js";
|
||||
import Clock from "../misc/Clock.js";
|
||||
import * as vars from "../variables.js";
|
||||
import options from "../options.js";
|
||||
|
||||
/**
|
||||
* @param {'cpu' | 'ram' | 'temp'} type
|
||||
* @param {string} title
|
||||
* @param {string} unit
|
||||
*/
|
||||
const SysProgress = (type, title, unit) =>
|
||||
Widget.Box({
|
||||
class_name: `circular-progress-box ${type}`,
|
||||
hexpand: true,
|
||||
tooltip_text: vars[type]
|
||||
.bind("value")
|
||||
.transform((v) => `${title}: ${Math.floor(v * 100)}${unit}`),
|
||||
child: Widget.CircularProgress({
|
||||
hexpand: true,
|
||||
class_name: `circular-progress ${type}`,
|
||||
child: Widget.Icon(icons.system[type]),
|
||||
start_at: 0.75,
|
||||
value: vars[type].bind(),
|
||||
rounded: options.radii.bind("value").transform((v) => v > 0),
|
||||
}),
|
||||
});
|
||||
|
||||
export default () =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
class_name: "datemenu vertical",
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "clock-box",
|
||||
vertical: true,
|
||||
children: [
|
||||
Clock({ format: "%H:%M" }),
|
||||
Widget.Label({
|
||||
class_name: "uptime",
|
||||
label: vars.uptime.bind("value").transform((t) => `uptime: ${t}`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "calendar",
|
||||
children: [
|
||||
Widget.Calendar({
|
||||
hexpand: true,
|
||||
hpack: "center",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "system-info horizontal",
|
||||
children: [
|
||||
SysProgress("cpu", "Cpu", "%"),
|
||||
SysProgress("ram", "Ram", "%"),
|
||||
SysProgress("temp", "Temperature", "°"),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
|
||||
import icons from "../icons.js";
|
||||
import Notification from "../misc/Notification.js";
|
||||
import { timeout } from "resource:///com/github/Aylur/ags/utils.js";
|
||||
|
||||
const ClearButton = () =>
|
||||
Widget.Button({
|
||||
on_clicked: () => {
|
||||
const list = Array.from(Notifications.notifications);
|
||||
for (let i = 0; i < list.length; i++)
|
||||
timeout(50 * i, () => list[i]?.close());
|
||||
},
|
||||
sensitive: Notifications.bind("notifications").transform(
|
||||
(n) => n.length > 0,
|
||||
),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Label("Clear "),
|
||||
Widget.Icon({
|
||||
icon: Notifications.bind("notifications").transform(
|
||||
(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 = () =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
children: Notifications.bind("notifications").transform((n) =>
|
||||
n.reverse().map(Notification),
|
||||
),
|
||||
visible: Notifications.bind("notifications").transform((n) => n.length > 0),
|
||||
});
|
||||
|
||||
const Placeholder = () =>
|
||||
Widget.Box({
|
||||
class_name: "placeholder",
|
||||
vertical: true,
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
vexpand: true,
|
||||
hexpand: true,
|
||||
visible: Notifications.bind("notifications").transform(
|
||||
(n) => n.length === 0,
|
||||
),
|
||||
children: [
|
||||
Widget.Icon(icons.notifications.silent),
|
||||
Widget.Label("Your inbox is empty"),
|
||||
],
|
||||
});
|
||||
|
||||
export default () =>
|
||||
Widget.Box({
|
||||
class_name: "notifications",
|
||||
vertical: true,
|
||||
children: [
|
||||
Header(),
|
||||
Widget.Scrollable({
|
||||
vexpand: true,
|
||||
class_name: "notification-scrollable",
|
||||
child: Widget.Box({
|
||||
class_name: "notification-list",
|
||||
vertical: true,
|
||||
children: [NotificationList(), Placeholder()],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Clock from "../misc/Clock.js";
|
||||
import DesktopMenu from "./DesktopMenu.js";
|
||||
import options from "../options.js";
|
||||
|
||||
const DesktopClock = () =>
|
||||
Widget.Box({
|
||||
class_name: "clock-box-shadow",
|
||||
child: Widget.CenterBox({
|
||||
class_name: "clock-box",
|
||||
start_widget: Clock({
|
||||
class_name: "clock",
|
||||
hpack: "center",
|
||||
format: "%H",
|
||||
}),
|
||||
center_widget: Widget.Box({
|
||||
class_name: "separator-box",
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
hpack: "center",
|
||||
children: [
|
||||
Widget.Separator({ vpack: "center", vexpand: true }),
|
||||
Widget.Separator({ vpack: "center", vexpand: true }),
|
||||
],
|
||||
}),
|
||||
end_widget: Clock({
|
||||
class_name: "clock",
|
||||
hpack: "center",
|
||||
format: "%M",
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
const Desktop = () =>
|
||||
Widget.EventBox({
|
||||
on_secondary_click: (_, event) => DesktopMenu().popup_at_pointer(event),
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
hexpand: true,
|
||||
visible: options.desktop.clock.enable.bind("value"),
|
||||
setup: (self) =>
|
||||
self.hook(options.desktop.clock.position, () => {
|
||||
const [hpack = "center", vpack = "center", offset = 64] =
|
||||
options.desktop.clock.position.value.split(" ") || [];
|
||||
|
||||
// @ts-expect-error
|
||||
self.hpack = hpack;
|
||||
self.vpack = vpack;
|
||||
self.setCss(`margin: ${Number(offset)}px;`);
|
||||
}),
|
||||
children: [
|
||||
DesktopClock(),
|
||||
Clock({ format: "%B %e. %A", class_name: "date" }),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
/** @param {number} monitor */
|
||||
export default (monitor) =>
|
||||
Widget.Window({
|
||||
monitor,
|
||||
keymode: "on-demand",
|
||||
name: `desktop${monitor}`,
|
||||
layer: "background",
|
||||
class_name: "desktop",
|
||||
anchor: ["top", "bottom", "left", "right"],
|
||||
child: Desktop(),
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import PowerMenu from "../services/powermenu.js";
|
||||
import icons from "../icons.js";
|
||||
import Gtk from "gi://Gtk";
|
||||
import { openSettings } from "../settings/theme.js";
|
||||
|
||||
/**
|
||||
* @param {string} label
|
||||
* @param {string} icon
|
||||
* @param {import('types/widgets/menu').MenuItemProps['on_activate']} on_activate
|
||||
*/
|
||||
const Item = (label, icon, on_activate) =>
|
||||
Widget.MenuItem({
|
||||
on_activate,
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon(icon),
|
||||
Widget.Label({
|
||||
label,
|
||||
hexpand: true,
|
||||
xalign: 0,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
export default () =>
|
||||
Widget.Menu({
|
||||
class_name: "desktop-menu",
|
||||
children: [
|
||||
Widget.MenuItem({
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon(icons.powermenu.shutdown),
|
||||
Widget.Label({
|
||||
label: "System",
|
||||
hexpand: true,
|
||||
xalign: 0,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
submenu: Widget.Menu({
|
||||
children: [
|
||||
Item("Shutdown", icons.powermenu.shutdown, () =>
|
||||
PowerMenu.action("shutdown"),
|
||||
),
|
||||
Item("Log Out", icons.powermenu.logout, () =>
|
||||
PowerMenu.action("logout"),
|
||||
),
|
||||
Item("Reboot", icons.powermenu.reboot, () =>
|
||||
PowerMenu.action("reboot"),
|
||||
),
|
||||
Item("Sleep", icons.powermenu.sleep, () =>
|
||||
PowerMenu.action("reboot"),
|
||||
),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
Item("Applications", icons.apps.apps, () =>
|
||||
App.openWindow("applauncher"),
|
||||
),
|
||||
new Gtk.SeparatorMenuItem(),
|
||||
Item("Settings", icons.ui.settings, openSettings),
|
||||
],
|
||||
});
|
||||
142
modules/home-manager/desktops/hyprland/ags/js/dock/Dock.js
Normal file
@@ -0,0 +1,142 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import Applications from "resource:///com/github/Aylur/ags/service/applications.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import icons from "../icons.js";
|
||||
import options from "../options.js";
|
||||
import { launchApp, range } from "../utils.js";
|
||||
|
||||
const focus = ({ address }) =>
|
||||
Hyprland.sendMessage(`dispatch focuswindow address:${address}`);
|
||||
|
||||
/** @param {import('types/widgets/button').ButtonProps & { icon: string, pinned?: boolean }} o */
|
||||
const AppButton = ({ icon, pinned = false, ...rest }) => {
|
||||
const indicators = Widget.Box({
|
||||
vpack: "end",
|
||||
hpack: "center",
|
||||
children: range(5, 0).map(() =>
|
||||
Widget.Box({
|
||||
class_name: "indicator",
|
||||
visible: false,
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
return Widget.Button({
|
||||
...rest,
|
||||
attribute: indicators,
|
||||
child: Widget.Box({
|
||||
class_name: "box",
|
||||
child: Widget.Overlay({
|
||||
child: Widget.Icon({
|
||||
icon,
|
||||
size: options.desktop.dock.icon_size.bind("value"),
|
||||
}),
|
||||
pass_through: true,
|
||||
overlays: pinned ? [indicators] : [],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const Taskbar = () =>
|
||||
Widget.Box({
|
||||
children: Hyprland.bind("clients").transform((c) =>
|
||||
c.map((client) => {
|
||||
for (const appName of options.desktop.dock.pinned_apps.value) {
|
||||
if (client.class.toLowerCase().includes(appName.toLowerCase()))
|
||||
return null;
|
||||
}
|
||||
for (const app of Applications.list) {
|
||||
if (
|
||||
(client.title && app.match(client.title)) ||
|
||||
(client.class && app.match(client.class))
|
||||
) {
|
||||
return AppButton({
|
||||
icon: app.icon_name || "",
|
||||
tooltip_text: app.name,
|
||||
on_primary_click: () => focus(client),
|
||||
on_middle_click: () => launchApp(app),
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
const PinnedApps = () =>
|
||||
Widget.Box({
|
||||
class_name: "pins",
|
||||
homogeneous: true,
|
||||
children: options.desktop.dock.pinned_apps.bind("value").transform((v) =>
|
||||
v
|
||||
.map((term) => ({ app: Applications.query(term)?.[0], term }))
|
||||
.filter(({ app }) => app)
|
||||
.map(({ app, term }) =>
|
||||
AppButton({
|
||||
pinned: true,
|
||||
icon: app.icon_name || "",
|
||||
on_primary_click: () => {
|
||||
for (const client of Hyprland.clients) {
|
||||
if (client.class.toLowerCase().includes(term))
|
||||
return focus(client);
|
||||
}
|
||||
|
||||
launchApp(app);
|
||||
},
|
||||
on_middle_click: () => launchApp(app),
|
||||
tooltip_text: app.name,
|
||||
setup: (button) =>
|
||||
button.hook(Hyprland, () => {
|
||||
const running = Hyprland.clients.filter((client) =>
|
||||
client.class.toLowerCase().includes(term),
|
||||
);
|
||||
|
||||
const focused = running.find(
|
||||
(client) => client.address === Hyprland.active.client.address,
|
||||
);
|
||||
|
||||
const index = running.findIndex((c) => c === focused);
|
||||
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
const indicator = button.attribute.children[i];
|
||||
indicator.visible = i < running.length;
|
||||
indicator.toggleClassName("focused", i === index);
|
||||
}
|
||||
|
||||
button.set_tooltip_text(
|
||||
running.length === 1 ? running[0].title : app.name,
|
||||
);
|
||||
}),
|
||||
}),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
export default () => {
|
||||
const pinnedapps = PinnedApps();
|
||||
const taskbar = Taskbar();
|
||||
const applauncher = AppButton({
|
||||
class_name: "launcher nonrunning",
|
||||
icon: icons.apps.apps,
|
||||
tooltip_text: "Applications",
|
||||
on_clicked: () => App.toggleWindow("applauncher"),
|
||||
});
|
||||
const separator = Widget.Separator({
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
orientation: 1,
|
||||
setup: (self) =>
|
||||
self.hook(
|
||||
taskbar,
|
||||
() => {
|
||||
self.visible = taskbar.children.length > 0;
|
||||
},
|
||||
"notify::children",
|
||||
),
|
||||
});
|
||||
return Widget.Box({
|
||||
class_name: "dock",
|
||||
children: [applauncher, pinnedapps, separator, taskbar],
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import Dock from "./Dock.js";
|
||||
import options from "../options.js";
|
||||
|
||||
/** @param {number} monitor */
|
||||
export default (monitor) => {
|
||||
const revealer = Widget.Revealer({
|
||||
transition: "slide_up",
|
||||
child: Dock(),
|
||||
setup: (self) => {
|
||||
const update = () => {
|
||||
const ws = Hyprland.getWorkspace(Hyprland.active.workspace.id);
|
||||
if (Hyprland.getMonitor(monitor)?.name === ws?.monitor)
|
||||
self.reveal_child = ws?.windows === 0;
|
||||
};
|
||||
self
|
||||
.hook(Hyprland, update, "client-added")
|
||||
.hook(Hyprland, update, "client-removed")
|
||||
.hook(Hyprland.active.workspace, update);
|
||||
},
|
||||
});
|
||||
|
||||
return Widget.Window({
|
||||
monitor,
|
||||
name: `dock${monitor}`,
|
||||
class_name: "floating-dock",
|
||||
anchor: ["bottom"],
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
revealer,
|
||||
Widget.Box({
|
||||
class_name: "padding",
|
||||
css: "padding: 2px;",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
setup: (self) =>
|
||||
self
|
||||
.on("enter-notify-event", () => (revealer.reveal_child = true))
|
||||
.on("leave-notify-event", () => (revealer.reveal_child = false))
|
||||
.bind("visible", options.bar.position, "value", (v) => v !== "bottom"),
|
||||
});
|
||||
};
|
||||
122
modules/home-manager/desktops/hyprland/ags/js/icons.js
Normal file
@@ -0,0 +1,122 @@
|
||||
export default {
|
||||
lock: "system-lock-screen-symbolic",
|
||||
fallback: {
|
||||
executable: "application-x-executable-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: "",
|
||||
},
|
||||
asusctl: {
|
||||
profile: {
|
||||
Balanced: "power-profile-balanced-symbolic",
|
||||
Quiet: "power-profile-power-saver-symbolic",
|
||||
Performance: "power-profile-performance-symbolic",
|
||||
},
|
||||
mode: {
|
||||
Integrated: "",
|
||||
Hybrid: "",
|
||||
},
|
||||
},
|
||||
apps: {
|
||||
apps: "view-app-grid-symbolic",
|
||||
search: "folder-saved-search-symbolic",
|
||||
},
|
||||
battery: {
|
||||
charging: "",
|
||||
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: "preferences-system-notifications-symbolic",
|
||||
silent: "notifications-disabled-symbolic",
|
||||
},
|
||||
trash: {
|
||||
full: "user-trash-full-symbolic",
|
||||
empty: "user-trash-symbolic",
|
||||
},
|
||||
mpris: {
|
||||
fallback: "audio-x-generic-symbolic",
|
||||
shuffle: {
|
||||
enabled: "",
|
||||
disabled: "",
|
||||
},
|
||||
loop: {
|
||||
none: "",
|
||||
track: "",
|
||||
playlist: "",
|
||||
},
|
||||
playing: "",
|
||||
paused: "",
|
||||
stopped: "",
|
||||
prev: "",
|
||||
next: "",
|
||||
},
|
||||
ui: {
|
||||
colorpicker: "color-select-symbolic",
|
||||
close: "window-close-symbolic",
|
||||
info: "info-symbolic",
|
||||
menu: "open-menu-symbolic",
|
||||
link: "external-link-symbolic",
|
||||
settings: "emblem-system-symbolic",
|
||||
tick: "object-select-symbolic",
|
||||
arrow: {
|
||||
right: "pan-end-symbolic",
|
||||
left: "pan-start-symbolic",
|
||||
down: "pan-down-symbolic",
|
||||
up: "pan-up-symbolic",
|
||||
},
|
||||
},
|
||||
system: {
|
||||
cpu: "org.gnome.SystemMonitor-symbolic",
|
||||
ram: "drive-harddisk-solidstate-symbolic",
|
||||
temp: "temperature-symbolic",
|
||||
},
|
||||
dialog: {
|
||||
Search: "",
|
||||
Applauncher: "",
|
||||
Bar: "",
|
||||
Border: "",
|
||||
Color: "",
|
||||
Desktop: "",
|
||||
Font: "",
|
||||
General: "",
|
||||
Miscellaneous: "",
|
||||
Theme: "",
|
||||
Notifications: " ",
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Avatar from "../misc/Avatar.js";
|
||||
import Lockscreen from "../services/lockscreen.js";
|
||||
import Layer from "gi://GtkLayerShell";
|
||||
|
||||
const PasswordEntry = () =>
|
||||
Widget.Box({
|
||||
children: [
|
||||
Widget.Entry({
|
||||
setup: (self) => self.hook(Lockscreen, () => (self.text = ""), "lock"),
|
||||
visibility: false,
|
||||
placeholder_text: "Password",
|
||||
on_accept: ({ text }) => Lockscreen.auth(text || ""),
|
||||
hpack: "center",
|
||||
hexpand: true,
|
||||
}),
|
||||
Widget.Spinner({
|
||||
active: true,
|
||||
vpack: "center",
|
||||
setup: (self) =>
|
||||
self.hook(
|
||||
Lockscreen,
|
||||
(_, auth) => (self.visible = auth),
|
||||
"authenticating",
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
/** @param {number} monitor */
|
||||
export default (monitor) => {
|
||||
const win = Widget.Window({
|
||||
name: `lockscreen${monitor}`,
|
||||
class_name: "lockscreen",
|
||||
monitor,
|
||||
layer: "overlay",
|
||||
visible: false,
|
||||
setup: (self) =>
|
||||
self.hook(Lockscreen, (_, lock) => (self.visible = lock), "lock"),
|
||||
child: Widget.Box({
|
||||
css: "min-width: 3000px; min-height: 2000px;",
|
||||
class_name: "shader",
|
||||
child: Widget.Box({
|
||||
class_name: "content",
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
hpack: "center",
|
||||
vpack: "center",
|
||||
children: [
|
||||
Avatar({
|
||||
hpack: "center",
|
||||
vpack: "center",
|
||||
}),
|
||||
PasswordEntry(),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
Layer.set_keyboard_mode(win, Layer.KeyboardMode.EXCLUSIVE);
|
||||
return win;
|
||||
};
|
||||
7
modules/home-manager/desktops/hyprland/ags/js/lockscreen/auth.py
Executable file
@@ -0,0 +1,7 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import pam
|
||||
import sys
|
||||
import getpass
|
||||
|
||||
print(pam.authenticate(getpass.getuser(), sys.argv[1]));
|
||||
43
modules/home-manager/desktops/hyprland/ags/js/main.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import Dashboard from "./dashboard/Dashboard.js";
|
||||
import Desktop from "./desktop/Desktop.js";
|
||||
import Lockscreen from "./lockscreen/Lockscreen.js";
|
||||
import Notifications from "./notifications/Notifications.js";
|
||||
import OSD from "./osd/OSD.js";
|
||||
import Overview from "./overview/Overview.js";
|
||||
import PowerMenu from "./powermenu/PowerMenu.js";
|
||||
import QuickSettings from "./quicksettings/QuickSettings.js";
|
||||
import ScreenCorners from "./screencorner/ScreenCorners.js";
|
||||
import TopBar from "./bar/TopBar.js";
|
||||
import Verification from "./powermenu/Verification.js";
|
||||
import About from "./about/about.js";
|
||||
import { init } from "./settings/setup.js";
|
||||
import { forMonitors } from "./utils.js";
|
||||
import { initWallpaper } from "./settings/wallpaper.js";
|
||||
import options from "./options.js";
|
||||
|
||||
initWallpaper();
|
||||
|
||||
const windows = () => [
|
||||
forMonitors(Desktop),
|
||||
forMonitors(Lockscreen),
|
||||
forMonitors(Notifications),
|
||||
forMonitors(OSD),
|
||||
forMonitors(ScreenCorners),
|
||||
forMonitors(TopBar),
|
||||
|
||||
Dashboard(),
|
||||
Overview(),
|
||||
PowerMenu(),
|
||||
QuickSettings(),
|
||||
Verification(),
|
||||
About(),
|
||||
];
|
||||
|
||||
export default {
|
||||
onConfigParsed: init,
|
||||
windows: windows().flat(1),
|
||||
closeWindowDelay: {
|
||||
quicksettings: options.transition.value,
|
||||
dashboard: options.transition.value,
|
||||
},
|
||||
};
|
||||
16
modules/home-manager/desktops/hyprland/ags/js/misc/Avatar.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import options from "../options.js";
|
||||
|
||||
/** @param {import('types/widgets/box').BoxProps=} props */
|
||||
export default (props) =>
|
||||
Widget.Box({ ...props, class_name: "avatar" })
|
||||
.hook(options.desktop.avatar, (box) =>
|
||||
box.setCss(`
|
||||
background-image: url('${options.desktop.avatar.value}');
|
||||
background-size: cover;
|
||||
`),
|
||||
)
|
||||
.on("size-allocate", (box) => {
|
||||
const h = box.get_allocated_height();
|
||||
box.set_size_request(Math.ceil(h * 1.1), -1);
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Battery from "resource:///com/github/Aylur/ags/service/battery.js";
|
||||
|
||||
export default () =>
|
||||
Widget.Icon({
|
||||
class_name: "battery",
|
||||
icon: Battery.bind("icon_name"),
|
||||
setup: (icon) =>
|
||||
icon.hook(Battery, () => {
|
||||
icon.toggleClassName("charging", Battery.charging);
|
||||
icon.toggleClassName("charged", Battery.charged);
|
||||
icon.toggleClassName("low", Battery.percent < 30);
|
||||
}),
|
||||
});
|
||||
17
modules/home-manager/desktops/hyprland/ags/js/misc/Clock.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { clock } from "../variables.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
|
||||
/**
|
||||
* @param {import('types/widgets/label').Props & {
|
||||
* format?: string,
|
||||
* interval?: number,
|
||||
* }} o
|
||||
*/
|
||||
export default ({ format = "%H:%M:%S %B %e. %A", ...rest } = {}) =>
|
||||
Widget.Label({
|
||||
class_name: "clock",
|
||||
label: clock.bind("value").transform((time) => {
|
||||
return time.format(format) || "wrong format";
|
||||
}),
|
||||
...rest,
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import { subclass, register } from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import AgsLabel from "resource:///com/github/Aylur/ags/widgets/label.js";
|
||||
|
||||
class FontIcon extends AgsLabel {
|
||||
static {
|
||||
register(this);
|
||||
}
|
||||
|
||||
/** @param {string | import('types/widgets/label').Props<any> & { icon?: string }} params */
|
||||
constructor(params = "") {
|
||||
// @ts-expect-error
|
||||
const { icon = "", ...rest } = params;
|
||||
|
||||
super(typeof params === "string" ? {} : rest);
|
||||
this.toggleClassName("font-icon");
|
||||
|
||||
if (typeof params === "object") this.icon = icon;
|
||||
|
||||
if (typeof params === "string") this.icon = params;
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this.label;
|
||||
}
|
||||
set icon(icon) {
|
||||
this.label = icon;
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.get_style_context().get_property(
|
||||
"font-size",
|
||||
Gtk.StateFlags.NORMAL,
|
||||
);
|
||||
}
|
||||
|
||||
/** @returns {[number, number]} */
|
||||
vfunc_get_preferred_height() {
|
||||
return [this.size, this.size];
|
||||
}
|
||||
|
||||
/** @returns {[number, number]} */
|
||||
vfunc_get_preferred_width() {
|
||||
return [this.size, this.size];
|
||||
}
|
||||
}
|
||||
|
||||
export default subclass(FontIcon);
|
||||
@@ -0,0 +1,62 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
|
||||
/**
|
||||
* @typedef {import('types/widgets/eventbox').EventBoxProps & {
|
||||
* indicator?: import('types/widgets/box').BoxProps['child']
|
||||
* direction?: 'left' | 'right' | 'down' | 'up'
|
||||
* duration?: number
|
||||
* setupRevealer?: (rev: ReturnType<typeof Widget.Revealer>) => void
|
||||
* setupEventBox?: (rev: ReturnType<typeof Widget.EventBox>) => void
|
||||
* }} HoverRevealProps
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {HoverRevealProps} props
|
||||
*/
|
||||
export default ({
|
||||
indicator,
|
||||
child,
|
||||
direction = "left",
|
||||
duration = 300,
|
||||
setupEventBox,
|
||||
setupRevealer,
|
||||
...rest
|
||||
}) => {
|
||||
let open = false;
|
||||
const vertical = direction === "down" || direction === "up";
|
||||
const posStart = direction === "down" || direction === "right";
|
||||
const posEnd = direction === "up" || direction === "left";
|
||||
|
||||
const revealer = Widget.Revealer({
|
||||
transition: `slide_${direction}`,
|
||||
setup: setupRevealer,
|
||||
transition_duration: duration,
|
||||
child,
|
||||
});
|
||||
|
||||
const eventbox = Widget.EventBox({
|
||||
...rest,
|
||||
setup: setupEventBox,
|
||||
on_hover: () => {
|
||||
if (open) return;
|
||||
|
||||
revealer.reveal_child = true;
|
||||
Utils.timeout(duration, () => (open = true));
|
||||
},
|
||||
on_hover_lost: () => {
|
||||
if (!open) return;
|
||||
|
||||
revealer.reveal_child = false;
|
||||
open = false;
|
||||
},
|
||||
child: Widget.Box({
|
||||
vertical,
|
||||
children: [posStart && indicator, revealer, posEnd && indicator],
|
||||
}),
|
||||
});
|
||||
|
||||
return Widget.Box({
|
||||
children: [eventbox],
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import RegularWindow from "./RegularWindow.js";
|
||||
import Gtk from "gi://Gtk";
|
||||
|
||||
export default () => {
|
||||
const selected = Widget.Label({
|
||||
css: "font-size: 1.2em;",
|
||||
});
|
||||
|
||||
const flowbox = Widget.FlowBox({
|
||||
min_children_per_line: 10,
|
||||
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({
|
||||
on_change: ({ text }) =>
|
||||
flowbox.get_children().forEach((child) => {
|
||||
child.visible = child.get_child().iconName.includes(text);
|
||||
}),
|
||||
});
|
||||
|
||||
return RegularWindow({
|
||||
name: "icons",
|
||||
visible: true,
|
||||
child: Widget.Box({
|
||||
css: "padding: 30px;",
|
||||
spacing: 20,
|
||||
vertical: true,
|
||||
children: [
|
||||
entry,
|
||||
Widget.Scrollable({
|
||||
hscroll: "never",
|
||||
vscroll: "always",
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
css: "min-width: 500px;" + "min-height: 500px;",
|
||||
child: flowbox,
|
||||
}),
|
||||
selected,
|
||||
],
|
||||
}),
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import GLib from "gi://GLib";
|
||||
|
||||
/** @param {import('types/service/notifications').Notification} n */
|
||||
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 = "dialog-information-symbolic";
|
||||
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,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
/** @param {import('types/service/notifications').Notification} notification */
|
||||
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,
|
||||
use_markup: true,
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "time",
|
||||
vpack: "start",
|
||||
label: GLib.DateTime.new_from_unix_local(
|
||||
notification.time,
|
||||
).format("%H:%M"),
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: "close-button",
|
||||
vpack: "start",
|
||||
child: Widget.Icon("window-close-symbolic"),
|
||||
on_clicked: () => notification.close(),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "description",
|
||||
hexpand: true,
|
||||
use_markup: true,
|
||||
xalign: 0,
|
||||
justification: "left",
|
||||
label: notification.body,
|
||||
wrap: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const actionsbox = 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),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
return Widget.EventBox({
|
||||
class_name: `notification ${notification.urgency}`,
|
||||
vexpand: false,
|
||||
on_primary_click: () => notification.dismiss(),
|
||||
on_hover() {
|
||||
actionsbox.reveal_child = true;
|
||||
},
|
||||
on_hover_lost() {
|
||||
actionsbox.reveal_child = true;
|
||||
notification.dismiss();
|
||||
},
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
children: [content, notification.actions.length > 0 && actionsbox],
|
||||
}),
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
import AgsWindow from "resource:///com/github/Aylur/ags/widgets/window.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import options from "../options.js";
|
||||
import GObject from "gi://GObject";
|
||||
|
||||
const keyGrabber = Widget.Window({
|
||||
name: "key-grabber",
|
||||
popup: true,
|
||||
anchor: ["top", "left", "right", "bottom"],
|
||||
css: "background-color: transparent;",
|
||||
visible: false,
|
||||
exclusivity: "ignore",
|
||||
keymode: "on-demand",
|
||||
layer: "top",
|
||||
attribute: { list: [] },
|
||||
setup: (self) =>
|
||||
self.on("notify::visible", ({ visible }) => {
|
||||
if (!visible)
|
||||
self.attribute?.list.forEach((name) => App.closeWindow(name));
|
||||
}),
|
||||
child: Widget.EventBox({ vexpand: true }).on("button-press-event", () => {
|
||||
App.closeWindow("key-grabber");
|
||||
keyGrabber.attribute?.list.forEach((name) => App.closeWindow(name));
|
||||
}),
|
||||
});
|
||||
|
||||
// add before any PopupWindow is instantiated
|
||||
App.addWindow(keyGrabber);
|
||||
|
||||
export class PopupWindow extends AgsWindow {
|
||||
static {
|
||||
GObject.registerClass(this);
|
||||
}
|
||||
|
||||
constructor({ name, child, transition = "none", visible = false, ...rest }) {
|
||||
super({
|
||||
...rest,
|
||||
name,
|
||||
popup: true,
|
||||
keymode: "exclusive",
|
||||
layer: "overlay",
|
||||
class_names: ["popup-window", name],
|
||||
});
|
||||
|
||||
child.toggleClassName("window-content");
|
||||
this.revealer = Widget.Revealer({
|
||||
transition,
|
||||
child,
|
||||
transition_duration: options.transition.value,
|
||||
setup: (self) =>
|
||||
self.hook(App, (_, wname, visible) => {
|
||||
if (wname === name) this.revealer.reveal_child = visible;
|
||||
}),
|
||||
});
|
||||
|
||||
this.child = Widget.Box({
|
||||
css: "padding: 1px;",
|
||||
child: this.revealer,
|
||||
});
|
||||
|
||||
this.show_all();
|
||||
this.visible = visible;
|
||||
|
||||
keyGrabber.bind("visible", this, "visible");
|
||||
keyGrabber.attribute?.list.push(name);
|
||||
}
|
||||
|
||||
set transition(dir) {
|
||||
this.revealer.transition = dir;
|
||||
}
|
||||
get transition() {
|
||||
return this.revealer.transition;
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {import('types/widgets/window').WindowProps & {
|
||||
* name: string
|
||||
* child: import('types/widgets/box').default
|
||||
* transition?: import('types/widgets/revealer').RevealerProps['transition']
|
||||
* }} config
|
||||
*/
|
||||
export default (config) => new PopupWindow(config);
|
||||
@@ -0,0 +1,60 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
|
||||
/** @param {import('types/widgets/box').BoxProps & {
|
||||
* width: number
|
||||
* height: number
|
||||
* }} o */
|
||||
export default ({
|
||||
height = 18,
|
||||
width = 180,
|
||||
vertical = false,
|
||||
child,
|
||||
...props
|
||||
}) => {
|
||||
const fill = Widget.Box({
|
||||
class_name: "fill",
|
||||
hexpand: vertical,
|
||||
vexpand: !vertical,
|
||||
hpack: vertical ? "fill" : "start",
|
||||
vpack: vertical ? "end" : "fill",
|
||||
children: [child],
|
||||
});
|
||||
|
||||
let fill_size = 0;
|
||||
|
||||
return Widget.Box({
|
||||
...props,
|
||||
class_name: "progress",
|
||||
css: `
|
||||
min-width: ${width}px;
|
||||
min-height: ${height}px;
|
||||
`,
|
||||
children: [fill],
|
||||
attribute: (value) => {
|
||||
if (value < 0) return;
|
||||
|
||||
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.setCss(`min-${axis}: ${preferred}px;`);
|
||||
return;
|
||||
}
|
||||
|
||||
const frames = 10;
|
||||
const goal = preferred - fill_size;
|
||||
const step = goal / frames;
|
||||
|
||||
for (let i = 0; i < frames; ++i) {
|
||||
Utils.timeout(5 * i, () => {
|
||||
fill_size += step;
|
||||
fill.setCss(`min-${axis}: ${fill_size}px`);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
import { subclass } from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Gtk from "gi://Gtk";
|
||||
|
||||
export default subclass(Gtk.Window, "RegularWindow");
|
||||
263
modules/home-manager/desktops/hyprland/ags/js/misc/mpris.js
Normal file
@@ -0,0 +1,263 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import icons from "../icons.js";
|
||||
import { blurImg } from "../utils.js";
|
||||
|
||||
/**
|
||||
* @param {import('types/service/mpris').MprisPlayer} player
|
||||
* @param {import('types/widgets/box').BoxProps=} props
|
||||
*/
|
||||
export const CoverArt = (player, props) =>
|
||||
Widget.Box({
|
||||
...props,
|
||||
class_name: "cover",
|
||||
css: player
|
||||
.bind("cover_path")
|
||||
.transform((p) => `background-image: url("${p}")`),
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('types/service/mpris').MprisPlayer} player
|
||||
* @param {import('types/widgets/box').BoxProps=} props
|
||||
*/
|
||||
export const BlurredCoverArt = (player, props) =>
|
||||
Widget.Box({
|
||||
...props,
|
||||
class_name: "blurred-cover",
|
||||
setup: (self) =>
|
||||
self.hook(
|
||||
player,
|
||||
(box) =>
|
||||
blurImg(player.cover_path).then((img) => {
|
||||
img && box.setCss(`background-image: url("${img}")`);
|
||||
}),
|
||||
"notify::cover-path",
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('types/service/mpris').MprisPlayer} player
|
||||
* @param {import('types/widgets/label').Props=} props
|
||||
*/
|
||||
export const TitleLabel = (player, props) =>
|
||||
Widget.Label({
|
||||
...props,
|
||||
class_name: "title",
|
||||
label: player.bind("track_title"),
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('types/service/mpris').MprisPlayer} player
|
||||
* @param {import('types/widgets/label').Props=} props
|
||||
*/
|
||||
export const ArtistLabel = (player, props) =>
|
||||
Widget.Label({
|
||||
...props,
|
||||
class_name: "artist",
|
||||
label: player.bind("track_artists").transform((a) => a.join(", ") || ""),
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('types/service/mpris').MprisPlayer} player
|
||||
* @param {import('types/widgets/icon').Props & { symbolic?: boolean }=} props
|
||||
*/
|
||||
export const PlayerIcon = (player, { symbolic = true, ...props } = {}) =>
|
||||
Widget.Icon({
|
||||
...props,
|
||||
class_name: "player-icon",
|
||||
tooltip_text: player.identity || "",
|
||||
setup: (self) =>
|
||||
self.hook(player, (icon) => {
|
||||
const name = `${player.entry}${symbolic ? "-symbolic" : ""}`;
|
||||
Utils.lookUpIcon(name)
|
||||
? (icon.icon = name)
|
||||
: (icon.icon = icons.mpris.fallback);
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('types/service/mpris').MprisPlayer} player
|
||||
* @param {import('types/widgets/slider').SliderProps=} props
|
||||
*/
|
||||
export const PositionSlider = (player, props) =>
|
||||
Widget.Slider({
|
||||
...props,
|
||||
class_name: "position-slider",
|
||||
draw_value: false,
|
||||
on_change: ({ value }) => (player.position = player.length * value),
|
||||
setup: (self) => {
|
||||
const update = () => {
|
||||
if (self.dragging) return;
|
||||
|
||||
self.visible = player.length > 0;
|
||||
if (player.length > 0) self.value = player.position / player.length;
|
||||
};
|
||||
self.hook(player, update);
|
||||
self.hook(player, update, "position");
|
||||
self.poll(1000, update);
|
||||
},
|
||||
});
|
||||
|
||||
/** @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 */
|
||||
export const PositionLabel = (player) =>
|
||||
Widget.Label({
|
||||
setup: (self) => {
|
||||
const update = (_, time) => {
|
||||
player.length > 0
|
||||
? (self.label = lengthStr(time || player.position))
|
||||
: (self.visible = !!player);
|
||||
};
|
||||
self.hook(player, update, "position");
|
||||
self.poll(1000, update);
|
||||
},
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const LengthLabel = (player) =>
|
||||
Widget.Label({
|
||||
label: player.bind("length").transform((l) => lengthStr(l)),
|
||||
visible: player.bind("length").transform((l) => l > 0),
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const Slash = (player) =>
|
||||
Widget.Label({
|
||||
label: "/",
|
||||
visible: player.bind("length").transform((l) => l > 0),
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {Object} o
|
||||
* @param {import('types/service/mpris').MprisPlayer} o.player
|
||||
* @param {import('types/widgets/stack').StackProps['children']} o.children
|
||||
* @param {'shuffle' | 'loop' | 'playPause' | 'previous' | 'next'} o.onClick
|
||||
* @param {string} o.prop
|
||||
* @param {string} o.canProp
|
||||
* @param {any} o.cantValue
|
||||
*/
|
||||
const PlayerButton = ({
|
||||
player,
|
||||
children,
|
||||
onClick,
|
||||
prop,
|
||||
canProp,
|
||||
cantValue,
|
||||
}) =>
|
||||
Widget.Button({
|
||||
child: Widget.Stack({ children }).bind(
|
||||
"shown",
|
||||
player,
|
||||
prop,
|
||||
(p) => `${p}`,
|
||||
),
|
||||
on_clicked: () => player[onClick](),
|
||||
visible: player.bind(canProp).transform((c) => c !== cantValue),
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const ShuffleButton = (player) =>
|
||||
PlayerButton({
|
||||
player,
|
||||
children: {
|
||||
true: Widget.Label({
|
||||
class_name: "shuffle enabled",
|
||||
label: icons.mpris.shuffle.enabled,
|
||||
}),
|
||||
false: Widget.Label({
|
||||
class_name: "shuffle disabled",
|
||||
label: icons.mpris.shuffle.disabled,
|
||||
}),
|
||||
},
|
||||
onClick: "shuffle",
|
||||
prop: "shuffle-status",
|
||||
canProp: "shuffle-status",
|
||||
cantValue: null,
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const LoopButton = (player) =>
|
||||
PlayerButton({
|
||||
player,
|
||||
children: {
|
||||
None: Widget.Label({
|
||||
class_name: "loop none",
|
||||
label: icons.mpris.loop.none,
|
||||
}),
|
||||
Track: Widget.Label({
|
||||
class_name: "loop track",
|
||||
label: icons.mpris.loop.track,
|
||||
}),
|
||||
Playlist: Widget.Label({
|
||||
class_name: "loop playlist",
|
||||
label: icons.mpris.loop.playlist,
|
||||
}),
|
||||
},
|
||||
onClick: "loop",
|
||||
prop: "loop-status",
|
||||
canProp: "loop-status",
|
||||
cantValue: null,
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const PlayPauseButton = (player) =>
|
||||
PlayerButton({
|
||||
player,
|
||||
children: {
|
||||
Playing: Widget.Label({
|
||||
class_name: "playing",
|
||||
label: icons.mpris.playing,
|
||||
}),
|
||||
Paused: Widget.Label({
|
||||
class_name: "paused",
|
||||
label: icons.mpris.paused,
|
||||
}),
|
||||
Stopped: Widget.Label({
|
||||
class_name: "stopped",
|
||||
label: icons.mpris.stopped,
|
||||
}),
|
||||
},
|
||||
onClick: "playPause",
|
||||
prop: "play-back-status",
|
||||
canProp: "can-play",
|
||||
cantValue: false,
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const PreviousButton = (player) =>
|
||||
PlayerButton({
|
||||
player,
|
||||
children: {
|
||||
true: Widget.Label({
|
||||
class_name: "previous",
|
||||
label: icons.mpris.prev,
|
||||
}),
|
||||
},
|
||||
onClick: "previous",
|
||||
prop: "can-go-prev",
|
||||
canProp: "can-go-prev",
|
||||
cantValue: false,
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
export const NextButton = (player) =>
|
||||
PlayerButton({
|
||||
player,
|
||||
children: {
|
||||
true: Widget.Label({
|
||||
class_name: "next",
|
||||
label: icons.mpris.next,
|
||||
}),
|
||||
},
|
||||
onClick: "next",
|
||||
prop: "can-go-next",
|
||||
canProp: "can-go-next",
|
||||
cantValue: false,
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Notification from "../misc/Notification.js";
|
||||
import options from "../options.js";
|
||||
|
||||
/** @param {import('types/widgets/revealer').default} parent */
|
||||
const Popups = (parent) => {
|
||||
const map = new Map();
|
||||
|
||||
const onDismissed = (_, id, force = false) => {
|
||||
if (!id || !map.has(id)) return;
|
||||
|
||||
if (map.get(id).isHovered() && !force) return;
|
||||
|
||||
if (map.size - 1 === 0) parent.reveal_child = false;
|
||||
|
||||
Utils.timeout(200, () => {
|
||||
map.get(id)?.destroy();
|
||||
map.delete(id);
|
||||
});
|
||||
};
|
||||
|
||||
/** @param {import('types/widgets/box').default} box */
|
||||
const onNotified = (box, id) => {
|
||||
if (!id || Notifications.dnd) return;
|
||||
|
||||
const n = Notifications.getNotification(id);
|
||||
if (!n) return;
|
||||
|
||||
if (options.notifications.black_list.value.includes(n.app_name || ""))
|
||||
return;
|
||||
|
||||
map.delete(id);
|
||||
map.set(id, Notification(n));
|
||||
box.children = Array.from(map.values()).reverse();
|
||||
Utils.timeout(10, () => {
|
||||
parent.reveal_child = true;
|
||||
});
|
||||
};
|
||||
|
||||
return Widget.Box({ vertical: true })
|
||||
.hook(Notifications, onNotified, "notified")
|
||||
.hook(Notifications, onDismissed, "dismissed")
|
||||
.hook(Notifications, (box, id) => onDismissed(box, id, true), "closed");
|
||||
};
|
||||
|
||||
/** @param {import('types/widgets/revealer').RevealerProps['transition']} transition */
|
||||
const PopupList = (transition = "slide_down") =>
|
||||
Widget.Box({
|
||||
css: "padding: 1px",
|
||||
children: [
|
||||
Widget.Revealer({
|
||||
transition,
|
||||
setup: (self) => (self.child = Popups(self)),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
/** @param {number} monitor */
|
||||
export default (monitor) =>
|
||||
Widget.Window({
|
||||
monitor,
|
||||
name: `notifications${monitor}`,
|
||||
class_name: "notifications",
|
||||
anchor: options.notifications.position.bind("value"),
|
||||
child: PopupList(),
|
||||
});
|
||||
303
modules/home-manager/desktops/hyprland/ags/js/options.js
Normal file
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* An object holding Options that are Variables with cached values.
|
||||
*
|
||||
* to update an option at runtime simply run
|
||||
* ags -r "options.path.to.option.setValue('value')"
|
||||
*
|
||||
* resetting:
|
||||
* ags -r "options.reset()"
|
||||
*/
|
||||
|
||||
import {
|
||||
Option,
|
||||
resetOptions,
|
||||
getValues,
|
||||
apply,
|
||||
getOptions,
|
||||
} from "./settings/option.js";
|
||||
import { USER } from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import themes from "./themes.js";
|
||||
|
||||
export default {
|
||||
reset: resetOptions,
|
||||
values: getValues,
|
||||
apply: apply,
|
||||
list: getOptions,
|
||||
|
||||
spacing: Option(9),
|
||||
padding: Option(8),
|
||||
radii: Option(9),
|
||||
|
||||
popover_padding_multiplier: Option(1.4, {
|
||||
category: "General",
|
||||
note: "popover-padding: padding × this",
|
||||
type: "float",
|
||||
unit: "",
|
||||
}),
|
||||
|
||||
color: {
|
||||
red: Option("#e55f86", { scss: "red" }),
|
||||
green: Option("#00D787", { scss: "green" }),
|
||||
yellow: Option("#EBFF71", { scss: "yellow" }),
|
||||
blue: Option("#51a4e7", { scss: "blue" }),
|
||||
magenta: Option("#9077e7", { scss: "magenta" }),
|
||||
teal: Option("#51e6e6", { scss: "teal" }),
|
||||
orange: Option("#E79E64", { scss: "orange" }),
|
||||
},
|
||||
|
||||
theme: {
|
||||
name: Option(themes[0].name, {
|
||||
category: "exclude",
|
||||
note: "Name to show as active in quicktoggles",
|
||||
}),
|
||||
|
||||
icon: Option(themes[0].icon, {
|
||||
category: "exclude",
|
||||
note: "Icon to show as active in quicktoggles",
|
||||
}),
|
||||
|
||||
scheme: Option("dark", {
|
||||
enums: ["dark", "light"],
|
||||
type: "enum",
|
||||
note: "Color scheme to set on Gtk apps: 'ligth' or 'dark'",
|
||||
title: "Color Scheme",
|
||||
scss: "color-scheme",
|
||||
}),
|
||||
bg: Option("#171717", {
|
||||
title: "Background Color",
|
||||
scss: "bg-color",
|
||||
}),
|
||||
fg: Option("#eeeeee", {
|
||||
title: "Foreground Color",
|
||||
scss: "fg-color",
|
||||
}),
|
||||
|
||||
accent: {
|
||||
accent: Option("$blue", {
|
||||
category: "Theme",
|
||||
title: "Accent Color",
|
||||
scss: "accent",
|
||||
}),
|
||||
fg: Option("#141414", {
|
||||
category: "Theme",
|
||||
title: "Accent Foreground Color",
|
||||
scss: "accent-fg",
|
||||
}),
|
||||
gradient: Option("to right, $accent, lighten($accent, 6%)", {
|
||||
category: "Theme",
|
||||
title: "Accent Linear Gradient",
|
||||
scss: "accent-gradient",
|
||||
}),
|
||||
},
|
||||
|
||||
widget: {
|
||||
bg: Option("$fg-color", {
|
||||
category: "Theme",
|
||||
title: "Widget Background Color",
|
||||
scss: "_widget-bg",
|
||||
}),
|
||||
opacity: Option(94, {
|
||||
category: "Theme",
|
||||
title: "Widget Background Opacity",
|
||||
unit: "",
|
||||
scss: "widget-opacity",
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
border: {
|
||||
color: Option("$fg-color", {
|
||||
category: "Border",
|
||||
title: "Border Color",
|
||||
scss: "_border-color",
|
||||
}),
|
||||
opacity: Option(97, {
|
||||
category: "Border",
|
||||
title: "Border Opacity",
|
||||
unit: "",
|
||||
}),
|
||||
width: Option(1, {
|
||||
category: "Border",
|
||||
title: "Border Width",
|
||||
}),
|
||||
},
|
||||
|
||||
hypr: {
|
||||
inactive_border: Option("rgba(333333ff)", {
|
||||
category: "Border",
|
||||
title: "Border on Inactive Windows",
|
||||
scss: "exclude",
|
||||
}),
|
||||
wm_gaps_multiplier: Option(2.4, {
|
||||
category: "General",
|
||||
scss: "wm-gaps-multiplier",
|
||||
note: "wm-gaps: padding × this",
|
||||
type: "float",
|
||||
unit: "",
|
||||
}),
|
||||
},
|
||||
|
||||
// TODO: use this on revealers
|
||||
transition: Option(200, {
|
||||
category: "exclude",
|
||||
note: "Transition time on aminations in ms, e.g on hover",
|
||||
unit: "ms",
|
||||
}),
|
||||
|
||||
font: {
|
||||
font: Option("Ubuntu Nerd Font", {
|
||||
type: "font",
|
||||
title: "Font",
|
||||
scss: "font",
|
||||
}),
|
||||
mono: Option("Mononoki Nerd Font", {
|
||||
title: "Monospaced Font",
|
||||
scss: "mono-font",
|
||||
}),
|
||||
size: Option(13, {
|
||||
scss: "font-size",
|
||||
unit: "pt",
|
||||
}),
|
||||
},
|
||||
|
||||
applauncher: {
|
||||
width: Option(500),
|
||||
height: Option(500),
|
||||
icon_size: Option(52),
|
||||
},
|
||||
|
||||
bar: {
|
||||
position: Option("top", {
|
||||
enums: ["top", "bottom"],
|
||||
type: "enum",
|
||||
}),
|
||||
style: Option("normal", {
|
||||
enums: ["floating", "normal", "separated"],
|
||||
type: "enum",
|
||||
}),
|
||||
flat_buttons: Option(true, { scss: "bar-flat-buttons" }),
|
||||
separators: Option(true),
|
||||
icon: Option("distro-icon", {
|
||||
note: '"distro-icon" or a single font',
|
||||
}),
|
||||
},
|
||||
|
||||
battery: {
|
||||
show_percentage: Option(true, {
|
||||
persist: true,
|
||||
noReload: false,
|
||||
category: "exclude",
|
||||
}),
|
||||
bar: {
|
||||
show_icon: Option(true, { category: "Bar" }),
|
||||
width: Option(70, { category: "Bar" }),
|
||||
height: Option(14, { category: "Bar" }),
|
||||
full: Option(false, { category: "Bar" }),
|
||||
},
|
||||
low: Option(30, { category: "Bar" }),
|
||||
medium: Option(50, { category: "Bar" }),
|
||||
},
|
||||
|
||||
desktop: {
|
||||
wallpaper: {
|
||||
fg: Option("#fff", { scss: "wallpaper-fg" }),
|
||||
img: Option(themes[0].options["desktop.wallpaper.img"], {
|
||||
scssFormat: (v) => `"${v}"`,
|
||||
type: "img",
|
||||
}),
|
||||
},
|
||||
avatar: Option(`/var/lib/AccountsService/icons/${USER}`, {
|
||||
scssFormat: (v) => `"${v}"`,
|
||||
type: "img",
|
||||
note: "displayed in quicksettings and locksreen",
|
||||
}),
|
||||
screen_corners: Option(true, { scss: "screen-corners" }),
|
||||
clock: {
|
||||
enable: Option(true),
|
||||
position: Option("center center", {
|
||||
note: "halign valign",
|
||||
}),
|
||||
},
|
||||
drop_shadow: Option(true, { scss: "drop-shadow" }),
|
||||
shadow: Option("rgba(0, 0, 0, .6)", { scss: "shadow" }),
|
||||
dock: {
|
||||
icon_size: Option(56),
|
||||
pinned_apps: Option(
|
||||
[
|
||||
"firefox",
|
||||
"org.wezfurlong.wezterm",
|
||||
"org.gnome.Nautilus",
|
||||
"org.gnome.Calendar",
|
||||
"obsidian",
|
||||
"transmission-gtk",
|
||||
"caprine",
|
||||
"teams-for-linux",
|
||||
"discord",
|
||||
"spotify",
|
||||
"com.usebottles.bottles",
|
||||
"org.gnome.Software",
|
||||
],
|
||||
{ scss: "exclude" },
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
notifications: {
|
||||
black_list: Option(["Spotify"], { note: "app-name | entry" }),
|
||||
position: Option(["top"], { note: "anchor" }),
|
||||
width: Option(450),
|
||||
},
|
||||
|
||||
dashboard: {
|
||||
sys_info_size: Option(70, {
|
||||
category: "Desktop",
|
||||
scss: "sys-info-size",
|
||||
}),
|
||||
},
|
||||
|
||||
mpris: {
|
||||
black_list: Option(["Caprine"], {
|
||||
category: "Bar",
|
||||
title: "List of blacklisted mpris players",
|
||||
note: "filters for bus-name, name, identity, entry",
|
||||
}),
|
||||
preferred: Option("spotify", {
|
||||
category: "Bar",
|
||||
title: "Preferred player",
|
||||
}),
|
||||
},
|
||||
|
||||
workspaces: Option(10, {
|
||||
category: "Bar",
|
||||
title: "No. workspaces on bar and overview",
|
||||
note: "Set it to 0 to make it dynamic",
|
||||
}),
|
||||
|
||||
temperature: "/sys/class/thermal/thermal_zone0/temp",
|
||||
systemFetchInterval: 5000,
|
||||
brightnessctlKBD: "asus::kbd_backlight",
|
||||
substitutions: {
|
||||
icons: [
|
||||
["transmission-gtk", "transmission"],
|
||||
["blueberry.py", "bluetooth"],
|
||||
["Caprine", "facebook-messenger"],
|
||||
["", "preferences-desktop-display"],
|
||||
],
|
||||
titles: [
|
||||
["com.github.Aylur.ags", "AGS"],
|
||||
["transmission-gtk", "Transmission"],
|
||||
["com.obsproject.Studio", "OBS"],
|
||||
["com.usebottles.bottles", "Bottles"],
|
||||
["com.github.wwmm.easyeffects", "Easy Effects"],
|
||||
["org.gnome.TextEditor", "Text Editor"],
|
||||
["org.gnome.design.IconLibrary", "Icon Library"],
|
||||
["blueberry.py", "Blueberry"],
|
||||
["org.wezfurlong.wezterm", "Wezterm"],
|
||||
["com.raggesilver.BlackBox", "BlackBox"],
|
||||
["firefox", "Firefox"],
|
||||
["org.gnome.Nautilus", "Files"],
|
||||
["libreoffice-writer", "Writer"],
|
||||
["", "Desktop"],
|
||||
],
|
||||
},
|
||||
};
|
||||
60
modules/home-manager/desktops/hyprland/ags/js/osd/OSD.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import FontIcon from "../misc/FontIcon.js";
|
||||
import Progress from "../misc/Progress.js";
|
||||
import Indicator from "../services/onScreenIndicator.js";
|
||||
|
||||
export const OnScreenIndicator = ({ height = 300, width = 48 } = {}) =>
|
||||
Widget.Box({
|
||||
class_name: "indicator",
|
||||
css: "padding: 1px;",
|
||||
child: Widget.Revealer({
|
||||
transition: "slide_left",
|
||||
setup: (self) =>
|
||||
self.hook(Indicator, (_, value) => {
|
||||
self.reveal_child = value > -1;
|
||||
}),
|
||||
child: Progress({
|
||||
width,
|
||||
height,
|
||||
vertical: true,
|
||||
setup: (self) =>
|
||||
self.hook(Indicator, (_, value) => self.attribute(value)),
|
||||
child: Widget.Stack({
|
||||
vpack: "start",
|
||||
hpack: "center",
|
||||
hexpand: false,
|
||||
children: {
|
||||
true: Widget.Icon({
|
||||
hpack: "center",
|
||||
size: width,
|
||||
setup: (w) =>
|
||||
w.hook(Indicator, (_, _v, name) => (w.icon = name || "")),
|
||||
}),
|
||||
false: FontIcon({
|
||||
hpack: "center",
|
||||
hexpand: true,
|
||||
css: `font-size: ${width}px;`,
|
||||
setup: (w) =>
|
||||
w.hook(Indicator, (_, _v, name) => (w.label = name || "")),
|
||||
}),
|
||||
},
|
||||
setup: (self) =>
|
||||
self.hook(Indicator, (_, _v, name) => {
|
||||
self.shown = `${!!Utils.lookUpIcon(name)}`;
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
/** @param {number} monitor */
|
||||
export default (monitor) =>
|
||||
Widget.Window({
|
||||
name: `indicator${monitor}`,
|
||||
monitor,
|
||||
class_name: "indicator",
|
||||
layer: "overlay",
|
||||
anchor: ["right"],
|
||||
child: OnScreenIndicator(),
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import { createSurfaceFromWidget, substitute } from "../utils.js";
|
||||
import Gdk from "gi://Gdk";
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import options from "../options.js";
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import icons from "../icons.js";
|
||||
|
||||
const SCALE = 0.08;
|
||||
const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)];
|
||||
|
||||
/** @param {string} args */
|
||||
const dispatch = (args) => Hyprland.sendMessage(`dispatch ${args}`);
|
||||
|
||||
/** @param {string} str */
|
||||
const icon = (str) => {
|
||||
const icon = substitute(options.substitutions.icons, str);
|
||||
if (Utils.lookUpIcon(icon)) return icon;
|
||||
|
||||
console.warn("no icon", icon);
|
||||
return icons.fallback.executable;
|
||||
};
|
||||
|
||||
export default ({ address, size: [w, h], class: c, title }) =>
|
||||
Widget.Button({
|
||||
class_name: "client",
|
||||
tooltip_text: `${title}`,
|
||||
child: Widget.Icon({
|
||||
css: `
|
||||
min-width: ${w * SCALE}px;
|
||||
min-height: ${h * SCALE}px;
|
||||
`,
|
||||
icon: icon(c),
|
||||
}),
|
||||
on_secondary_click: () => dispatch(`closewindow address:${address}`),
|
||||
on_clicked: () =>
|
||||
dispatch(`focuswindow address:${address}`).then(() =>
|
||||
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,
|
||||
),
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import PopupWindow from "../misc/PopupWindow.js";
|
||||
import Workspace from "./Workspace.js";
|
||||
import options from "../options.js";
|
||||
import { range } from "../utils.js";
|
||||
|
||||
const ws = options.workspaces;
|
||||
|
||||
const Overview = () =>
|
||||
Widget.Box({
|
||||
children: [Workspace(0)], // for type infer
|
||||
setup: (self) =>
|
||||
Utils.idle(() => {
|
||||
self.hook(ws, () => {
|
||||
self.children = range(ws.value).map(Workspace);
|
||||
update(self);
|
||||
children(self);
|
||||
});
|
||||
self.hook(Hyprland, update);
|
||||
self.hook(Hyprland, children, "notify::workspaces");
|
||||
update(self);
|
||||
children(self);
|
||||
}),
|
||||
});
|
||||
|
||||
/** @param {ReturnType<typeof Overview>} box */
|
||||
const update = (box) => {
|
||||
if (!box.get_parent()?.visible) return;
|
||||
|
||||
Hyprland.sendMessage("j/clients")
|
||||
.then((clients) => {
|
||||
box.children.forEach((ws) => {
|
||||
ws.attribute(JSON.parse(clients));
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
/** @param {import('types/widgets/box').default} box */
|
||||
const children = (box) => {
|
||||
if (ws.value === 0) {
|
||||
box.children = Hyprland.workspaces
|
||||
.sort((ws1, ws2) => ws1.id - ws2.id)
|
||||
.map(({ id }) => Workspace(id));
|
||||
}
|
||||
};
|
||||
|
||||
export default () =>
|
||||
PopupWindow({
|
||||
name: "overview",
|
||||
child: Overview(),
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Gdk from "gi://Gdk";
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
import Client from "./Client.js";
|
||||
|
||||
const SCALE = 0.08;
|
||||
const TARGET = [Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.SAME_APP, 0)];
|
||||
|
||||
/** @param {string} args */
|
||||
const dispatch = (args) => Utils.execAsync(`hyprctl dispatch ${args}`);
|
||||
|
||||
/** @param {number} index */
|
||||
export default (index) => {
|
||||
const fixed = Gtk.Fixed.new();
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "workspace",
|
||||
vpack: "center",
|
||||
css: `
|
||||
min-width: ${3840 * SCALE}px;
|
||||
min-height: ${2160 * SCALE}px;
|
||||
`,
|
||||
setup: (box) =>
|
||||
box.hook(Hyprland, () => {
|
||||
box.toggleClassName("active", Hyprland.active.workspace.id === index);
|
||||
}),
|
||||
child: Widget.EventBox({
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
on_primary_click: () => dispatch(`workspace ${index}`),
|
||||
setup: (eventbox) => {
|
||||
eventbox.drag_dest_set(
|
||||
Gtk.DestDefaults.ALL,
|
||||
TARGET,
|
||||
Gdk.DragAction.COPY,
|
||||
);
|
||||
eventbox.connect("drag-data-received", (_w, _c, _x, _y, data) => {
|
||||
dispatch(`movetoworkspacesilent ${index},address:${data.get_text()}`);
|
||||
});
|
||||
},
|
||||
child: fixed,
|
||||
}),
|
||||
|
||||
/** @param {Array<import('types/service/hyprland').Client>} clients */
|
||||
attribute: (clients) => {
|
||||
fixed.get_children().forEach((ch) => ch.destroy());
|
||||
clients
|
||||
.filter(({ workspace: { id } }) => id === index)
|
||||
.forEach((c) => {
|
||||
c.at[0] -= Hyprland.getMonitor(c.monitor)?.x || 0;
|
||||
c.at[1] -= Hyprland.getMonitor(c.monitor)?.y || 0;
|
||||
c.mapped && fixed.put(Client(c), c.at[0] * SCALE, c.at[1] * SCALE);
|
||||
});
|
||||
|
||||
fixed.show_all();
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import icons from "../icons.js";
|
||||
import PowerMenu from "../services/powermenu.js";
|
||||
import ShadedPopup from "./ShadedPopup.js";
|
||||
|
||||
/**
|
||||
* @param {'sleep' | 'reboot' | 'logout' | 'shutdown'} action
|
||||
* @param {string} label
|
||||
*/
|
||||
const SysButton = (action, label) =>
|
||||
Widget.Button({
|
||||
on_clicked: () => PowerMenu.action(action),
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
children: [Widget.Icon(icons.powermenu[action]), Widget.Label(label)],
|
||||
}),
|
||||
});
|
||||
|
||||
export default () =>
|
||||
ShadedPopup({
|
||||
name: "powermenu",
|
||||
expand: true,
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
SysButton("sleep", "Sleep"),
|
||||
SysButton("reboot", "Reboot"),
|
||||
SysButton("logout", "Log Out"),
|
||||
SysButton("shutdown", "Shutdown"),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
|
||||
/** @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)),
|
||||
});
|
||||
|
||||
/**
|
||||
* @template {import('gi://Gtk?version=3.0').default.Widget} T
|
||||
* @param {import('types/widgets/window').WindowProps<T> & {
|
||||
* name: string
|
||||
* child: import('types/widgets/box').default
|
||||
* }} o
|
||||
*/
|
||||
export default ({ name, child, ...rest }) =>
|
||||
Widget.Window({
|
||||
...rest,
|
||||
class_names: ["popup-window", name],
|
||||
name,
|
||||
visible: false,
|
||||
popup: true,
|
||||
keymode: "on-demand",
|
||||
setup() {
|
||||
child.toggleClassName("window-content");
|
||||
},
|
||||
child: Widget.CenterBox({
|
||||
class_name: "shader",
|
||||
css: "min-width: 5000px; min-height: 3000px;",
|
||||
start_widget: Padding(name),
|
||||
end_widget: Padding(name),
|
||||
center_widget: Widget.CenterBox({
|
||||
vertical: true,
|
||||
start_widget: Padding(name),
|
||||
end_widget: Padding(name),
|
||||
center_widget: child,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import PowerMenu from "../services/powermenu.js";
|
||||
import ShadedPopup from "./ShadedPopup.js";
|
||||
|
||||
export default () =>
|
||||
ShadedPopup({
|
||||
name: "verification",
|
||||
expand: true,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "text-box",
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "title",
|
||||
label: PowerMenu.bind("title"),
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "desc",
|
||||
label: "Are you sure?",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "buttons horizontal",
|
||||
vexpand: true,
|
||||
vpack: "end",
|
||||
homogeneous: true,
|
||||
children: [
|
||||
Widget.Button({
|
||||
child: Widget.Label("No"),
|
||||
on_clicked: () => App.toggleWindow("verification"),
|
||||
}),
|
||||
Widget.Button({
|
||||
child: Widget.Label("Yes"),
|
||||
on_clicked: () => Utils.exec(PowerMenu.cmd),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Header from "./widgets/Header.js";
|
||||
import PopupWindow from "../misc/PopupWindow.js";
|
||||
import { Volume, Microhone, SinkSelector, AppMixer } from "./widgets/Volume.js";
|
||||
import { NetworkToggle, WifiSelection } from "./widgets/Network.js";
|
||||
import { BluetoothToggle, BluetoothDevices } from "./widgets/Bluetooth.js";
|
||||
import { ThemeToggle, ThemeSelector } from "./widgets/Theme.js";
|
||||
import { ProfileToggle, ProfileSelector } from "./widgets/AsusProfile.js";
|
||||
import Media from "./widgets/Media.js";
|
||||
import Brightness from "./widgets/Brightness.js";
|
||||
import DND from "./widgets/DND.js";
|
||||
import MicMute from "./widgets/MicMute.js";
|
||||
import options from "../options.js";
|
||||
|
||||
const Row = (toggles = [], menus = []) =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "row horizontal",
|
||||
children: toggles,
|
||||
}),
|
||||
...menus,
|
||||
],
|
||||
});
|
||||
|
||||
const Homogeneous = (toggles) =>
|
||||
Widget.Box({
|
||||
homogeneous: true,
|
||||
children: toggles,
|
||||
});
|
||||
|
||||
export default () =>
|
||||
PopupWindow({
|
||||
name: "quicksettings",
|
||||
setup: (self) =>
|
||||
self.hook(options.bar.position, () => {
|
||||
self.anchor = ["right", options.bar.position.value];
|
||||
if (options.bar.position.value === "top")
|
||||
self.transition = "slide_down";
|
||||
|
||||
if (options.bar.position.value === "bottom")
|
||||
self.transition = "slide_up";
|
||||
}),
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Header(),
|
||||
Widget.Box({
|
||||
class_name: "sliders-box vertical",
|
||||
vertical: true,
|
||||
children: [
|
||||
Row([Volume()], [SinkSelector(), AppMixer()]),
|
||||
Microhone(),
|
||||
Brightness(),
|
||||
],
|
||||
}),
|
||||
Row(
|
||||
[Homogeneous([ThemeToggle(), BluetoothToggle()]), MicMute()],
|
||||
[ThemeSelector(), BluetoothDevices()],
|
||||
),
|
||||
Media(),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,134 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Variable from "resource:///com/github/Aylur/ags/variable.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import icons from "../icons.js";
|
||||
|
||||
/** name of the currently opened menu */
|
||||
export const opened = Variable("");
|
||||
App.connect("window-toggled", (_, name, visible) => {
|
||||
if (name === "quicksettings" && !visible)
|
||||
Utils.timeout(500, () => (opened.value = ""));
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} name - menu name
|
||||
* @param {(() => void) | false=} activate
|
||||
*/
|
||||
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,
|
||||
on_clicked: () => {
|
||||
opened.value = opened.value === name ? "" : name;
|
||||
if (typeof activate === "function") activate();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} o
|
||||
* @param {string} o.name - menu name
|
||||
* @param {import('gi://Gtk').Gtk.Widget} o.icon
|
||||
* @param {import('gi://Gtk').Gtk.Widget} o.label
|
||||
* @param {() => void} o.activate
|
||||
* @param {() => void} o.deactivate
|
||||
* @param {boolean=} o.activateOnArrow
|
||||
* @param {[import('gi://GObject').GObject.Object, () => boolean]} o.connection
|
||||
*/
|
||||
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,
|
||||
class_name: "label-box horizontal",
|
||||
children: [icon, label],
|
||||
}),
|
||||
on_clicked: () => {
|
||||
if (condition()) {
|
||||
deactivate();
|
||||
if (opened.value === name) opened.value = "";
|
||||
} else {
|
||||
activate();
|
||||
}
|
||||
},
|
||||
}),
|
||||
Arrow(name, activateOnArrow && activate),
|
||||
],
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {Object} o
|
||||
* @param {string} o.name - menu name
|
||||
* @param {import('gi://Gtk').Gtk.Widget} o.icon
|
||||
* @param {import('gi://Gtk').Gtk.Widget} o.title
|
||||
* @param {import('gi://Gtk').Gtk.Widget[]} o.content
|
||||
*/
|
||||
export const Menu = ({ name, icon, title, content }) =>
|
||||
Widget.Revealer({
|
||||
transition: "slide_down",
|
||||
reveal_child: opened.bind().transform((v) => v === name),
|
||||
child: Widget.Box({
|
||||
class_names: ["menu", name],
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "title horizontal",
|
||||
children: [icon, title],
|
||||
}),
|
||||
Widget.Separator(),
|
||||
...content,
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {Object} o
|
||||
* @param {import('gi://Gtk').Gtk.Widget} o.icon
|
||||
* @param {() => void} o.toggle
|
||||
* @param {[import('gi://GObject').GObject.Object, () => boolean]} o.connection
|
||||
*/
|
||||
export const SimpleToggleButton = ({
|
||||
icon,
|
||||
toggle,
|
||||
connection: [service, condition],
|
||||
}) =>
|
||||
Widget.Button({
|
||||
class_name: "simple-toggle",
|
||||
setup: (self) =>
|
||||
self.hook(service, () => {
|
||||
self.toggleClassName("active", condition());
|
||||
}),
|
||||
child: icon,
|
||||
on_clicked: toggle,
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import icons from "../../icons.js";
|
||||
import Asusctl from "../../services/asusctl.js";
|
||||
import { ArrowToggleButton, Menu } from "../ToggleButton.js";
|
||||
|
||||
export const ProfileToggle = () =>
|
||||
ArrowToggleButton({
|
||||
name: "asusctl-profile",
|
||||
icon: Widget.Icon({
|
||||
icon: Asusctl.bind("profile").transform((p) => icons.asusctl.profile[p]),
|
||||
}),
|
||||
label: Widget.Label({
|
||||
label: Asusctl.bind("profile"),
|
||||
}),
|
||||
connection: [Asusctl, () => Asusctl.profile !== "Balanced"],
|
||||
activate: () => Asusctl.setProfile("Quiet"),
|
||||
deactivate: () => Asusctl.setProfile("Balanced"),
|
||||
activateOnArrow: false,
|
||||
});
|
||||
|
||||
export const ProfileSelector = () =>
|
||||
Menu({
|
||||
name: "asusctl-profile",
|
||||
icon: Widget.Icon({
|
||||
icon: Asusctl.bind("profile").transform((p) => icons.asusctl.profile[p]),
|
||||
}),
|
||||
title: Widget.Label("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"),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
import Bluetooth from "resource:///com/github/Aylur/ags/service/bluetooth.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import icons from "../../icons.js";
|
||||
import { Menu, ArrowToggleButton } from "../ToggleButton.js";
|
||||
|
||||
export const BluetoothToggle = () =>
|
||||
ArrowToggleButton({
|
||||
name: "bluetooth",
|
||||
icon: Widget.Icon({
|
||||
icon: Bluetooth.bind("enabled").transform(
|
||||
(p) => icons.bluetooth[p ? "enabled" : "disabled"],
|
||||
),
|
||||
}),
|
||||
label: Widget.Label({
|
||||
truncate: "end",
|
||||
setup: (self) =>
|
||||
self.hook(Bluetooth, () => {
|
||||
if (!Bluetooth.enabled) return (self.label = "Disabled");
|
||||
|
||||
if (Bluetooth.connected_devices.length === 0)
|
||||
return (self.label = "Not Connected");
|
||||
|
||||
if (Bluetooth.connected_devices.length === 1)
|
||||
return (self.label = Bluetooth.connected_devices[0].alias);
|
||||
|
||||
self.label = `${Bluetooth.connected_devices.length} Connected`;
|
||||
}),
|
||||
}),
|
||||
connection: [Bluetooth, () => Bluetooth.enabled],
|
||||
deactivate: () => (Bluetooth.enabled = false),
|
||||
activate: () => (Bluetooth.enabled = true),
|
||||
});
|
||||
|
||||
/** @param {import('types/service/bluetooth').BluetoothDevice} device */
|
||||
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").transform((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").transform((p) => !p),
|
||||
setup: (self) =>
|
||||
self.on("notify::active", () => {
|
||||
device.setConnection(self.active);
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const BluetoothDevices = () =>
|
||||
Menu({
|
||||
name: "bluetooth",
|
||||
icon: Widget.Icon(icons.bluetooth.disabled),
|
||||
title: Widget.Label("Bluetooth"),
|
||||
content: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: Bluetooth.bind("devices").transform((ds) =>
|
||||
ds.filter((d) => d.name).map(DeviceItem),
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import icons from "../../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 default () =>
|
||||
Widget.Box({
|
||||
children: [
|
||||
Widget.Button({
|
||||
child: Widget.Icon(icons.brightness.indicator),
|
||||
tooltip_text: Brightness.bind("screen").transform(
|
||||
(v) => `Screen Brightness: ${Math.floor(v * 100)}%`,
|
||||
),
|
||||
}),
|
||||
BrightnessSlider(),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
|
||||
import icons from "../../icons.js";
|
||||
import { SimpleToggleButton } from "../ToggleButton.js";
|
||||
|
||||
export default () =>
|
||||
SimpleToggleButton({
|
||||
icon: Widget.Icon({
|
||||
icon: Notifications.bind("dnd").transform(
|
||||
(dnd) => icons.notifications[dnd ? "silent" : "noisy"],
|
||||
),
|
||||
}),
|
||||
toggle: () => (Notifications.dnd = !Notifications.dnd),
|
||||
connection: [Notifications, () => Notifications.dnd],
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Battery from "resource:///com/github/Aylur/ags/service/battery.js";
|
||||
import PowerMenu from "../../services/powermenu.js";
|
||||
import Lockscreen from "../../services/lockscreen.js";
|
||||
import Avatar from "../../misc/Avatar.js";
|
||||
import icons from "../../icons.js";
|
||||
import { openSettings } from "../../settings/theme.js";
|
||||
import { uptime } from "../../variables.js";
|
||||
import DND from "./DND.js";
|
||||
|
||||
export default () =>
|
||||
Widget.Box({
|
||||
class_name: "header horizontal",
|
||||
children: [
|
||||
Avatar(),
|
||||
Widget.Box({
|
||||
hpack: "end",
|
||||
vpack: "center",
|
||||
hexpand: true,
|
||||
children: [
|
||||
/*Widget.Box({
|
||||
class_name: "battery horizontal",
|
||||
children: [
|
||||
Widget.Icon({ icon: Battery.bind("icon_name") }),
|
||||
Widget.Label({
|
||||
label: Battery.bind("percent").transform((p) => `${p}%`),
|
||||
}),
|
||||
],
|
||||
}),*/
|
||||
DND(),
|
||||
Widget.Label({
|
||||
class_name: "uptime",
|
||||
label: uptime.bind().transform((v) => `up: ${v}`),
|
||||
}),
|
||||
Widget.Button({
|
||||
on_clicked: openSettings,
|
||||
child: Widget.Icon(icons.ui.settings),
|
||||
}),
|
||||
Widget.Button({
|
||||
on_clicked: () => Lockscreen.lockscreen(),
|
||||
child: Widget.Icon(icons.lock),
|
||||
}),
|
||||
Widget.Button({
|
||||
on_clicked: () => PowerMenu.action("shutdown"),
|
||||
child: Widget.Icon(icons.powermenu.shutdown),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
import Mpris from "resource:///com/github/Aylur/ags/service/mpris.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as mpris from "../../misc/mpris.js";
|
||||
import options from "../../options.js";
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
const Footer = (player) =>
|
||||
Widget.CenterBox({
|
||||
class_name: "footer-box",
|
||||
start_widget: Widget.Box({
|
||||
class_name: "position",
|
||||
children: [
|
||||
mpris.PositionLabel(player),
|
||||
mpris.Slash(player),
|
||||
mpris.LengthLabel(player),
|
||||
],
|
||||
}),
|
||||
center_widget: Widget.Box({
|
||||
class_name: "controls",
|
||||
children: [
|
||||
mpris.ShuffleButton(player),
|
||||
mpris.PreviousButton(player),
|
||||
mpris.PlayPauseButton(player),
|
||||
mpris.NextButton(player),
|
||||
mpris.LoopButton(player),
|
||||
],
|
||||
}),
|
||||
end_widget: mpris.PlayerIcon(player, {
|
||||
symbolic: false,
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
}),
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
const TextBox = (player) =>
|
||||
Widget.Box({
|
||||
children: [
|
||||
mpris.CoverArt(player, {
|
||||
hpack: "end",
|
||||
hexpand: false,
|
||||
}),
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
class_name: "labels",
|
||||
children: [
|
||||
mpris.TitleLabel(player, {
|
||||
xalign: 0,
|
||||
justification: "left",
|
||||
wrap: true,
|
||||
}),
|
||||
mpris.ArtistLabel(player, {
|
||||
xalign: 0,
|
||||
justification: "left",
|
||||
wrap: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||
const PlayerBox = (player) =>
|
||||
Widget.Box({
|
||||
class_name: `player ${player.name}`,
|
||||
child: mpris.BlurredCoverArt(player, {
|
||||
hexpand: true,
|
||||
child: Widget.Box({
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
TextBox(player),
|
||||
mpris.PositionSlider(player),
|
||||
Footer(player),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export default () =>
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
class_name: "media vertical",
|
||||
visible: Mpris.bind("players").transform((p) => p.length > 0),
|
||||
children: Mpris.bind("players").transform((ps) =>
|
||||
ps
|
||||
.filter((p) => !options.mpris.black_list.value.includes(p.identity))
|
||||
.map(PlayerBox),
|
||||
),
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import Audio from "resource:///com/github/Aylur/ags/service/audio.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import icons from "../../icons.js";
|
||||
import { SimpleToggleButton } from "../ToggleButton.js";
|
||||
|
||||
export default () =>
|
||||
SimpleToggleButton({
|
||||
icon: Widget.Icon().hook(
|
||||
Audio,
|
||||
(self) => {
|
||||
self.icon = Audio.microphone?.is_muted
|
||||
? icons.audio.mic.muted
|
||||
: icons.audio.mic.high;
|
||||
},
|
||||
"microphone-changed",
|
||||
),
|
||||
toggle: () => (Audio.microphone.is_muted = !Audio.microphone.is_muted),
|
||||
connection: [Audio, () => Audio.microphone?.is_muted || false],
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Network from "resource:///com/github/Aylur/ags/service/network.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import icons from "../../icons.js";
|
||||
import { Menu, ArrowToggleButton } from "../ToggleButton.js";
|
||||
import Applications from "resource:///com/github/Aylur/ags/service/applications.js";
|
||||
|
||||
export const NetworkToggle = () =>
|
||||
ArrowToggleButton({
|
||||
name: "network",
|
||||
icon: Widget.Icon({
|
||||
icon: Network.wifi.bind("icon_name"),
|
||||
}),
|
||||
label: Widget.Label({
|
||||
truncate: "end",
|
||||
label: Network.wifi
|
||||
.bind("ssid")
|
||||
.transform((ssid) => ssid || "Not Connected"),
|
||||
}),
|
||||
connection: [Network, () => Network.wifi.enabled],
|
||||
deactivate: () => (Network.wifi.enabled = false),
|
||||
activate: () => {
|
||||
Network.wifi.enabled = true;
|
||||
Network.wifi.scan();
|
||||
},
|
||||
});
|
||||
|
||||
export const WifiSelection = () =>
|
||||
Menu({
|
||||
name: "network",
|
||||
icon: Widget.Icon({
|
||||
icon: Network.wifi.bind("icon_name"),
|
||||
}),
|
||||
title: Widget.Label("Wifi Selection"),
|
||||
content: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
setup: (self) =>
|
||||
self.hook(
|
||||
Network,
|
||||
() =>
|
||||
(self.children = Network.wifi?.access_points.map((ap) =>
|
||||
Widget.Button({
|
||||
on_clicked: () =>
|
||||
Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon(ap.iconName),
|
||||
Widget.Label(ap.ssid || ""),
|
||||
ap.active &&
|
||||
Widget.Icon({
|
||||
icon: icons.ui.tick,
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
)),
|
||||
),
|
||||
}),
|
||||
Widget.Separator(),
|
||||
Widget.Button({
|
||||
on_clicked: () =>
|
||||
Applications.query("gnome-control-center")?.[0].launch(),
|
||||
child: Widget.Box({
|
||||
children: [Widget.Icon(icons.ui.settings), Widget.Label("Network")],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import { ArrowToggleButton, Menu, opened } from "../ToggleButton.js";
|
||||
import themes from "../../themes.js";
|
||||
import icons from "../../icons.js";
|
||||
import options from "../../options.js";
|
||||
import { setTheme, openSettings } from "../../settings/theme.js";
|
||||
|
||||
export const ThemeToggle = () =>
|
||||
ArrowToggleButton({
|
||||
name: "theme",
|
||||
icon: Widget.Label().bind("label", options.theme.icon),
|
||||
label: Widget.Label().bind("label", options.theme.name),
|
||||
connection: [opened, () => opened.value === "theme"],
|
||||
activate: () => opened.setValue("theme"),
|
||||
activateOnArrow: false,
|
||||
deactivate: () => {},
|
||||
});
|
||||
|
||||
export const ThemeSelector = () =>
|
||||
Menu({
|
||||
name: "theme",
|
||||
icon: Widget.Label().bind("label", options.theme.icon),
|
||||
title: Widget.Label("Theme Selector"),
|
||||
content: [
|
||||
...themes.map(({ name, icon }) =>
|
||||
Widget.Button({
|
||||
on_clicked: () => setTheme(name),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Label(icon),
|
||||
Widget.Label(name),
|
||||
Widget.Icon({
|
||||
icon: icons.ui.tick,
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
visible: options.theme.name
|
||||
.bind("value")
|
||||
.transform((v) => v === name),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
),
|
||||
Widget.Separator(),
|
||||
Widget.Button({
|
||||
on_clicked: openSettings,
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon(icons.ui.settings),
|
||||
Widget.Label("Theme Settings"),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,158 @@
|
||||
import Audio from "resource:///com/github/Aylur/ags/service/audio.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import icons from "../../icons.js";
|
||||
import FontIcon from "../../misc/FontIcon.js";
|
||||
import { getAudioTypeIcon } from "../../utils.js";
|
||||
import { Arrow } from "../ToggleButton.js";
|
||||
import { Menu } from "../ToggleButton.js";
|
||||
|
||||
/** @param {'speaker' | 'microphone'=} type */
|
||||
const VolumeIndicator = (type = "speaker") =>
|
||||
Widget.Button({
|
||||
on_clicked: () => (Audio[type].is_muted = !Audio[type].is_muted),
|
||||
child: Widget.Icon().hook(Audio[type], (icon) => {
|
||||
icon.icon =
|
||||
type === "speaker"
|
||||
? getAudioTypeIcon(Audio[type].icon_name || "")
|
||||
: icons.audio.mic.high;
|
||||
|
||||
icon.tooltip_text = `Volume ${Math.floor(Audio[type].volume * 100)}%`;
|
||||
}),
|
||||
});
|
||||
|
||||
/** @param {'speaker' | 'microphone'=} type */
|
||||
const VolumeSlider = (type = "speaker") =>
|
||||
Widget.Slider({
|
||||
hexpand: true,
|
||||
draw_value: false,
|
||||
on_change: ({ value }) => (Audio[type].volume = value),
|
||||
setup: (self) =>
|
||||
self.hook(Audio[type], () => {
|
||||
self.value = Audio[type].volume || 0;
|
||||
}),
|
||||
});
|
||||
|
||||
export const Volume = () =>
|
||||
Widget.Box({
|
||||
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").transform((a) => a.length > 0),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const Microhone = () =>
|
||||
Widget.Box({
|
||||
class_name: "slider horizontal",
|
||||
visible: Audio.bind("recorders").transform((a) => a.length > 0),
|
||||
children: [VolumeIndicator("microphone"), VolumeSlider("microphone")],
|
||||
});
|
||||
|
||||
/** @param {import('types/service/audio').Stream} stream */
|
||||
const MixerItem = (stream) =>
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
class_name: "mixer-item horizontal",
|
||||
children: [
|
||||
Widget.Icon({
|
||||
tooltip_text: stream.bind("name").transform((n) => n || ""),
|
||||
icon: stream.bind("name").transform((n) => {
|
||||
return Utils.lookUpIcon(n || "") ? n || "" : icons.mpris.fallback;
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
xalign: 0,
|
||||
truncate: "end",
|
||||
label: stream.bind("description").transform((d) => d || ""),
|
||||
}),
|
||||
Widget.Slider({
|
||||
hexpand: true,
|
||||
draw_value: false,
|
||||
value: stream.bind("volume"),
|
||||
on_change: ({ value }) => (stream.volume = value),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Label({
|
||||
xalign: 1,
|
||||
label: stream.bind("volume").transform((v) => `${Math.floor(v * 100)}`),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
/** @param {import('types/service/audio').Stream} stream */
|
||||
const SinkItem = (stream) =>
|
||||
Widget.Button({
|
||||
hexpand: true,
|
||||
on_clicked: () => (Audio.speaker = stream),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon({
|
||||
icon: getAudioTypeIcon(stream.icon_name || ""),
|
||||
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")
|
||||
.transform((s) => s === stream.stream),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const SettingsButton = () =>
|
||||
Widget.Button({
|
||||
on_clicked: () => Utils.execAsync("pavucontrol"),
|
||||
hexpand: true,
|
||||
child: Widget.Box({
|
||||
children: [Widget.Icon(icons.ui.settings), Widget.Label("Settings")],
|
||||
}),
|
||||
});
|
||||
|
||||
export const AppMixer = () =>
|
||||
Menu({
|
||||
name: "app-mixer",
|
||||
icon: FontIcon(icons.audio.mixer),
|
||||
title: Widget.Label("App Mixer"),
|
||||
content: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: Audio.bind("apps").transform((a) => a.map(MixerItem)),
|
||||
}),
|
||||
Widget.Separator(),
|
||||
SettingsButton(),
|
||||
],
|
||||
});
|
||||
|
||||
export const SinkSelector = () =>
|
||||
Menu({
|
||||
name: "sink-selector",
|
||||
icon: Widget.Icon(icons.audio.type.headset),
|
||||
title: Widget.Label("Sink Selector"),
|
||||
content: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: Audio.bind("speakers").transform((a) => a.map(SinkItem)),
|
||||
}),
|
||||
Widget.Separator(),
|
||||
SettingsButton(),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import Gtk from "gi://Gtk";
|
||||
import options from "../options.js";
|
||||
|
||||
/** @param {'topleft' | 'topright' | 'bottomleft' | 'bottomright'} place */
|
||||
const Corner = (place) =>
|
||||
Widget.DrawingArea({
|
||||
class_name: "corner",
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
hpack: place.includes("left") ? "start" : "end",
|
||||
vpack: place.includes("top") ? "start" : "end",
|
||||
setup: (self) =>
|
||||
self
|
||||
.hook(options.radii, () => {
|
||||
const r = options.radii.value * 2;
|
||||
self.set_size_request(r, r);
|
||||
})
|
||||
.connect("draw", (self, cr) => {
|
||||
const context = self.get_style_context();
|
||||
const c = context.get_property(
|
||||
"background-color",
|
||||
Gtk.StateFlags.NORMAL,
|
||||
);
|
||||
const r = context.get_property(
|
||||
"border-radius",
|
||||
Gtk.StateFlags.NORMAL,
|
||||
);
|
||||
|
||||
switch (place) {
|
||||
case "topleft":
|
||||
cr.arc(r, r, r, Math.PI, (3 * Math.PI) / 2);
|
||||
cr.lineTo(0, 0);
|
||||
break;
|
||||
|
||||
case "topright":
|
||||
cr.arc(0, r, r, (3 * Math.PI) / 2, 2 * Math.PI);
|
||||
cr.lineTo(r, 0);
|
||||
break;
|
||||
|
||||
case "bottomleft":
|
||||
cr.arc(r, 0, r, Math.PI / 2, Math.PI);
|
||||
cr.lineTo(0, r);
|
||||
break;
|
||||
|
||||
case "bottomright":
|
||||
cr.arc(0, 0, r, 0, Math.PI / 2);
|
||||
cr.lineTo(r, r);
|
||||
break;
|
||||
}
|
||||
|
||||
cr.closePath();
|
||||
cr.setSourceRGBA(c.red, c.green, c.blue, c.alpha);
|
||||
cr.fill();
|
||||
}),
|
||||
});
|
||||
|
||||
/** @type {Array<'topleft' | 'topright' | 'bottomleft' | 'bottomright'>} */
|
||||
const places = ["topleft", "topright", "bottomleft", "bottomright"];
|
||||
|
||||
/** @param {number} monitor */
|
||||
export default (monitor) =>
|
||||
places.map((place) =>
|
||||
Widget.Window({
|
||||
name: `corner${monitor}${place}`,
|
||||
monitor,
|
||||
class_name: "corner",
|
||||
anchor: [
|
||||
place.includes("top") ? "top" : "bottom",
|
||||
place.includes("right") ? "right" : "left",
|
||||
],
|
||||
visible: options.desktop.screen_corners.bind("value"),
|
||||
child: Corner(place),
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,70 @@
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||
|
||||
class Asusctl extends Service {
|
||||
static {
|
||||
Service.register(
|
||||
this,
|
||||
{},
|
||||
{
|
||||
profile: ["string", "r"],
|
||||
mode: ["string", "r"],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
profiles = /** @type {const} */ (["Performance", "Balanced", "Quiet"]);
|
||||
#profile = "Balanced";
|
||||
#mode = "Hyprid";
|
||||
|
||||
nextProfile() {
|
||||
Utils.execAsync("asusctl profile -n")
|
||||
.then(() => {
|
||||
this.#profile = Utils.exec("asusctl profile -p").split(" ")[3];
|
||||
this.changed("profile");
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
/** @param {'Performance' | 'Balanced' | 'Quiet'} prof */
|
||||
setProfile(prof) {
|
||||
Utils.execAsync(`asusctl profile --profile-set ${prof}`)
|
||||
.then(() => {
|
||||
this.#profile = prof;
|
||||
this.changed("profile");
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
nextMode() {
|
||||
Utils.execAsync(
|
||||
`supergfxctl -m ${this.#mode === "Hybrid" ? "Integrated" : "Hybrid"}`,
|
||||
)
|
||||
.then(() => {
|
||||
this.#mode = Utils.exec("supergfxctl -g");
|
||||
this.changed("profile");
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if (Utils.exec("which asusctl")) {
|
||||
this.available = true;
|
||||
this.#profile = Utils.exec("asusctl profile -p").split(" ")[3];
|
||||
Utils.execAsync("supergfxctl -g").then((mode) => (this.#mode = mode));
|
||||
} else {
|
||||
this.available = false;
|
||||
}
|
||||
}
|
||||
|
||||
get profile() {
|
||||
return this.#profile;
|
||||
}
|
||||
get mode() {
|
||||
return this.#mode;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Asusctl();
|
||||
@@ -0,0 +1,77 @@
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||
import options from "../options.js";
|
||||
import { dependencies } from "../utils.js";
|
||||
|
||||
const KBD = options.brightnessctlKBD;
|
||||
|
||||
class Brightness extends Service {
|
||||
static {
|
||||
Service.register(
|
||||
this,
|
||||
{},
|
||||
{
|
||||
screen: ["float", "rw"],
|
||||
kbd: ["int", "rw"],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#kbd = 0;
|
||||
#kbdMax = 3;
|
||||
#screen = 0;
|
||||
|
||||
get kbd() {
|
||||
return this.#kbd;
|
||||
}
|
||||
get screen() {
|
||||
return this.#screen;
|
||||
}
|
||||
|
||||
set kbd(value) {
|
||||
if (!dependencies(["brightnessctl"])) return;
|
||||
|
||||
if (value < 0 || value > this.#kbdMax) return;
|
||||
|
||||
Utils.execAsync(`brightnessctl -d ${KBD} s ${value} -q`)
|
||||
.then(() => {
|
||||
this.#kbd = value;
|
||||
this.changed("kbd");
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
set screen(percent) {
|
||||
if (!dependencies(["gbmonctl"])) return;
|
||||
|
||||
if (percent < 0) percent = 0;
|
||||
|
||||
if (percent > 1) percent = 1;
|
||||
|
||||
Utils.execAsync(
|
||||
`gbmonctl --prop brightness -val ${Math.min(
|
||||
Math.max(Math.floor(percent * 100), 0),
|
||||
100,
|
||||
)}`,
|
||||
)
|
||||
.then(() => {
|
||||
this.#screen = percent;
|
||||
this.changed("screen");
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if (dependencies(["brightnessctl"])) {
|
||||
this.#kbd = Number(Utils.exec(`brightnessctl -d ${KBD} g`));
|
||||
this.#kbdMax = Number(Utils.exec(`brightnessctl -d ${KBD} m`));
|
||||
this.#screen =
|
||||
Number(Utils.exec("brightnessctl g")) /
|
||||
Number(Utils.exec("brightnessctl m"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new Brightness();
|
||||
@@ -0,0 +1,73 @@
|
||||
import { Variable } from "resource:///com/github/Aylur/ags/variable.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||
import { dependencies } from "../utils.js";
|
||||
import icons from "../icons.js";
|
||||
|
||||
const COLORS_CACHE = Utils.CACHE_DIR + "/colorpicker.json";
|
||||
|
||||
class Colors extends Service {
|
||||
static {
|
||||
Service.register(
|
||||
this,
|
||||
{},
|
||||
{
|
||||
colors: ["jsobject"],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** @type {Variable<string[]>} */
|
||||
#colors = new Variable([]);
|
||||
get colors() {
|
||||
return this.#colors.value;
|
||||
}
|
||||
|
||||
#notifID = 0;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.#colors.connect("changed", () => this.changed("colors"));
|
||||
|
||||
Utils.readFileAsync(COLORS_CACHE)
|
||||
.then((out) => this.#colors.setValue(JSON.parse(out || "[]")))
|
||||
.catch(() => print("no colorpicker cache found"));
|
||||
}
|
||||
|
||||
/** @param {string} color */
|
||||
wlCopy(color) {
|
||||
Utils.execAsync(["wl-copy", color]).catch((err) => console.error(err));
|
||||
}
|
||||
|
||||
async pick() {
|
||||
if (!dependencies(["hyprpicker"])) return;
|
||||
|
||||
const color = await Utils.execAsync("hyprpicker");
|
||||
if (!color) return;
|
||||
|
||||
this.wlCopy(color);
|
||||
const list = this.#colors.value;
|
||||
if (!list.includes(color)) {
|
||||
list.push(color);
|
||||
if (list.length > 10) list.shift();
|
||||
|
||||
this.#colors.value = list;
|
||||
Utils.writeFile(JSON.stringify(list, null, 2), COLORS_CACHE).catch(
|
||||
(err) => console.error(err),
|
||||
);
|
||||
}
|
||||
|
||||
const n = await Utils.notify({
|
||||
id: this.#notifID,
|
||||
iconName: icons.ui.colorpicker,
|
||||
summary: color,
|
||||
actions: {
|
||||
Copy: () => this.wlCopy(color),
|
||||
},
|
||||
});
|
||||
this.#notifID = n.id;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Colors();
|
||||
@@ -0,0 +1,30 @@
|
||||
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
const authpy = App.configDir + "/js/lockscreen/auth.py";
|
||||
|
||||
class Lockscreen extends Service {
|
||||
static {
|
||||
Service.register(this, {
|
||||
lock: ["boolean"],
|
||||
authenticating: ["boolean"],
|
||||
});
|
||||
}
|
||||
|
||||
lockscreen() {
|
||||
this.emit("lock", true);
|
||||
}
|
||||
|
||||
/** @param {string} password */
|
||||
auth(password) {
|
||||
this.emit("authenticating", true);
|
||||
Utils.execAsync([authpy, password])
|
||||
.then((out) => {
|
||||
this.emit("lock", out !== "True");
|
||||
this.emit("authenticating", false);
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
}
|
||||
}
|
||||
|
||||
export default new Lockscreen();
|
||||
@@ -0,0 +1,58 @@
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||
import Audio from "resource:///com/github/Aylur/ags/service/audio.js";
|
||||
import icons from "../icons.js";
|
||||
import { getAudioTypeIcon } from "../utils.js";
|
||||
import Brightness from "./brightness.js";
|
||||
|
||||
class Indicator extends Service {
|
||||
static {
|
||||
Service.register(this, {
|
||||
popup: ["double", "string"],
|
||||
});
|
||||
}
|
||||
|
||||
#delay = 1500;
|
||||
#count = 0;
|
||||
|
||||
/**
|
||||
* @param {number} value - 0 < v < 1
|
||||
* @param {string} icon
|
||||
*/
|
||||
popup(value, icon) {
|
||||
this.emit("popup", value, icon);
|
||||
this.#count++;
|
||||
Utils.timeout(this.#delay, () => {
|
||||
this.#count--;
|
||||
|
||||
if (this.#count === 0) this.emit("popup", -1, icon);
|
||||
});
|
||||
}
|
||||
|
||||
speaker() {
|
||||
this.popup(
|
||||
Audio.speaker?.volume || 0,
|
||||
getAudioTypeIcon(Audio.speaker?.icon_name || ""),
|
||||
);
|
||||
}
|
||||
|
||||
display() {
|
||||
// brightness is async, so lets wait a bit
|
||||
Utils.timeout(10, () =>
|
||||
this.popup(Brightness.screen, icons.brightness.screen),
|
||||
);
|
||||
}
|
||||
|
||||
kbd() {
|
||||
// brightness is async, so lets wait a bit
|
||||
Utils.timeout(10, () =>
|
||||
this.popup((Brightness.kbd * 33 + 1) / 100, icons.brightness.keyboard),
|
||||
);
|
||||
}
|
||||
|
||||
connect(event = "popup", callback) {
|
||||
return super.connect(event, callback);
|
||||
}
|
||||
}
|
||||
|
||||
export default new Indicator();
|
||||
@@ -0,0 +1,43 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||
|
||||
class PowerMenu extends Service {
|
||||
static {
|
||||
Service.register(
|
||||
this,
|
||||
{},
|
||||
{
|
||||
title: ["string"],
|
||||
cmd: ["string"],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#title = "";
|
||||
#cmd = "";
|
||||
|
||||
get title() {
|
||||
return this.#title;
|
||||
}
|
||||
get cmd() {
|
||||
return this.#cmd;
|
||||
}
|
||||
|
||||
/** @param {'sleep' | 'reboot' | 'logout' | 'shutdown'} action */
|
||||
action(action) {
|
||||
[this.#cmd, this.#title] = {
|
||||
sleep: ["systemctl suspend", "Sleep"],
|
||||
reboot: ["systemctl reboot", "Reboot"],
|
||||
logout: ["pkill Hyprland", "Log Out"],
|
||||
shutdown: ["shutdown now", "Shutdown"],
|
||||
}[action];
|
||||
|
||||
this.notify("cmd");
|
||||
this.notify("title");
|
||||
this.emit("changed");
|
||||
App.closeWindow("powermenu");
|
||||
App.openWindow("verification");
|
||||
}
|
||||
}
|
||||
|
||||
export default new PowerMenu();
|
||||
@@ -0,0 +1,112 @@
|
||||
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import GLib from "gi://GLib";
|
||||
import { dependencies } from "../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"],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#path = GLib.get_home_dir() + "/Videos/Screencasting";
|
||||
#file = "";
|
||||
#interval = 0;
|
||||
|
||||
recording = false;
|
||||
timer = 0;
|
||||
|
||||
async start() {
|
||||
if (!dependencies(["slurp", "wf-recorder"])) return;
|
||||
|
||||
if (this.recording) return;
|
||||
|
||||
const area = await Utils.execAsync("slurp");
|
||||
Utils.ensureDirectory(this.#path);
|
||||
this.#file = `${this.#path}/${now()}.mp4`;
|
||||
Utils.execAsync(["wf-recorder", "-g", area, "-f", this.#file]);
|
||||
this.recording = true;
|
||||
this.changed("recording");
|
||||
|
||||
this.timer = 0;
|
||||
this.#interval = Utils.interval(1000, () => {
|
||||
this.changed("timer");
|
||||
this.timer++;
|
||||
});
|
||||
}
|
||||
|
||||
async stop() {
|
||||
if (!dependencies(["notify-send"])) return;
|
||||
|
||||
if (!this.recording) return;
|
||||
|
||||
Utils.execAsync("killall -INT wf-recorder");
|
||||
this.recording = false;
|
||||
this.changed("recording");
|
||||
GLib.source_remove(this.#interval);
|
||||
|
||||
const res = await Utils.execAsync([
|
||||
"notify-send",
|
||||
"-A",
|
||||
"files=Show in Files",
|
||||
"-A",
|
||||
"view=View",
|
||||
"-i",
|
||||
"video-x-generic-symbolic",
|
||||
"Screenrecord",
|
||||
this.#file,
|
||||
]);
|
||||
|
||||
if (res === "files") Utils.execAsync("xdg-open " + this.#path);
|
||||
|
||||
if (res === "view") Utils.execAsync("xdg-open " + this.#file);
|
||||
}
|
||||
|
||||
async screenshot(full = false) {
|
||||
if (!dependencies(["slurp", "wayshot"])) return;
|
||||
|
||||
const path = GLib.get_home_dir() + "/Pictures/Screenshots";
|
||||
const file = `${path}/${now()}.png`;
|
||||
Utils.ensureDirectory(path);
|
||||
|
||||
await Utils.execAsync(
|
||||
["wayshot", "-f", file].concat(
|
||||
full ? [] : ["-s", await Utils.execAsync("slurp")],
|
||||
),
|
||||
);
|
||||
|
||||
Utils.execAsync(["bash", "-c", `wl-copy < ${file}`]);
|
||||
|
||||
const res = await Utils.execAsync([
|
||||
"notify-send",
|
||||
"-A",
|
||||
"files=Show in Files",
|
||||
"-A",
|
||||
"view=View",
|
||||
"-A",
|
||||
"edit=Edit",
|
||||
"-i",
|
||||
file,
|
||||
"Screenshot",
|
||||
file,
|
||||
]);
|
||||
if (res === "files") Utils.execAsync("xdg-open " + path);
|
||||
|
||||
if (res === "view") Utils.execAsync("xdg-open " + file);
|
||||
|
||||
if (res === "edit") Utils.execAsync(["swappy", "-f", file]);
|
||||
|
||||
App.closeWindow("dashboard");
|
||||
}
|
||||
}
|
||||
|
||||
export default new Recorder();
|
||||
@@ -0,0 +1,297 @@
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Widget from "resource:///com/github/Aylur/ags/widget.js";
|
||||
import RegularWindow from "../misc/RegularWindow.js";
|
||||
import Variable from "resource:///com/github/Aylur/ags/variable.js";
|
||||
import icons from "../icons.js";
|
||||
import { getOptions, getValues } from "./option.js";
|
||||
import options from "../options.js";
|
||||
|
||||
const optionsList = getOptions();
|
||||
const categories = Array.from(
|
||||
new Set(optionsList.map((opt) => opt.category)),
|
||||
).filter((category) => category !== "exclude");
|
||||
|
||||
const currentPage = Variable(categories[0]);
|
||||
const search = Variable("");
|
||||
const showSearch = Variable(false);
|
||||
showSearch.connect("changed", ({ value }) => {
|
||||
if (!value) search.value = "";
|
||||
});
|
||||
|
||||
/** @param {import('./option.js').Opt<string>} opt */
|
||||
const EnumSetter = (opt) => {
|
||||
const lbl = Widget.Label().bind("label", opt);
|
||||
const step = (dir = 1) => {
|
||||
const i = opt.enums.findIndex((i) => i === lbl.label);
|
||||
opt.setValue(
|
||||
dir > 0
|
||||
? i + dir > opt.enums.length - 1
|
||||
? opt.enums[0]
|
||||
: opt.enums[i + dir]
|
||||
: i + dir < 0
|
||||
? opt.enums[opt.enums.length - 1]
|
||||
: opt.enums[i + dir],
|
||||
true,
|
||||
);
|
||||
};
|
||||
const next = Widget.Button({
|
||||
child: Widget.Icon(icons.ui.arrow.right),
|
||||
on_clicked: () => step(+1),
|
||||
});
|
||||
const prev = Widget.Button({
|
||||
child: Widget.Icon(icons.ui.arrow.left),
|
||||
on_clicked: () => step(-1),
|
||||
});
|
||||
return Widget.Box({
|
||||
class_name: "enum-setter",
|
||||
children: [prev, lbl, next],
|
||||
});
|
||||
};
|
||||
|
||||
/** @param {import('./option.js').Opt} opt */
|
||||
const Setter = (opt) => {
|
||||
switch (opt.type) {
|
||||
case "number":
|
||||
return Widget.SpinButton({
|
||||
setup(self) {
|
||||
self.set_range(0, 1000);
|
||||
self.set_increments(1, 5);
|
||||
self.on("value-changed", () => opt.setValue(self.value, true));
|
||||
self.hook(opt, () => (self.value = opt.value));
|
||||
},
|
||||
});
|
||||
case "float":
|
||||
case "object":
|
||||
return Widget.Entry({
|
||||
on_accept: (self) => opt.setValue(JSON.parse(self.text || ""), true),
|
||||
setup: (self) =>
|
||||
self.hook(opt, () => (self.text = JSON.stringify(opt.value))),
|
||||
});
|
||||
case "string":
|
||||
return Widget.Entry({
|
||||
on_accept: (self) => opt.setValue(self.text, true),
|
||||
setup: (self) => self.hook(opt, () => (self.text = opt.value)),
|
||||
});
|
||||
case "enum":
|
||||
return EnumSetter(opt);
|
||||
case "boolean":
|
||||
return Widget.Switch()
|
||||
.on("notify::active", (self) => opt.setValue(self.active, true))
|
||||
.hook(opt, (self) => (self.active = opt.value));
|
||||
|
||||
case "img":
|
||||
return Widget.FileChooserButton().on("selection-changed", (self) => {
|
||||
opt.setValue(self.get_uri()?.replace("file://", ""), true);
|
||||
});
|
||||
|
||||
case "font":
|
||||
return Widget.FontButton({
|
||||
show_size: false,
|
||||
use_size: false,
|
||||
setup: (self) =>
|
||||
self
|
||||
.on("notify::font", ({ font }) => opt.setValue(font, true))
|
||||
.hook(opt, () => (self.font = opt.value)),
|
||||
});
|
||||
default:
|
||||
return Widget.Label({
|
||||
label: "no setter with type " + opt.type,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** @param {import('./option.js').Opt} opt */
|
||||
const Row = (opt) =>
|
||||
Widget.Box({
|
||||
class_name: "row",
|
||||
attribute: opt,
|
||||
children: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
vpack: "center",
|
||||
children: [
|
||||
opt.title &&
|
||||
Widget.Label({
|
||||
xalign: 0,
|
||||
class_name: "summary",
|
||||
label: opt.title,
|
||||
}),
|
||||
Widget.Label({
|
||||
xalign: 0,
|
||||
class_name: "id",
|
||||
label: `id: "${opt.id}"`,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({ hexpand: true }),
|
||||
Widget.Box({
|
||||
vpack: "center",
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hpack: "end",
|
||||
child: Setter(opt),
|
||||
}),
|
||||
opt.note &&
|
||||
Widget.Label({
|
||||
xalign: 1,
|
||||
class_name: "note",
|
||||
label: opt.note,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
/** @param {string} category */
|
||||
const Page = (category) =>
|
||||
Widget.Scrollable({
|
||||
vexpand: true,
|
||||
class_name: "page",
|
||||
child: Widget.Box({
|
||||
class_name: "page-content vertical",
|
||||
vertical: true,
|
||||
setup: (self) =>
|
||||
self.hook(search, () => {
|
||||
for (const child of self.children) {
|
||||
child.visible =
|
||||
child.attribute.id.includes(search.value) ||
|
||||
child.attribute.title.includes(search.value) ||
|
||||
child.attribute.note.includes(search.value);
|
||||
}
|
||||
}),
|
||||
children: optionsList
|
||||
.filter((opt) => opt.category.includes(category))
|
||||
.map(Row),
|
||||
}),
|
||||
});
|
||||
|
||||
const sidebar = Widget.Revealer({
|
||||
reveal_child: search.bind().transform((v) => !v),
|
||||
transition: "slide_right",
|
||||
child: Widget.Box({
|
||||
hexpand: false,
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "sidebar-header",
|
||||
children: [
|
||||
Widget.Button({
|
||||
hexpand: true,
|
||||
label: icons.dialog.Search + " Search",
|
||||
on_clicked: () => (showSearch.value = !showSearch.value),
|
||||
}),
|
||||
Widget.Button({
|
||||
hpack: "end",
|
||||
child: Widget.Icon(icons.ui.info),
|
||||
on_clicked: () => App.toggleWindow("about"),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Scrollable({
|
||||
vexpand: true,
|
||||
hscroll: "never",
|
||||
child: Widget.Box({
|
||||
class_name: "sidebar-box vertical",
|
||||
vertical: true,
|
||||
children: [
|
||||
...categories.map((name) =>
|
||||
Widget.Button({
|
||||
label: (icons.dialog[name] || "") + " " + name,
|
||||
xalign: 0,
|
||||
class_name: currentPage
|
||||
.bind()
|
||||
.transform((v) => `${v === name ? "active" : ""}`),
|
||||
on_clicked: () => currentPage.setValue(name),
|
||||
}),
|
||||
),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "sidebar-footer",
|
||||
child: Widget.Button({
|
||||
class_name: "copy",
|
||||
child: Widget.Label({
|
||||
label: " Save",
|
||||
xalign: 0,
|
||||
}),
|
||||
hexpand: true,
|
||||
on_clicked: () => {
|
||||
Utils.execAsync(["wl-copy", getValues()]);
|
||||
Utils.execAsync([
|
||||
"notify-send",
|
||||
"-i",
|
||||
"preferences-desktop-theme-symbolic",
|
||||
"Theme copied to clipboard",
|
||||
'To save it permanently, make a new theme in <span weight="bold">themes.js</span>',
|
||||
]);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const searchEntry = Widget.Revealer({
|
||||
transition: "slide_down",
|
||||
reveal_child: showSearch.bind(),
|
||||
transition_duration: options.transition.bind("value"),
|
||||
child: Widget.Entry({
|
||||
setup: (self) =>
|
||||
self.hook(showSearch, () => {
|
||||
if (!showSearch.value) self.text = "";
|
||||
|
||||
if (showSearch.value) self.grab_focus();
|
||||
}),
|
||||
hexpand: true,
|
||||
class_name: "search",
|
||||
placeholder_text: "Search Options",
|
||||
secondary_icon_name: icons.apps.search,
|
||||
on_change: ({ text }) => (search.value = text || ""),
|
||||
}),
|
||||
});
|
||||
|
||||
const categoriesStack = Widget.Stack({
|
||||
transition: "slide_left_right",
|
||||
children: categories.reduce((obj, name) => {
|
||||
obj[name] = Page(name);
|
||||
return obj;
|
||||
}, {}),
|
||||
shown: currentPage.bind(),
|
||||
visible: search.bind().transform((v) => !v),
|
||||
});
|
||||
|
||||
const searchPage = Widget.Box({
|
||||
visible: search.bind().transform((v) => !!v),
|
||||
child: Page(""),
|
||||
});
|
||||
|
||||
export default RegularWindow({
|
||||
name: "settings-dialog",
|
||||
title: "Settings",
|
||||
setup: (win) =>
|
||||
win
|
||||
.on("delete-event", () => {
|
||||
win.hide();
|
||||
return true;
|
||||
})
|
||||
.on("key-press-event", (_, event) => {
|
||||
if (event.get_keyval()[1] === imports.gi.Gdk.KEY_Escape) {
|
||||
showSearch.setValue(false);
|
||||
search.setValue("");
|
||||
}
|
||||
})
|
||||
.set_default_size(800, 500),
|
||||
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
sidebar,
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: [searchEntry, categoriesStack, searchPage],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import Mpris from "resource:///com/github/Aylur/ags/service/mpris.js";
|
||||
|
||||
export async function globals() {
|
||||
try {
|
||||
globalThis.options = (await import("../options.js")).default;
|
||||
globalThis.iconBrowser = (await import("../misc/IconBrowser.js")).default;
|
||||
globalThis.app = (
|
||||
await import("resource:///com/github/Aylur/ags/app.js")
|
||||
).default;
|
||||
globalThis.audio = (
|
||||
await import("resource:///com/github/Aylur/ags/service/audio.js")
|
||||
).default;
|
||||
globalThis.recorder = (await import("../services/screenrecord.js")).default;
|
||||
globalThis.brightness = (await import("../services/brightness.js")).default;
|
||||
globalThis.indicator = (
|
||||
await import("../services/onScreenIndicator.js")
|
||||
).default;
|
||||
globalThis.app = (
|
||||
await import("resource:///com/github/Aylur/ags/app.js")
|
||||
).default;
|
||||
|
||||
Mpris.players.forEach((player) => {
|
||||
player.connect("changed", (player) => {
|
||||
globalThis.mpris = player || Mpris.players[0];
|
||||
});
|
||||
});
|
||||
|
||||
Mpris.connect("player-added", (mpris, bus) => {
|
||||
mpris.getPlayer(bus)?.connect("changed", (player) => {
|
||||
globalThis.mpris = player || Mpris.players[0];
|
||||
});
|
||||
});
|
||||
|
||||
Mpris.connect("player-closed", () => {
|
||||
globalThis.mpris = Mpris.players[0];
|
||||
});
|
||||
} catch (error) {
|
||||
logError(error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import Hyprland from "resource:///com/github/Aylur/ags/service/hyprland.js";
|
||||
import options from "../options.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
|
||||
const noIgnorealpha = ["verification", "powermenu", "lockscreen"];
|
||||
|
||||
/** @param {Array<string>} batch */
|
||||
function sendBatch(batch) {
|
||||
const cmd = batch
|
||||
.filter((x) => !!x)
|
||||
.map((x) => `keyword ${x}`)
|
||||
.join("; ");
|
||||
|
||||
Hyprland.sendMessage(`[[BATCH]]/${cmd}`);
|
||||
}
|
||||
|
||||
/** @param {string} scss */
|
||||
function getColor(scss) {
|
||||
if (scss.includes("#")) return scss.replace("#", "");
|
||||
|
||||
if (scss.includes("$")) {
|
||||
const opt = options
|
||||
.list()
|
||||
.find((opt) => opt.scss === scss.replace("$", ""));
|
||||
return opt?.value.replace("#", "") || "ff0000";
|
||||
}
|
||||
}
|
||||
|
||||
export function hyprlandInit() {
|
||||
sendBatch(
|
||||
App.windows.flatMap(({ name }) => [
|
||||
`layerrule blur, ${name}`,
|
||||
noIgnorealpha.some((skip) => name?.includes(skip))
|
||||
? ""
|
||||
: `layerrule ignorealpha 0.3, ${name}`,
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
export async function setupHyprland() {
|
||||
/*Hyprland.event("activewindowv2", async (addr) => {
|
||||
const client = Hyprland.getClient(addr);
|
||||
if (!client.pinned || !client.floating) return;
|
||||
const x = client.at[0];
|
||||
console.log(
|
||||
await Utils.execAsync(`hyprctl dispatch moveactive exact ${x} 80`),
|
||||
);
|
||||
});*/
|
||||
|
||||
const wm_gaps = Math.floor(
|
||||
options.hypr.wm_gaps_multiplier.value * options.spacing.value,
|
||||
);
|
||||
const border_width = options.border.width.value;
|
||||
const radii = options.radii.value;
|
||||
const drop_shadow = options.desktop.drop_shadow.value;
|
||||
const inactive_border = options.hypr.inactive_border.value;
|
||||
const accent = getColor(options.theme.accent.accent.value);
|
||||
|
||||
sendBatch([
|
||||
`general:border_size ${border_width}`,
|
||||
`general:gaps_out ${wm_gaps}`,
|
||||
`general:gaps_in ${Math.floor(wm_gaps / 2)}`,
|
||||
`general:col.active_border rgba(${accent}ff)`,
|
||||
`general:col.inactive_border ${inactive_border}`,
|
||||
`decoration:rounding ${radii}`,
|
||||
`decoration:drop_shadow ${drop_shadow ? "yes" : "no"}`,
|
||||
]);
|
||||
}
|
||||
198
modules/home-manager/desktops/hyprland/ags/js/settings/option.js
Normal file
@@ -0,0 +1,198 @@
|
||||
import {
|
||||
CACHE_DIR,
|
||||
readFile,
|
||||
writeFile,
|
||||
} from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import { exec } from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import options from "../options.js";
|
||||
import Service from "resource:///com/github/Aylur/ags/service.js";
|
||||
import { reloadScss } from "./scss.js";
|
||||
import { setupHyprland } from "./hyprland.js";
|
||||
const CACHE_FILE = CACHE_DIR + "/options.json";
|
||||
|
||||
/** object that holds the overriedden values */
|
||||
let cacheObj = JSON.parse(readFile(CACHE_FILE) || "{}");
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} OptionConfig
|
||||
* @property {string=} scss - name of scss variable set to "exclude" to not include it in the generated scss file
|
||||
* @property {string=} unit - scss unit on numbers, default is "px"
|
||||
* @property {string=} title
|
||||
* @property {string=} note
|
||||
* @property {string=} category
|
||||
* @property {boolean=} noReload - don't reload css & hyprland on change
|
||||
* @property {boolean=} persist - ignore reset call
|
||||
* @property {'object' | 'string' | 'img' | 'number' | 'float' | 'font' | 'enum' =} type
|
||||
* @property {Array<string> =} enums
|
||||
* @property {(value: T) => any=} format
|
||||
* @property {(value: T) => any=} scssFormat
|
||||
*/
|
||||
|
||||
/** @template T */
|
||||
export class Opt extends Service {
|
||||
static {
|
||||
Service.register(
|
||||
this,
|
||||
{},
|
||||
{
|
||||
value: ["jsobject"],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#value;
|
||||
#scss = "";
|
||||
unit = "px";
|
||||
noReload = false;
|
||||
persist = false;
|
||||
id = "";
|
||||
title = "";
|
||||
note = "";
|
||||
type = "";
|
||||
category = "";
|
||||
|
||||
/** @type {Array<string>} */
|
||||
enums = [];
|
||||
|
||||
/** @type {(v: T) => any} */
|
||||
format = (v) => v;
|
||||
|
||||
/** @type {(v: T) => any} */
|
||||
scssFormat = (v) => v;
|
||||
|
||||
/**
|
||||
* @param {T} value
|
||||
* @param {OptionConfig<T> =} config
|
||||
*/
|
||||
constructor(value, config) {
|
||||
super();
|
||||
this.#value = value;
|
||||
this.defaultValue = value;
|
||||
this.type = typeof value;
|
||||
|
||||
if (config) Object.keys(config).forEach((c) => (this[c] = config[c]));
|
||||
|
||||
import("../options.js").then(this.#init.bind(this));
|
||||
}
|
||||
|
||||
set scss(scss) {
|
||||
this.#scss = scss;
|
||||
}
|
||||
get scss() {
|
||||
return this.#scss || this.id.split(".").join("-").split("_").join("-");
|
||||
}
|
||||
|
||||
#init() {
|
||||
getOptions(); // sets the ids as a side effect
|
||||
|
||||
if (cacheObj[this.id] !== undefined) this.setValue(cacheObj[this.id]);
|
||||
|
||||
const words = this.id
|
||||
.split(".")
|
||||
.flatMap((w) => w.split("_"))
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1));
|
||||
|
||||
this.title ||= words.join(" ");
|
||||
this.category ||= words.length === 1 ? "General" : words.at(0) || "General";
|
||||
|
||||
this.connect("changed", () => {
|
||||
cacheObj[this.id] = this.value;
|
||||
writeFile(JSON.stringify(cacheObj, null, 2), CACHE_FILE);
|
||||
});
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.#value;
|
||||
}
|
||||
set value(value) {
|
||||
this.setValue(value);
|
||||
}
|
||||
|
||||
/** @param {T} value */
|
||||
setValue(value, reload = false) {
|
||||
if (typeof value !== typeof this.defaultValue) {
|
||||
console.error(
|
||||
Error(
|
||||
`WrongType: Option "${this.id}" can't be set to ${value}, ` +
|
||||
`expected "${typeof this.defaultValue}", but got "${typeof value}"`,
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.value !== value) {
|
||||
this.#value = this.format(value);
|
||||
this.changed("value");
|
||||
|
||||
if (reload && !this.noReload) {
|
||||
reloadScss();
|
||||
setupHyprland();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset(reload = false) {
|
||||
if (!this.persist) this.setValue(this.defaultValue, reload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
* @param {OptionConfig<T> =} config
|
||||
* @returns {Opt<T>}
|
||||
*/
|
||||
export function Option(value, config) {
|
||||
return new Opt(value, config);
|
||||
}
|
||||
|
||||
/** @returns {Array<Opt<any>>} */
|
||||
export function getOptions(object = options, path = "") {
|
||||
return Object.keys(object).flatMap((key) => {
|
||||
/** @type Option<any> */
|
||||
const obj = object[key];
|
||||
const id = path ? path + "." + key : key;
|
||||
|
||||
if (obj instanceof Opt) {
|
||||
obj.id = id;
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (typeof obj === "object") return getOptions(obj, id);
|
||||
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
export function resetOptions() {
|
||||
exec(`rm -rf ${CACHE_FILE}`);
|
||||
cacheObj = {};
|
||||
getOptions().forEach((opt) => opt.reset());
|
||||
}
|
||||
|
||||
export function getValues() {
|
||||
const obj = {};
|
||||
for (const opt of getOptions()) {
|
||||
if (opt.category !== "exclude") obj[opt.id] = opt.value;
|
||||
}
|
||||
|
||||
return JSON.stringify(obj, null, 2);
|
||||
}
|
||||
|
||||
/** @param {string | object} config */
|
||||
export function apply(config) {
|
||||
const options = getOptions();
|
||||
const settings = typeof config === "string" ? JSON.parse(config) : config;
|
||||
|
||||
for (const id of Object.keys(settings)) {
|
||||
const opt = options.find((opt) => opt.id === id);
|
||||
if (!opt) {
|
||||
print(`No option with id: "${id}"`);
|
||||
continue;
|
||||
}
|
||||
|
||||
opt.setValue(settings[id]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import { getOptions } from "./option.js";
|
||||
|
||||
export function scssWatcher() {
|
||||
return Utils.subprocess(
|
||||
[
|
||||
"inotifywait",
|
||||
"--recursive",
|
||||
"--event",
|
||||
"create,modify",
|
||||
"-m",
|
||||
App.configDir + "/scss",
|
||||
],
|
||||
reloadScss,
|
||||
() => print("missing dependancy for css hotreload: inotify-tools"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* generate an scss file that makes every option available as a variable
|
||||
* based on the passed scss parameter or the path in the object
|
||||
*
|
||||
* e.g
|
||||
* options.bar.style.value => $bar-style
|
||||
*/
|
||||
export async function reloadScss() {
|
||||
const opts = getOptions();
|
||||
const vars = opts.map((opt) => {
|
||||
if (opt.scss === "exclude") return "";
|
||||
|
||||
const unit = typeof opt.value === "number" ? opt.unit : "";
|
||||
const value = opt.scssFormat ? opt.scssFormat(opt.value) : opt.value;
|
||||
return `$${opt.scss}: ${value}${unit};`;
|
||||
});
|
||||
|
||||
const bar_style = opts.find((opt) => opt.id === "bar.style")?.value || "";
|
||||
const additional =
|
||||
bar_style === "normal"
|
||||
? "//"
|
||||
: `
|
||||
window#quicksettings .window-content {
|
||||
margin-right: $wm-gaps;
|
||||
}
|
||||
`;
|
||||
|
||||
try {
|
||||
const tmp = "/tmp/ags/scss";
|
||||
Utils.ensureDirectory(tmp);
|
||||
await Utils.writeFile(vars.join("\n"), `${tmp}/options.scss`);
|
||||
await Utils.writeFile(additional, `${tmp}/additional.scss`);
|
||||
await Utils.execAsync(
|
||||
`sassc ${App.configDir}/scss/main.scss ${tmp}/style.css`,
|
||||
);
|
||||
App.resetCss();
|
||||
App.applyCss(`${tmp}/style.css`);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) console.error(error.message);
|
||||
|
||||
if (typeof error === "string") console.error(error);
|
||||
}
|
||||
}
|
||||
126
modules/home-manager/desktops/hyprland/ags/js/settings/setup.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import * as Utils from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import Battery from "resource:///com/github/Aylur/ags/service/battery.js";
|
||||
import Notifications from "resource:///com/github/Aylur/ags/service/notifications.js";
|
||||
import options from "../options.js";
|
||||
import icons from "../icons.js";
|
||||
import { reloadScss } from "./scss.js";
|
||||
import { wallpaper } from "./wallpaper.js";
|
||||
import { hyprlandInit, setupHyprland } from "./hyprland.js";
|
||||
import { globals } from "./globals.js";
|
||||
import { showAbout } from "../about/about.js";
|
||||
import Gtk from "gi://Gtk?version=3.0";
|
||||
|
||||
export function init() {
|
||||
notificationBlacklist();
|
||||
warnOnLowBattery();
|
||||
globals();
|
||||
tmux();
|
||||
kitty();
|
||||
gsettigsColorScheme();
|
||||
gtkFontSettings();
|
||||
dependandOptions();
|
||||
|
||||
reloadScss();
|
||||
hyprlandInit();
|
||||
setupHyprland();
|
||||
wallpaper();
|
||||
showAbout();
|
||||
}
|
||||
|
||||
function dependandOptions() {
|
||||
options.bar.style.connect("changed", ({ value }) => {
|
||||
if (value !== "normal")
|
||||
options.desktop.screen_corners.setValue(false, true);
|
||||
});
|
||||
}
|
||||
|
||||
function kitty() {
|
||||
if (!Utils.exec("which kitty")) return;
|
||||
console.log("kitty");
|
||||
options.theme.scheme.connect("changed", ({ value }) =>
|
||||
Utils.execAsync(
|
||||
`kitty +kitten themes --reload-in=all --config-file-name /home/theaninova/.config/kitty/current-colors.conf Catppuccin-${
|
||||
value === "light" ? "Latte" : "Frappe"
|
||||
}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function tmux() {
|
||||
if (!Utils.exec("which tmux")) return;
|
||||
|
||||
/** @param {string} scss */
|
||||
function getColor(scss) {
|
||||
if (scss.includes("#")) return scss;
|
||||
|
||||
if (scss.includes("$")) {
|
||||
const opt = options
|
||||
.list()
|
||||
.find((opt) => opt.scss === scss.replace("$", ""));
|
||||
return opt?.value;
|
||||
}
|
||||
}
|
||||
|
||||
options.theme.accent.accent.connect("changed", ({ value }) =>
|
||||
Utils.execAsync(`tmux set @main_accent ${getColor(value)}`).catch((err) =>
|
||||
console.error(err.message),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function gsettigsColorScheme() {
|
||||
if (!Utils.exec("which gsettings")) return;
|
||||
|
||||
options.theme.scheme.connect("changed", ({ value }) => {
|
||||
const gsettings = "gsettings set org.gnome.desktop.interface color-scheme";
|
||||
Utils.execAsync(`${gsettings} "prefer-${value}"`).catch((err) =>
|
||||
console.error(err.message),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function gtkFontSettings() {
|
||||
const settings = Gtk.Settings.get_default();
|
||||
if (!settings) {
|
||||
console.error(Error("Gtk.Settings unavailable"));
|
||||
return;
|
||||
}
|
||||
|
||||
const callback = () => {
|
||||
const { size, font } = options.font;
|
||||
settings.gtk_font_name = `${font.value} ${size.value}`;
|
||||
};
|
||||
|
||||
options.font.font.connect("notify::value", callback);
|
||||
options.font.size.connect("notify::value", callback);
|
||||
}
|
||||
|
||||
function notificationBlacklist() {
|
||||
Notifications.connect("notified", (_, id) => {
|
||||
const n = Notifications.getNotification(id);
|
||||
options.notifications.black_list.value.forEach((item) => {
|
||||
if (n?.app_name.includes(item) || n?.app_entry?.includes(item)) n.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function warnOnLowBattery() {
|
||||
Battery.connect("notify::percent", () => {
|
||||
const low = options.battery.low.value;
|
||||
if (
|
||||
Battery.percent !== low ||
|
||||
Battery.percent !== low / 2 ||
|
||||
!Battery.charging
|
||||
)
|
||||
return;
|
||||
|
||||
Utils.execAsync([
|
||||
"notify-send",
|
||||
`${Battery.percent}% Battery Percentage`,
|
||||
"-i",
|
||||
icons.battery.warning,
|
||||
"-u",
|
||||
"critical",
|
||||
]);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import App from "resource:///com/github/Aylur/ags/app.js";
|
||||
import options from "../options.js";
|
||||
import themes from "../themes.js";
|
||||
import { reloadScss } from "./scss.js";
|
||||
import { setupHyprland } from "./hyprland.js";
|
||||
import { wallpaper } from "./wallpaper.js";
|
||||
|
||||
/** @param {string} name */
|
||||
export function setTheme(name) {
|
||||
options.reset();
|
||||
const theme = themes.find((t) => t.name === name);
|
||||
if (!theme) return print("No theme named " + name);
|
||||
|
||||
options.apply(theme.options);
|
||||
reloadScss();
|
||||
setupHyprland();
|
||||
wallpaper();
|
||||
}
|
||||
|
||||
export const WP = App.configDir + "/assets/";
|
||||
|
||||
export const lightColors = {
|
||||
"theme.scheme": "light",
|
||||
"color.red": "#d20f39",
|
||||
"color.green": "#40a02b",
|
||||
"color.yellow": "#df8e1d",
|
||||
"color.blue": "#1e66f5",
|
||||
"color.magenta": "#8839ef",
|
||||
"color.teal": "#179299",
|
||||
"color.orange": "#fe640b",
|
||||
"theme.bg": "transparentize(#eff1f5, 0.3)",
|
||||
"theme.fg": "#4c4f69",
|
||||
};
|
||||
|
||||
export const darkColors = {
|
||||
"theme.scheme": "dark",
|
||||
"color.red": "#e78284",
|
||||
"color.green": "#a6d189",
|
||||
"color.yellow": "#e5c890",
|
||||
"color.blue": "#8caaee",
|
||||
"color.magenta": "#ca9ee6",
|
||||
"color.teal": "#81c8be",
|
||||
"color.orange": "#ef9f76",
|
||||
"theme.bg": "transparentize(#303446, 0.3)",
|
||||
"theme.fg": "#c6d0f5",
|
||||
};
|
||||
|
||||
export const Theme = ({ name, icon = " ", ...options }) => ({
|
||||
name,
|
||||
icon,
|
||||
options: {
|
||||
"theme.name": name,
|
||||
"theme.icon": icon,
|
||||
...options,
|
||||
},
|
||||
});
|
||||
|
||||
let settingsDialog;
|
||||
export async function openSettings() {
|
||||
if (settingsDialog) return settingsDialog.present();
|
||||
|
||||
try {
|
||||
settingsDialog = (await import("./SettingsDialog.js")).default;
|
||||
settingsDialog.present();
|
||||
} catch (error) {
|
||||
if (error instanceof Error) console.error(error.message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import options from "../options.js";
|
||||
import { exec, execAsync } from "resource:///com/github/Aylur/ags/utils.js";
|
||||
import { dependencies } from "../utils.js";
|
||||
|
||||
export function initWallpaper() {
|
||||
if (dependencies(["swww"])) {
|
||||
exec("swww init");
|
||||
|
||||
options.desktop.wallpaper.img.connect("changed", wallpaper);
|
||||
}
|
||||
}
|
||||
|
||||
export function wallpaper() {
|
||||
if (!dependencies(["swww"])) return;
|
||||
|
||||
execAsync(["swww", "img", options.desktop.wallpaper.img.value]).catch((err) =>
|
||||
console.error(err),
|
||||
);
|
||||
}
|
||||
77
modules/home-manager/desktops/hyprland/ags/js/themes.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* A Theme is a set of options that will be applied
|
||||
* ontop of the default values. see options.js for possible options
|
||||
*/
|
||||
import { Theme, WP, lightColors, darkColors } from "./settings/theme.js";
|
||||
|
||||
export default [
|
||||
Theme({
|
||||
name: "Frappé",
|
||||
icon: "",
|
||||
"desktop.screen_corners": false,
|
||||
"desktop.clock.enable": false,
|
||||
"bar.style": "separated",
|
||||
"bar.separators": false,
|
||||
"desktop.wallpaper.img":
|
||||
WP + "wallpapers/Lakeside/lakeside_2019_midnight.png",
|
||||
...darkColors,
|
||||
}),
|
||||
Theme({
|
||||
name: "Latte",
|
||||
icon: "",
|
||||
"desktop.screen_corners": false,
|
||||
"desktop.clock.enable": false,
|
||||
"bar.style": "separated",
|
||||
"bar.separators": false,
|
||||
"desktop.wallpaper.img":
|
||||
WP + "wallpapers/Lakeside/Lakeside_2019_Teal_NoDeer_UHD2.png",
|
||||
...lightColors,
|
||||
"theme.widget.bg": "$accent",
|
||||
"theme.widget.opacity": 64,
|
||||
}),
|
||||
/*Theme({
|
||||
name: "Leaves",
|
||||
icon: "",
|
||||
"desktop.wallpaper.img": WP + "leaves.jpg",
|
||||
"theme.accent.accent": "$green",
|
||||
"theme.accent.gradient": "to right, $accent, darken($accent, 14%)",
|
||||
"theme.widget.opacity": 92,
|
||||
"border.opacity": 86,
|
||||
"theme.bg": "transparentize(#171717, 0.3)",
|
||||
"bar.style": "floating",
|
||||
radii: 0,
|
||||
}),
|
||||
Theme({
|
||||
name: "Ivory",
|
||||
icon: "",
|
||||
...lightColors,
|
||||
"desktop.wallpaper.img": WP + "ivory.png",
|
||||
"desktop.wallpaper.fg": "$bg_color",
|
||||
"desktop.screen_corners": false,
|
||||
"bar.style": "separated",
|
||||
"theme.widget.bg": "$accent",
|
||||
"theme.widget.opacity": 64,
|
||||
"desktop.drop_shadow": false,
|
||||
"border.width": 2,
|
||||
"border.opacity": 0,
|
||||
"theme.accent.gradient": "to right, $accent, darken($accent, 6%)",
|
||||
"hypr.inactive_border": "rgba(111111FF)",
|
||||
"bar.separators": false,
|
||||
}),
|
||||
Theme({
|
||||
name: "Space",
|
||||
icon: "",
|
||||
"desktop.wallpaper.img": WP + "space.jpg",
|
||||
spacing: 11,
|
||||
padding: 10,
|
||||
radii: 12,
|
||||
"theme.accent.accent": "$magenta",
|
||||
"desktop.screen_corners": false,
|
||||
"desktop.clock.enable": false,
|
||||
"bar.separators": false,
|
||||
"bar.icon": "",
|
||||
"theme.bg": "transparentize(#171717, 0.3)",
|
||||
"theme.widget.opacity": 95,
|
||||
"bar.flat_buttons": false,
|
||||
}),*/
|
||||
];
|
||||