Compare commits
1657 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| df0c7bbbd6 | |||
| 7bfbf011d2 | |||
| 945c6a0782 | |||
| 20cae142d7 | |||
| 20e1f0e0bb | |||
| a412688af2 | |||
| dec65aea17 | |||
| cbcb8cf8e1 | |||
| fdcb95b8a4 | |||
| 694eaccfbf | |||
| 42919c59ec | |||
| 58980959aa | |||
| f98c422254 | |||
| 1cfeff8b10 | |||
| 3cc68b29da | |||
| 3b212c454d | |||
| b5ddc36d5e | |||
| 38586f0efc | |||
| 0462ee5e56 | |||
| 711fb48e37 | |||
| 6d4ea704d6 | |||
| 39ed0e472a | |||
| a54a8d8daf | |||
| 80d62c438f | |||
| 61b89de945 | |||
| c812e9c8af | |||
| d001bc1269 | |||
| 98d2c4f881 | |||
| 4396079c28 | |||
| e10ad1ed71 | |||
| 051accbe2f | |||
| 74941d15bf | |||
| cf1ea9e3d9 | |||
| 3dd1264a12 | |||
| dbb8d015e8 | |||
| 13892a01e1 | |||
| fb7dbaa187 | |||
| 2c4668bcb0 | |||
| d0e2c69de3 | |||
| 3a64eae028 | |||
| 284bb084c6 | |||
| 14c115f80a | |||
| 8b5a790d54 | |||
| e6a19a6afb | |||
| 781af1d420 | |||
| baa3c2a773 | |||
| 8f4190a939 | |||
| 696ff4298f | |||
| f2462eb1b4 | |||
| 53768a6885 | |||
| 5db16e6245 | |||
| dda0a228cf | |||
| f678a55e6a | |||
| b322af7051 | |||
| 7236d2f50b | |||
| 87f5bc5870 | |||
| 3187239175 | |||
| 09fd61c71d | |||
| c1f84c77dd | |||
| 3f242fa298 | |||
| c7ac7b5b43 | |||
| 688a36af58 | |||
| 10e1509d5d | |||
| fc2e51ebbc | |||
| 1433fb5412 | |||
| b383635fa8 | |||
| 788c01c242 | |||
| b965b50009 | |||
| 241f33fb2f | |||
| f9bc4b1608 | |||
| 0091fce2c1 | |||
| cf43479530 | |||
| d74e385f84 | |||
| d292a85a5e | |||
| 3b1d8fd262 | |||
| 769ed3bf71 | |||
| d10ac9cc74 | |||
| 4bf31544e7 | |||
| 03a149c10e | |||
| 5ec8cca5d5 | |||
| 44c8de82c7 | |||
| b08a545ece | |||
| 42b9c7c854 | |||
| 994985ecae | |||
| 229c9d5e78 | |||
| ae52e28afb | |||
| 758d40fc8b | |||
| 601cb3ffbe | |||
| 9aa869af77 | |||
| 1d07260fd0 | |||
| fe06c1891f | |||
| 26f2a9f3fd | |||
| 1cc04e118f | |||
| 87181585aa | |||
| a831d393c1 | |||
| 1845e59090 | |||
| 3fe8377309 | |||
| 20b3d2498e | |||
| 917fae6d4f | |||
| a91fe7db30 | |||
| daa4dd7b0f | |||
| cd952729f4 | |||
| 843025bc64 | |||
| 40368432e8 | |||
| 2c88a71eed | |||
| 082f12084d | |||
| 23b471edc2 | |||
| e50ea627e8 | |||
| fd8f569477 | |||
| 1766375348 | |||
| 1a58f1258a | |||
| 0f867df271 | |||
| a27a6deddf | |||
| e5e85db75d | |||
| 96cda9e6dd | |||
| fe3c502459 | |||
| e2799414dd | |||
| fa08f972d6 | |||
| b06a7ce58e | |||
| a0c5940a94 | |||
| 3365719a49 | |||
| 6afe810d69 | |||
| bf70be7f4a | |||
| 1a4b4b8bef | |||
| 7f49daf422 | |||
| f4c32f89f2 | |||
| 2807bed255 | |||
| e5e598853f | |||
| 47aa8232f7 | |||
| 06c51553ba | |||
| 281646ef0c | |||
| 2f1c66570f | |||
| 60144ca3de | |||
| a0131e5bf8 | |||
| 4afaa7cc7e | |||
| ad9f25c346 | |||
| 6be3fe0c65 | |||
| 3bebabd95e | |||
| 6e9f2c14ce | |||
| 4f68e9e61a | |||
| 56a7e8cbdd | |||
| 25a63b593d | |||
| 32af8bf257 | |||
| cd0d49032a | |||
| 11f7adc643 | |||
| 6d6fa42857 | |||
| d14d170016 | |||
| bd04346f16 | |||
| 46bd7c785a | |||
| 520068e523 | |||
| 902c8327d3 | |||
| d497e00474 | |||
| b5e1bcf3be | |||
| 923841cb56 | |||
| 6ca5175bb0 | |||
| 9aa6d03f87 | |||
| eff52332b5 | |||
| bf376c8aaa | |||
| 64f6081b14 | |||
| b3f81f350c | |||
| 025a819b63 | |||
| 2667751a1c | |||
| b1007f2ded | |||
| aca19d6903 | |||
| 52dced17a3 | |||
| a1606c9c23 | |||
| 9249034fa6 | |||
| 1924111d6b | |||
| 8214e2d052 | |||
| 564fd54cdb | |||
| a92bd67957 | |||
| 22469dc5f2 | |||
| 5f4f8980f0 | |||
| e24630b9be | |||
| 51076dda88 | |||
| 37244dc0f7 | |||
| c82a2e835b | |||
| 8a35609b0d | |||
| 1f208125bf | |||
| ba6fba447a | |||
| c19766c887 | |||
| 0e7422c335 | |||
| 67695c8edb | |||
| ede86dc7a7 | |||
| a28945f3ec | |||
| 80275c5adf | |||
| 31f2184dc6 | |||
| 9d830767c7 | |||
| 9471223a76 | |||
| c2942afe8e | |||
| d8adcce5fb | |||
| b9a7fa4d0d | |||
| 7f8265c9e6 | |||
| c1ff57c3d0 | |||
| 4cad276963 | |||
| 5a4b4e6d3c | |||
| 7440f78069 | |||
| 38cd1a7169 | |||
| 4443b736d8 | |||
| d509a8777b | |||
| 27754e5d03 | |||
| fbb284ae00 | |||
| 9bbabc3079 | |||
| 5a3abbe45a | |||
| e5283cac5c | |||
| eb55f2533d | |||
| 352e459d82 | |||
| 90a4e2128b | |||
| c79a3e6309 | |||
| a6f1560bce | |||
| a60f917f59 | |||
| 0f93784b8a | |||
| 5af7420129 | |||
| 17d2d32bf1 | |||
| 7534509caa | |||
| 76db90af1b | |||
| b4b15a2808 | |||
| f2055d128e | |||
| 534ef6fd7c | |||
| 4cb2c6589d | |||
| f23e9e5da9 | |||
| 22fb48cd0a | |||
| 3ae87c8a67 | |||
| 5d773090eb | |||
| c0d64c4630 | |||
| a323e32a42 | |||
| 1703cb24d1 | |||
| 35ce444c23 | |||
| 8b2f1c054c | |||
| 56c43aa1e6 | |||
| 2cd705950f | |||
| 467b84d3e2 | |||
| 4270d2fe56 | |||
| 519de4f5ba | |||
| 90a6efecbb | |||
| 27e3fd5e13 | |||
| e55d70fa62 | |||
| b873a1f033 | |||
| 0f0bc9d318 | |||
| 087a736d1a | |||
| 97fbec551f | |||
| 7c974b7fb4 | |||
| 4f712116c2 | |||
| 83ce5f3fea | |||
| 7f4a626a83 | |||
| 47810c95da | |||
| bd14c73f9b | |||
| f368382765 | |||
| efd44c421b | |||
| d429d55d16 | |||
| e3fc712e11 | |||
| f5b495969b | |||
| 1ee08fca51 | |||
| 525108dd95 | |||
| d005f9204c | |||
| 6237e117aa | |||
| 0c29167057 | |||
| e28550b53f | |||
| c43e163a56 | |||
| 31f40ae9ee | |||
| b8051ce2cf | |||
| 09c637914d | |||
| 7e283404f3 | |||
| 8a20824266 | |||
| 0e63e698f2 | |||
| c2bb57f0bc | |||
| 6676d5844b | |||
| ca7d6c8ae0 | |||
| b267b74e8b | |||
| dcc14a565d | |||
| f96e84ff4a | |||
| 41e9c00fa8 | |||
| 85e3fd5e36 | |||
| daf7d2c9dc | |||
| f663837c4e | |||
| 77d9b93887 | |||
| 816d2b8a76 | |||
| f40cf790af | |||
| 8e1a3d26b1 | |||
| a240329f22 | |||
| 6afa6d2142 | |||
| 0c587415ea | |||
| ace8802480 | |||
| 7e1637b810 | |||
| 0fabedb0c4 | |||
| e567f06cef | |||
| bf87ed69ce | |||
| 7cb0e4e039 | |||
| 510f1b2188 | |||
| 1bea1e8c91 | |||
| 9c89099cf1 | |||
| efae444942 | |||
| 3536e54b50 | |||
| 17984c812f | |||
| 09ad926642 | |||
| e3549e639e | |||
| b421691734 | |||
| 835c113416 | |||
| 2e807806ba | |||
| 2c21eccac3 | |||
| f302da1275 | |||
| 82ce9b866f | |||
| df23d79e04 | |||
| 43aae4ee56 | |||
| 73be5c5f0a | |||
| 02c71e9310 | |||
| f041272302 | |||
| aeb1955947 | |||
| a0332cb0df | |||
| 79df7bbeef | |||
| af64052e33 | |||
| fc479c3582 | |||
| 886e16a1cf | |||
| 367b1b9499 | |||
| c61da40f70 | |||
| 90cc63e57a | |||
| fc17f23533 | |||
| 69fc9d9b35 | |||
| 533156e0d0 | |||
| 8c125cccb1 | |||
| 3da64f6bc5 | |||
| daa671c6a5 | |||
| 71eb88016e | |||
| c45166387b | |||
| f4dffe7f37 | |||
| df69162f78 | |||
| e14c9b61d5 | |||
| b1cc6bd19b | |||
| e02b505ae5 | |||
| 6aad5a9581 | |||
| 4b33a10779 | |||
| 6ec202da9e | |||
| 54cf1cf821 | |||
| d36966e2d7 | |||
| f01a2a06b5 | |||
| f866ef1fd7 | |||
| 480966f978 | |||
| b3f06049be | |||
| 7887805550 | |||
| dbd12d3e92 | |||
| db79ecd636 | |||
| 9da8cc0cea | |||
| f9c7bbbe01 | |||
| 81116598cb | |||
| 46c803c9ce | |||
| 93567a68e8 | |||
| 2294653431 | |||
| aae3add0c9 | |||
| b690cd6335 | |||
| 487c0fc916 | |||
| 0551c010b5 | |||
| d645286744 | |||
| f3e674684e | |||
| fd72cd5e22 | |||
| e8d2f8c476 | |||
| e7e25cd25b | |||
| d7750c099b | |||
| b74019a83b | |||
| f81f4925a9 | |||
| df307d3d45 | |||
| a122d6e905 | |||
| a97b7706d8 | |||
| e622928d9d | |||
| fceda9bd35 | |||
| 0bd22f89a7 | |||
| 1b996e37a7 | |||
| 403344e120 | |||
| a200951b75 | |||
| 5c71c53787 | |||
| 40b1e8297f | |||
| dfbbe3fcca | |||
| b484e3311c | |||
| 287172cd5d | |||
| 5ef979db79 | |||
| af2dcc8533 | |||
| 06514ed7b7 | |||
| c7bc853ab3 | |||
| 3f20ab758c | |||
| e3db8372a7 | |||
| 175379dfdb | |||
| ae87646e40 | |||
| b0f09b20d4 | |||
| f10bbacf7b | |||
| f50a3fe686 | |||
| bdc55dd082 | |||
| 86a10e9af5 | |||
| e5934c3eed | |||
| dabd8dc136 | |||
| 5509e21759 | |||
| f324310355 | |||
| 8b25e2b037 | |||
| 634fb09d2e | |||
| 0ac39d4356 | |||
| 60a0dcfdcf | |||
| 6551d7cc5e | |||
| 4859acb700 | |||
| 34c46910b2 | |||
| d51ce6a46f | |||
| bd8daf4015 | |||
| 649be3741c | |||
| fcc2ee3551 | |||
| 128808a56d | |||
| 714895976f | |||
| 9fe68c5a38 | |||
| a3a62f9826 | |||
| 389fd5e42c | |||
| 947a13556a | |||
| ee1fbf72cc | |||
| 06775806d5 | |||
| 961d5e7721 | |||
| 727227cf8b | |||
| cead3c87ea | |||
| c550a792b8 | |||
| cc1e5e4636 | |||
| e279e4d972 | |||
| eb6fca6697 | |||
| 7476655302 | |||
| 8eb50a8917 | |||
| d835d8bc30 | |||
| 981e3be9b0 | |||
| db3d8ddfc2 | |||
| f5c421ab99 | |||
| 4ad001fd9e | |||
| 05f3e52f55 | |||
| 8b10ec2cfb | |||
| 7b278aeff7 | |||
| d5e9b20aec | |||
| e9c3eca68a | |||
| 41a328fbf0 | |||
| e1a8ba09f1 | |||
| c565dc1a7e | |||
| ede90eb282 | |||
| f95165185c | |||
| 26361718a7 | |||
| 41cf490681 | |||
| 44e384a256 | |||
| 21276f4d1e | |||
| 43e8c85295 | |||
| 4672138b00 | |||
| b8f115ef10 | |||
| 574cbecf18 | |||
| 97b3f7ec55 | |||
| 7982f43a62 | |||
| fc32ce56d0 | |||
| 6c6a4edf59 | |||
| 44da20ed4a | |||
| 4f8de83ff4 | |||
| 066971e720 | |||
| 107dc8cc24 | |||
| 7b42efd37a | |||
| 9df78087f0 | |||
| 06d12fb8ec | |||
| 08d1a2dfd6 | |||
| 035e51b36e | |||
| 901aa820e5 | |||
| c1a5641ff5 | |||
| 957a63d04a | |||
| 43960b3a60 | |||
| 8d7e4bdd0d | |||
| 9610baf903 | |||
| 52011a7d80 | |||
| 6be1437ecc | |||
| cc49b4c921 | |||
| 3aa1d5f1ed | |||
| 991f113e4e | |||
| 04963a616d | |||
| 839ca74bf1 | |||
| 437b2020b7 | |||
| 4712931850 | |||
| e56af1adc6 | |||
| 7f245e2896 | |||
| 4fd5ba5630 | |||
| 7c5740a39b | |||
| 53998cc51a | |||
| 3faa20a29b | |||
| 57a2e5aba4 | |||
| ad01fdad4e | |||
| 2893dc33b6 | |||
| 5a846cdec5 | |||
| c13ba5f25e | |||
| 28e956e55c | |||
| a05b041d69 | |||
| 496225ab9d | |||
| 254b9471e4 | |||
| b137feac16 | |||
| cf159f6112 | |||
| 60f055f07d | |||
| 5dd0fe2761 | |||
| 43fe3874dd | |||
| ddf97f79d0 | |||
| 5a050be3de | |||
| d8aa0de443 | |||
| 7662e4f904 | |||
| b0d64d00b6 | |||
| 9dd8f32595 | |||
| 99dd0a4e2e | |||
| 531411315e | |||
| e6eb53796d | |||
| a3ced86214 | |||
| 435ff32904 | |||
| e1270836d0 | |||
| 93f8d0990f | |||
| b23a2d4f2c | |||
| d031087972 | |||
| cdf24a1c19 | |||
| e857d538fb | |||
| 3bd699c9e6 | |||
| 6f756f48cb | |||
| b14a5b3dd9 | |||
| 5f23f6caa3 | |||
| d05ae5231c | |||
| 07d3eea1d1 | |||
| 23830f3454 | |||
| 24ae5d327e | |||
| 7b9454b101 | |||
| b976ba17d5 | |||
| 9c803164e1 | |||
| b9592b30af | |||
| a536e1600d | |||
| 6267e54ad7 | |||
| 11b337b189 | |||
| 20d34eb622 | |||
| 416df97b64 | |||
| b3cabb788b | |||
| c620c11ba8 | |||
| 7515f77846 | |||
| b35ef90916 | |||
| a4d2a720d0 | |||
| 8141e15bd9 | |||
| 064488a9c4 | |||
| bb08c61b76 | |||
| 6ed9c9869e | |||
| 586d7d4f9b | |||
| 0ac96e02ab | |||
| 63be9874f4 | |||
| c1393ce7c7 | |||
| 973d0c0c07 | |||
| c4f81e7027 | |||
| ed0289df3b | |||
| 7e4cbaf5df | |||
| bce8b6f9a8 | |||
| 0718546167 | |||
| 8e222eb40d | |||
| fddb7ecc05 | |||
| b5ae6f01eb | |||
| ecd7a225e9 | |||
| f2e4508cfc | |||
| 4031925e11 | |||
| a99f6cac5e | |||
| d5bccd9bb1 | |||
| 94102cec97 | |||
| 778620c312 | |||
| c50a505cdb | |||
| 1830aeba18 | |||
| 1ba6b761f0 | |||
| b1921b7847 | |||
| f37fa1b071 | |||
| a79201ebd7 | |||
| b557586a62 | |||
| 1842ab790e | |||
| e8937e2030 | |||
| 2ddfc77b66 | |||
| 00526116cc | |||
| 1c2bfb7991 | |||
| fa52acad27 | |||
| 18c11899cb | |||
| 3cd323cb1a | |||
| 09c894db0a | |||
| 3f030805d1 | |||
| e96be21850 | |||
| 9242b93558 | |||
| 4de08c438b | |||
| 576311f7b2 | |||
| bf96099e46 | |||
| 854edba825 | |||
| fe3e5de518 | |||
| d5b1e9f40c | |||
| 54fe878580 | |||
| 075a21a9db | |||
| cc605e24d9 | |||
| 4ea7401190 | |||
| 3b4525413a | |||
| 8bf59faf66 | |||
| 54b4dd7818 | |||
| 21b3cca54a | |||
| af65c39c87 | |||
| 1d51cc3388 | |||
| b6c1cd504e | |||
| ba0f2248d8 | |||
| 96605fb0fe | |||
| 991abd4c1c | |||
| fec23cab8d | |||
| 0fe7bdc5b5 | |||
| 65557dfb3d | |||
| ad907a72a1 | |||
| c65aea86c6 | |||
| 76dd63a326 | |||
| 8798b4e826 | |||
| 7b1fa1246f | |||
| f3e4773811 | |||
| 233b4c78ab | |||
| f81d316ad4 | |||
| f123e90392 | |||
| d0607c789f | |||
| 85278ea147 | |||
| 9cbe0f38f9 | |||
| 6bcb20ee4c | |||
| ca104160b0 | |||
| bc13baa5a9 | |||
| 2014a030d6 | |||
| f673cae32b | |||
| f5d09d569c | |||
| 94749c1c69 | |||
| 9c216036ec | |||
| 7e8750f79b | |||
| 8ca46aecdd | |||
| 0e6779fafc | |||
| acde0218b3 | |||
| b8a1955ab9 | |||
| e49426c027 | |||
| 00d547362b | |||
| cce6e821c2 | |||
| a48ebfc4c1 | |||
| b23bdb0188 | |||
| 69b92b57aa | |||
| 84d692feef | |||
| 6834d41f4b | |||
| 9fad405064 | |||
| a8ddbb5d2d | |||
| 1fd328f90a | |||
| 6779fa90a5 | |||
| 45d6bfa3fb | |||
| 5d1a9b1e9c | |||
| 77ae119d32 | |||
| 0facd08fa9 | |||
| 0ccdc47034 | |||
| 7c21ec0c5a | |||
| 731beb0f7c | |||
| 9a170a3c5b | |||
| f93d629acb | |||
| eede0e3c34 | |||
| f4f97be46d | |||
| 3bea2a314e | |||
| 4d20de926c | |||
| b307b4ed95 | |||
| 653cba4d4e | |||
| adb93e382f | |||
| b2938ef678 | |||
| 809c8806d0 | |||
| d809c2e789 | |||
| ca2d073775 | |||
| 4038c437c6 | |||
| 12011fd0c8 | |||
| 528ae04711 | |||
| 7612a3f742 | |||
| 2272b94531 | |||
| 21d628b598 | |||
| ab0049ec5c | |||
| ac394784e3 | |||
| 943a72ce12 | |||
| 927d9b0f85 | |||
| 7454087c88 | |||
| 37fd19fc9a | |||
| aa90e75c59 | |||
| 7b090c2e2a | |||
| 1a4a8d87fc | |||
| fbd0644942 | |||
| 4eb4f635e7 | |||
| 60d6bfae9f | |||
| 009345c5f6 | |||
| 00d4d368df | |||
| 5fda1cdc61 | |||
| 91955ef66c | |||
| 89585f8121 | |||
| 449df7f161 | |||
| a5170c51b3 | |||
| 20bda361a3 | |||
| ca7ae4e1e8 | |||
| 6f08589265 | |||
| ac5c902569 | |||
| 731b5cccad | |||
| fd1e82bd8e | |||
| 2140eff9af | |||
| 6f97bb2635 | |||
| fd0b5f4377 | |||
| c5bd1d6bc0 | |||
| 40ba90df27 | |||
| 450a1493fe | |||
| 3b1f9aa41e | |||
| e993671d02 | |||
| b36758a155 | |||
| 14a46622c3 | |||
| 9aea0f1034 | |||
| 8e87a7aa99 | |||
| 4d31886e4a | |||
| 4886f87afa | |||
| 923b22a75a | |||
| 610ac47289 | |||
| de28b8d314 | |||
| 7c50f6c8d0 | |||
| 8b493e091d | |||
| 13065d7e5a | |||
| 3f7d6759c1 | |||
| a4be03cd5f | |||
| 44d5994248 | |||
| d8b6d52a85 | |||
| e0e6054f2f | |||
| eeed0c0b1a | |||
| 5fbdea2cae | |||
| 393944a8a7 | |||
| a442e2be91 | |||
| 18398e1b93 | |||
| 1b08584ced | |||
| 20a3da8a19 | |||
| 1dd4c4a109 | |||
| a228c54dd5 | |||
| 125d3c0a4d | |||
| c9f5397821 | |||
| 57a5c0f743 | |||
| 8dc31cc790 | |||
| fd1d74ada1 | |||
| 28756860aa | |||
| 5929533d78 | |||
| 395baf1509 | |||
| b4d89b66f5 | |||
| e9f803b8a5 | |||
| 8b25dca1dc | |||
| 514363247a | |||
| 354a415e73 | |||
| 4df5de3122 | |||
| e5815974b1 | |||
| 2ff8e8fa67 | |||
| dd7038adf2 | |||
| 39d25107bd | |||
| 2c671578ae | |||
| 76680ba86c | |||
| 4d83c5a8f9 | |||
| 274a857dff | |||
| c891c9a921 | |||
| 247a8e0cca | |||
| 15c0ab6279 | |||
| 5c7c21fdf1 | |||
| 14348306e2 | |||
| 02cc4c1aa2 | |||
| 1aadbfc019 | |||
| b53e657091 | |||
| 74967cbac8 | |||
| a139451a9b | |||
| 7708372922 | |||
| 09146ec176 | |||
| 3abadb666e | |||
| be164a823d | |||
| d5dda1722a | |||
| 28ea9a7861 | |||
| ec87635e8e | |||
| 48219fbd95 | |||
| 33b5202a5d | |||
| 141fa55869 | |||
| 2a2e2da879 | |||
| 73bd73d910 | |||
| 9d733fcff5 | |||
| 307c543d8d | |||
| 5ffd1c85d3 | |||
| f771d47526 | |||
| 50e73d4a3a | |||
| 6cc96d094d | |||
| dc149d2636 | |||
| 6a9fd04989 | |||
| 7c01257cdd | |||
| fa99a0b3c8 | |||
| cec8dc991c | |||
| 2b78225114 | |||
| 2ffec8014a | |||
| 30f2db1e4d | |||
| 1b44b27142 | |||
| badc1249c0 | |||
| 8621b3f6ff | |||
| e67d8e9c81 | |||
| 769ad73737 | |||
| afb14409c2 | |||
| 971aaf3b98 | |||
| 2d2145a780 | |||
| 28078910a2 | |||
| 6235e6e665 | |||
| 5bf63bc36c | |||
| 02192368d2 | |||
| b0cfcaff3d | |||
| d208b07a94 | |||
| e715478310 | |||
| 5dedbf91e0 | |||
| 64e04ae15b | |||
| 573105d269 | |||
| 8fc6a4b349 | |||
| c6c76be8f9 | |||
| 1cd15ffa53 | |||
| eb9c3f7867 | |||
| a369409930 | |||
| ad6788f67a | |||
| 28de204036 | |||
| cf5bfb16bd | |||
| 5642d379a5 | |||
| e33992ecf8 | |||
| fa85e0cc68 | |||
| 42ee8d9ca4 | |||
| d521e014fd | |||
| 43545bce41 | |||
| 78723402ee | |||
| 9698974ad7 | |||
| 6f138677a8 | |||
| 9161044860 | |||
| 30e804acd8 | |||
| 40fe0bebcf | |||
| 4f25572d1d | |||
| 81cc35702d | |||
| cd3ed42b6d | |||
| 4637b82471 | |||
| e1d77c0c3e | |||
| bc688792cb | |||
| 36b33ba4f1 | |||
| edde61a46c | |||
| aa1fcd7eb9 | |||
| 3f59e2078a | |||
| c2edd26598 | |||
| a0ceed9586 | |||
| a6e360c1db | |||
| 79b49bd57e | |||
| 0c1b7abe36 | |||
| 6d39b63a74 | |||
| 438b2aac65 | |||
| 3ffc25d5d8 | |||
| 3b689f8a66 | |||
| 2b62a5a0cc | |||
| 7e46145df1 | |||
| 9655549739 | |||
| 57a3d1369b | |||
| 2ae7a519d0 | |||
| 9165c6b706 | |||
| 7818864fac | |||
| 3f577f1088 | |||
| 546435db6d | |||
| c6f64a3acb | |||
| c6b7652c6e | |||
| bfebd69568 | |||
| 3fb46f9604 | |||
| 17384f5761 | |||
| 5a5328b1cb | |||
| edc83764c4 | |||
| 1f5527164c | |||
| af5a7964d0 | |||
| 41c2814610 | |||
| c197f6eab2 | |||
| 410da66834 | |||
| eb6a0e38f7 | |||
| 8e90c2898a | |||
| 4074741187 | |||
| 01bcab4af1 | |||
| 6c5cc98016 | |||
| b39618cd89 | |||
| 22316b4684 | |||
| 1e7e3a84c6 | |||
| 872e0762b6 | |||
| 61fec4b53d | |||
| 8e7b012c4c | |||
| 7ae53ac364 | |||
| c519505296 | |||
| a9b60c1d1b | |||
| 22108934ff | |||
| c93f8eafea | |||
| 856cfd3ebb | |||
| bccb516223 | |||
| 53717588f9 | |||
| 4258d94d00 | |||
| 01f8631663 | |||
| 393d90d3f7 | |||
| bd90e2c19e | |||
| fe19cea6c9 | |||
| 56ab7f212e | |||
| 5179611c64 | |||
| 0c930294f1 | |||
| 8541dd3178 | |||
| 7e9a07838e | |||
| 7f2d1702ca | |||
| b4b461d815 | |||
| bc75157b9f | |||
| 67465045bb | |||
| 5dbfe00207 | |||
| 38ac1723f6 | |||
| af3ef1efde | |||
| 3fba370e87 | |||
| a28b1b5aa2 | |||
| aec5ef442e | |||
| c907512cf0 | |||
| b87190993f | |||
| 2d7cacbefb | |||
| 63e81369c2 | |||
| 6f9bc17d57 | |||
| 7bd1810852 | |||
| 433fe1449a | |||
| b6566ec67b | |||
| b67b0baa1b | |||
| 73e6d627bc | |||
| e4ace3c416 | |||
| b58c86cdd6 | |||
| 9ff93169df | |||
| 44cdb3a52e | |||
| 3d7fbba014 | |||
| ce1a397a7c | |||
| fa7da189fc | |||
| 582fffda1b | |||
| 3bf6f7ca09 | |||
| 0a7c549125 | |||
| 3bb510e910 | |||
| 75a84f6ed1 | |||
| a7aadd7439 | |||
| 1e90434c18 | |||
| d718f023cd | |||
| 28b47b25ea | |||
| 2bf933a3e1 | |||
| 24a548cd97 | |||
| ce307391a8 | |||
| 7abc7c07af | |||
| d8c49a3d04 | |||
| 064bb51d7a | |||
| b74e36ca60 | |||
| 3e344a3de0 | |||
| b19cc90fd3 | |||
| eff8d56ef5 | |||
| a0825e7774 | |||
| 765e370bd5 | |||
| 8469a07f4f | |||
| fcf4646928 | |||
| f5bbb1747c | |||
| 1868bffbef | |||
| eb5c6796ae | |||
| a35d128bb5 | |||
| 74787f8927 | |||
| 64455b594b | |||
| aa064fb6c5 | |||
| 70f3a25798 | |||
| 19230db8b7 | |||
| 4fb7f7c1e7 | |||
| 0b20dda56e | |||
| 5c29ac8d1e | |||
| eeed075be2 | |||
| 6c23d482dd | |||
| b2a7b191cf | |||
| 95c3cf2c90 | |||
| 26dc81fc6d | |||
| 26bab59886 | |||
| c412090dbc | |||
| a4c2bf31c7 | |||
| 5d15ccae9f | |||
| 63f1954c2a | |||
| 1fc4b88784 | |||
| aa9b1c3331 | |||
| 458b086468 | |||
| 75762e8ddd | |||
| 58dbdd5189 | |||
| 0d3d38a032 | |||
| 68ea59328e | |||
| f74a4f056e | |||
| d70e5c396f | |||
| c70b8f7aa2 | |||
| 737dd01c8d | |||
| 945deafa63 | |||
| a77b7f7b66 | |||
| e86a0e23bb | |||
| 6218ffabca | |||
| 4de3c5e587 | |||
| 7dc448938a | |||
| 1c30330faf | |||
| 40d36c2468 | |||
| d60113bab9 | |||
| 71a19a86f8 | |||
| 5385a4834a | |||
| 91b55ad2af | |||
| b1428b34c1 | |||
| 3a5b0af81d | |||
| 1f1a9a0b13 | |||
| 83c9f98d0a | |||
| c8f8c85c6e | |||
| 0c14112d60 | |||
| aa15572c68 | |||
| b16e171619 | |||
| a23551c7f2 | |||
| 95c2947d4a | |||
| a87e4ff449 | |||
| 8fac9fe67e | |||
| eafaf78c12 | |||
| 66e40128f8 | |||
| cf303380b6 | |||
| 4b6b27a8fd | |||
| 48f054c809 | |||
| 10e25fcd03 | |||
| 35dadbb7c4 | |||
| 29bb41aff0 | |||
| 28e6da1bbc | |||
| b9a92507d1 | |||
| fc8985b242 | |||
| 924a01bca6 | |||
| fac79c8a7d | |||
| 0e89e8dcc2 | |||
| 594dc64824 | |||
| 79ec382b7f | |||
| 1ec94aa2a6 | |||
| 70a5343d60 | |||
| d88da39fe9 | |||
| 5f369a511a | |||
| afac62e788 | |||
| e4533cc03f | |||
| ee9c4baa9d | |||
| 27c36530cb | |||
| a5b80c1c73 | |||
| d6e9e9f2a5 | |||
| 122c1f8e37 | |||
| 07a3edf020 | |||
| 299fd4f107 | |||
| 1ae989c0c0 | |||
| 4d9323800b | |||
| e935976c91 | |||
| 319680e7cc | |||
| aa40185058 | |||
| d0e0716221 | |||
| 62e6641480 | |||
| 6c25cc6a78 | |||
| ec3ac73def | |||
| 9b547d6ece | |||
| ce1f149547 | |||
| 394d992b19 | |||
| b8b3b7993b | |||
| 915877eba6 | |||
| 7dda10629a | |||
| fc5a5d7f63 | |||
| d2ff23813d | |||
| 06840c6c84 | |||
| fda70b0aef | |||
| 7b13ddcbfc | |||
| 741af0fc43 | |||
| abf9b6c5e4 | |||
| 56361dda86 | |||
| d276e7b568 | |||
| 5af0fe35df | |||
| 94fb563a15 | |||
| 865e8575b2 | |||
| d95147712b | |||
| 20e1fa935a | |||
| f885351464 | |||
| 33fdc1cdc7 | |||
| 9d39417142 | |||
| 96ed90e2cc | |||
| f99c390a76 | |||
| fba3a54f82 | |||
| fedeb47dbc | |||
| 79ac51a1ca | |||
| 02631da9f1 | |||
| 692172d57b | |||
| 8bd4bbe7ea | |||
| 0d93f90c62 | |||
| 3f9459a07e | |||
| 19d063c3e9 | |||
| 7f8a5315c4 | |||
| 0f7f7d997b | |||
| ab54a24434 | |||
| 77901659e5 | |||
| b3f74b6c1c | |||
| 7eb5fa8d8e | |||
| a90e2132e3 | |||
| 60b0db7120 | |||
| af538950a8 | |||
| 5787e95c51 | |||
| 3b3be4b6cb | |||
| c1fa902189 | |||
| 31706e2724 | |||
| 21d6bcc63e | |||
| c4ad1a7127 | |||
| 17984344ee | |||
| 035a58e7c5 | |||
| 646e33e59f | |||
| 68ddfb0f02 | |||
| cd3b8b5bab | |||
| da88fd4267 | |||
| 29413fdc8e | |||
| c643d26bce | |||
| 69d4f32933 | |||
| 26154a6cdb | |||
| 7b22553f09 | |||
| 9726f0f586 | |||
| 25f6f09d22 | |||
| 3e6a68b472 | |||
| 60e18c4854 | |||
| f7633dd61f | |||
| ae74354140 | |||
| b0987b224d | |||
| 5ecae9662f | |||
| 0f29869a76 | |||
| 67dd730666 | |||
| ccaa13fa21 | |||
| ff795b1373 | |||
| 81287e9eb5 | |||
| 26b9d8193c | |||
| 81bdb352aa | |||
| 2ae8530c83 | |||
| 476755fc18 | |||
| 4247495cf4 | |||
| b6eada070a | |||
| 2399600169 | |||
| db9877beae | |||
| 43f1d5c53e | |||
| 4a31d538c6 | |||
| 23dd7baf35 | |||
| a8e1f3ef4c | |||
| cd622e9f81 | |||
| 71f7d45084 | |||
| 0f586fe49e | |||
| 42913816ce | |||
| 027f9a1793 | |||
| 8bc2cf335b | |||
| 626c71a942 | |||
| 58b9c4f1d6 | |||
| 24163dfa4a | |||
| e3d596e034 | |||
| cd48f45462 | |||
| a0229e8132 | |||
| be7ff94ae3 | |||
| 98ddce24ad | |||
| b380a77a7d | |||
| 9f7a2d86cf | |||
| 343a7ef4fe | |||
| 88c3b0ba83 | |||
| 918997acfa | |||
| dd1fb111a5 | |||
| 7124b69912 | |||
| 76bcca55d2 | |||
| 7fa2ffc14e | |||
| 0ee141493f | |||
| 194b21c41e | |||
| 93634985e8 | |||
| 6757dc2ca1 | |||
| 3a888f9be4 | |||
| 34c9cbeedb | |||
| 97a2769002 | |||
| f87e6e2b6a | |||
| 7bd3f1e08d | |||
| aeb20a8949 | |||
| 58e99403ad | |||
| 530987152c | |||
| 4085b4a74a | |||
| 852172a7ff | |||
| 7aa1230553 | |||
| a7097014a3 | |||
| 9f711c20e0 | |||
| 87c031b825 | |||
| 18e2f5dd7f | |||
| d1b70185f1 | |||
| 254175c98e | |||
| e8e67912ec | |||
| 3c00805763 | |||
| 599055b49f | |||
| 82fa754497 | |||
| 9ec9cc0e25 | |||
| f52c9415c9 | |||
| 27626ee59a | |||
| 0b36e2a833 | |||
| 1aa0e3fb47 | |||
| 4a40ce5646 | |||
| 6e126dc08d | |||
| 6201798ef5 | |||
| ec3c09607d | |||
| 2b104435dc | |||
| 83be5822b1 | |||
| 0491bd993e | |||
| 8713e94428 | |||
| ccab724db2 | |||
| 7876ab993a | |||
| bd8e004795 | |||
| 01ab0f5ab9 | |||
| 0728dba0cd | |||
| 3dd5e78b35 | |||
| 667afa6d64 | |||
| d24555a4d7 | |||
| 410971a228 | |||
| b497c22d6b | |||
| de6969f561 | |||
| 0ce4260134 | |||
| 7237dd053d | |||
| 0f6c076dda | |||
| b57e678dc9 | |||
| 6031bb4953 | |||
| 05dc53b396 | |||
| e97f819a5c | |||
| 28c37c08d2 | |||
| f430b22884 | |||
| cf4505635b | |||
| 8ad0de63e1 | |||
| 8068ea95d5 | |||
| 44cbd025f4 | |||
| ac76930dd1 | |||
| 3a653d9558 | |||
| ae50430527 | |||
| 83386bcdbf | |||
| 1e175e4e82 | |||
| 01815d04dc | |||
| 46ea6900b4 | |||
| 6394fe049d | |||
| f324fc7c10 | |||
| 434c383fcd | |||
| 9eade626aa | |||
| 263299b97d | |||
| 7e491ce7d8 | |||
| 6707487d3c | |||
| 0498d7ea98 | |||
| fc9bda9f7f | |||
| 2b47083c12 | |||
| 1f700f33b2 | |||
| 769dafb428 | |||
| e3bc2e5d84 | |||
| f3120f1e0d | |||
| 036d8b4852 | |||
| a67a8d746f | |||
| 2237ec135c | |||
| a238c33fd0 | |||
| b90cf14228 | |||
| 7bbd1d2d52 | |||
| 8aebbb1b51 | |||
| df7504d4a2 | |||
| 063399a6d6 | |||
| 9762a94138 | |||
| 800f5c7c64 | |||
| 16995d2ae5 | |||
| b4cced68f1 | |||
| 9bb01bba30 | |||
| cb3842f0bd | |||
| 5d95d20a32 | |||
| b628ee0c3e | |||
| 688bf0bef8 | |||
| 06d6d3a9ca | |||
| bb4debb863 | |||
| 5d1a3fe6a7 | |||
| 72b5d006ba | |||
| d382358766 | |||
| 429cb50ff7 | |||
| 3a01dad945 | |||
| b9ae5cafa0 | |||
| ed8e4b8766 | |||
| cecd47caea | |||
| 7711946cf5 | |||
| 074aebbe5d | |||
| 8b78d05805 | |||
| cf962a26f4 | |||
| 14778696e9 | |||
| ed56d03c09 | |||
| f9d9df4bbf | |||
| 0859d75256 | |||
| 21303b24c8 | |||
| 06965e7eed | |||
| fc0b069ad2 | |||
| b0b27d6842 | |||
| 47725ea4b5 | |||
| 103d349c5f | |||
| c41364fb16 | |||
| b984d6794e | |||
| cf4aa1256d | |||
| 4a6fcb4f4c | |||
| 426804304c | |||
| 074f8ed902 | |||
| d3a1bd52c7 | |||
| a4103859cd | |||
| ed19b7b635 | |||
| f21dd041ae | |||
| 2de21b5f25 | |||
| a547ae6cf9 | |||
| c39901e95d | |||
| 4f0522d913 | |||
| b972d3fabe | |||
| d6bef6b17d | |||
| 231100cbdc | |||
| 9c8d71ca4d | |||
| fcdc17dd93 | |||
| a719ca684c | |||
| e5033c3213 | |||
| d37cf9e9c4 | |||
| d9688884e4 | |||
| 2f369bbf3d | |||
| 7f1b67d21c | |||
| 6118589248 | |||
| 9a7f5c2def | |||
| 1999f17443 | |||
| 7e39f0a34f | |||
| fb9a726ba0 | |||
| ddc3f0e880 | |||
| d66b0891e7 | |||
| f1ba087bff | |||
| 6cf5f2edc4 | |||
| f0570b0e20 | |||
| cd851afc8d | |||
| 532497f6bd | |||
| f648137a8e | |||
| e9fd2f89fe | |||
| 5565911e2c | |||
| 7337012280 | |||
| e3f25e3bc4 | |||
| 9582a473c6 | |||
| 22918e4b4a | |||
| 0d567ea5dc | |||
| 703697e1c4 | |||
| 0b89ed0d17 | |||
| f3de317ddd | |||
| cd7e60b7a1 | |||
| 352a5bea5c | |||
| f33eaf6d0f | |||
| 81ebf9cc11 | |||
| 23f92e0a96 | |||
| e69f8ced88 | |||
| ba5b052ee8 | |||
| b6e6997e7c | |||
| c39507747b | |||
| c9f4390f0f | |||
| 801cd2e855 | |||
| e9c73ebb71 | |||
| 6af3a1b5a7 | |||
| c86dbeb2b3 | |||
| 4ff6b2a30a | |||
| 3310d2b431 | |||
| b53de43c95 | |||
| ffb8e284b4 | |||
| f593ad0f2c | |||
| 9114d5b2fe | |||
| 5b47a20d49 | |||
| b0acc5a68e | |||
| d7382db669 | |||
| f290192de4 | |||
| 8360d4d589 | |||
| df56224df6 | |||
| 1a636ff8e9 | |||
| 4dcc1f340f | |||
| f199bc4256 | |||
| 11e28286e1 | |||
| 4c454d463a | |||
| 80034d8658 | |||
| 296afa96c6 | |||
| 9f69b8815a | |||
| cdc38f7e6e | |||
| fcb4e6cc85 | |||
| cb00f31f79 | |||
| 84c65d32be | |||
| cc119bb596 | |||
| 88c9850073 | |||
| 75fcb9a990 | |||
| 7121a4ae30 | |||
| e636920c44 | |||
| e0cf3d0962 | |||
| 53b6f5d8e8 | |||
| 5acbfac255 | |||
| 82bb1175bd | |||
| f1b1589a7d | |||
| 0f4ff90f01 | |||
| 43e0bcbf73 | |||
| e26045ec73 | |||
| 2d52680bed | |||
| f42f526f93 | |||
| 53b03af3e1 | |||
| 32f8692f13 | |||
| 19c321e7ae | |||
| 021a49a72d | |||
| eb0bad1af6 | |||
| 513d140ea2 | |||
| 3e368141c7 | |||
| b59b7c1e47 | |||
| 6a1acc819b | |||
| e5db36e21e | |||
| d759bc274f | |||
| 3290755fa8 | |||
| fd3455d3ec | |||
| 88355b2504 | |||
| 79f078653a | |||
| a952ea02dc | |||
| 59abffb1c1 | |||
| d737139bee | |||
| a1479a9b6c | |||
| 45894c3255 | |||
| dcfdb2ecff | |||
| ba7ed5be1c | |||
| 307412e6ca | |||
| 41e1e89696 | |||
| d239ab6b1f | |||
| 0905773d08 | |||
| 9ca80d352b | |||
| b52440bcc1 | |||
| 28c9cacf68 | |||
| 8199fdd2bf | |||
| f74ed76850 | |||
| 5b301addd8 | |||
| 1c1a141701 | |||
| e85f59db8c | |||
| fc69ca0599 | |||
| 2ad304aaf2 | |||
| c1b56922aa | |||
| 8c737f2ca4 | |||
| 20d9561143 | |||
| 1062ef9b49 | |||
| 2b54f64c8c | |||
| 5cd5a9d7e4 | |||
| bb49747fd9 | |||
| f38c7bffed | |||
| 813f02604e | |||
| c579dce2cf | |||
| f6d9c2998b | |||
| 60c303b771 | |||
| 79e7f262a7 | |||
| 8f2863c02c | |||
| cd8cb03797 | |||
| 9f4aa3f7e1 | |||
| eb2c9f2fe1 | |||
| e7ffd2455a | |||
| 63b7a3a36c | |||
| 376f2bfeb1 | |||
| 0a34b139f4 | |||
| 45d0a8e501 | |||
| 485dd2952e | |||
| a111b50e37 | |||
| b2d14ca101 | |||
| f3ab3573c3 | |||
| ef4ae4480f | |||
| 8277a2d942 | |||
| 8bbf040100 | |||
| e6bb1a3fde | |||
| 6df7482b3d | |||
| bdc0ade117 | |||
| 29a149b340 | |||
| 80af866650 | |||
| 1237d9660e | |||
| 8e6582b801 | |||
| 18ad260ce9 | |||
| c0933a3b20 | |||
| a116ae6ab5 | |||
| 0e2eea7555 | |||
| 767e35851b | |||
| a25a3c186b | |||
| 28256c0a72 | |||
| dac9ed2785 | |||
| 9cc576b98d | |||
| 87c95f7a7f | |||
| 4065142830 | |||
| f56308b6e3 | |||
| bdbdf7cb83 | |||
| 9a0f28c003 | |||
| 2d2d93d5d8 | |||
| 8c44dd6119 | |||
| 0cb1bc4ca5 | |||
| db83864d78 | |||
| 63365c2109 | |||
| 9bbc26dd70 | |||
| a383cfd125 | |||
| 8bb9070d9a | |||
| 690e934a46 | |||
| be1974a89e | |||
| d21087b0d9 | |||
| 22b1ad087c | |||
| ce25675f73 | |||
| 955a7696f1 | |||
| 4cfacde337 | |||
| eafa8f02b6 | |||
| 1338b65aaa | |||
| 7c8068b2bd | |||
| 75f749dbe0 | |||
| 58910f50e8 | |||
| 36eebffd10 | |||
| 0e8e0e4b06 | |||
| 8524fb500f | |||
| 8fd1d9acef | |||
| c452dd3538 | |||
| 222f083322 | |||
| 47527d7175 | |||
| 6f907f7961 | |||
| 891a226fdb | |||
| 7cbc707308 | |||
| 8d3a036b3b | |||
| ee21139356 | |||
| b37814e9fa | |||
| 07cca33cb4 | |||
| 980533052b | |||
| 8124f688da | |||
| 89e726b5a2 | |||
| 42695d9253 | |||
| 28fe7817b4 | |||
| 6aa37e2529 | |||
| 2d8eb163e7 | |||
| 445b10d6f0 | |||
| 0a4dd832a3 | |||
| 8feee4e61a | |||
| fe23017a97 | |||
| b01b52c8f8 | |||
| 0f81d2cace | |||
| d051246b7e | |||
| fd7f22ffba | |||
| c5c0cf001c | |||
| ab846062e4 | |||
| 172e77a6cf | |||
| 0998d8a0fc | |||
| 71b82730ab | |||
| 6b597bf1fb | |||
| 5c431096ec | |||
| e278bed251 | |||
| efbcf5469b | |||
| de0a91619e | |||
| 7be0c1a034 | |||
| 71e43d6e3d | |||
| a77865379f | |||
| e20cc9e80f | |||
| c66c920cbe | |||
| 695dbee501 | |||
| de44a8d91e | |||
| de6ba1c82e | |||
| 5b1124d658 | |||
| 122b16a720 | |||
| dfcf2eec57 | |||
| 9a9a35d75e | |||
| ec89c9cdc8 | |||
| e66ab2b2c9 | |||
| dc167bd25e | |||
| 680378e68f | |||
| 190dbff98d | |||
| ff7aa46cda | |||
| 8c47d85927 | |||
| de99a3f114 | |||
| 8b77aa151b | |||
| 00461468c9 | |||
| ad323f7885 | |||
| a50b673e07 | |||
| d1e66c4441 | |||
| 7cc00b99a1 | |||
| c745615a2a | |||
| 7aacac554d | |||
| fcab19c392 | |||
| 98e2bf53ed | |||
| 51310e632c | |||
| 2a46b17958 | |||
| e189f0317f | |||
| 138684fcea | |||
| 23af50a30d | |||
| 28a6284968 | |||
| ce114b33ff | |||
| d05c451294 | |||
| 9fc0d26eb5 | |||
| 25a0c88670 | |||
| 0d5d9a04f5 | |||
| f2ed0057bf | |||
| 661fcfd263 | |||
| dbed431085 | |||
| fb89ca98f6 | |||
| 04bec463c7 | |||
| 25170f0d2c | |||
| 85a522ba14 | |||
| a28618ab78 | |||
| 955a5ba7f2 | |||
| dbd23a6ea4 | |||
| df93057dd3 | |||
| 3c2b6d4e7f | |||
| 2f11e12c75 | |||
| 2faf615b68 | |||
| 8bb2e0aa97 | |||
| b6e4a47329 | |||
| 08197fca23 | |||
| 423fb90266 | |||
| 67d1d73845 | |||
| fa0531b975 | |||
| cb01563ba8 | |||
| 4446259a0b | |||
| a5eb2f0ae6 | |||
| bf4a1097c1 | |||
| 87ebc8761b | |||
| 4e87ce8f4b | |||
| 24d24c1f85 | |||
| c88d77e3f8 | |||
| 553c42b82c | |||
| 3eb1dd3180 | |||
| 10b2cf2662 | |||
| 545fd8b58a | |||
| 2e31972c56 | |||
| d095cb7812 | |||
| fc31d5fd2f | |||
| d73ba14154 | |||
| d8e09e80ff | |||
| 211c4fb102 | |||
| d448b76dd4 | |||
| e8ebf5af73 | |||
| 459dab65ca | |||
| 73304ef8d4 | |||
| aa025e4fac | |||
| 8eac9338fe | |||
| b0deecc0aa | |||
| af111a4be9 | |||
| 6a8a9172b1 | |||
| 291b997972 | |||
| 70a5520f47 | |||
| e7397f824b | |||
| 0beee14cd8 | |||
| 9ba251ff30 | |||
| 5b24ac037f | |||
| ea70c354ad | |||
| 1623b456ec | |||
| 307959ba76 | |||
| 7f1699663d | |||
| 252675d3ff | |||
| 84c45f2068 | |||
| 5873995e42 | |||
| 077922c667 | |||
| 47dcb1523a | |||
| 86f1e63ed2 | |||
| 709415a6b4 | |||
| fa46485d57 | |||
| c2d5d2b61a | |||
| e39c1af5ae | |||
| ca6463cae8 | |||
| 0e17b7a981 | |||
| bcd1167d39 | |||
| fbe17dc3e3 | |||
| b102e5c1a5 | |||
| df9a5e398e | |||
| 90fafa219a | |||
| 9e1b55a749 | |||
| 4ca15a1fc3 | |||
| 5bf80dae4e | |||
| 1f4568d22f | |||
| 903a975033 | |||
| 0ee9afba4f | |||
| 9b5713d6b4 | |||
| 94176fad83 | |||
| de759b5120 | |||
| 9bc4ff16a7 | |||
| 814f6ced5f | |||
| 91dae8ad85 | |||
| fb4f8a86f4 | |||
| e15d2fe82c | |||
| 4ede1c74e8 | |||
| 9d98396e33 | |||
| e43dcf5d4c | |||
| eac4ab3e3c | |||
| d256d8fc35 | |||
| 3157e99e04 | |||
| 91fee0d6e9 | |||
| 521061fc64 | |||
| 4328746df0 | |||
| 2b87d939bb | |||
| 39a88a6cd3 | |||
| d0ad416e28 | |||
| 61e4f59aa0 | |||
| e1b7336d5d | |||
| 627c8562f7 | |||
| 061bb2abeb | |||
| 1dc46fa104 | |||
| 4161467356 | |||
| bfb7ccffb5 | |||
| 3a6c032782 | |||
| e4b761917a | |||
| 9df7129c6c | |||
| 0b9717c2a5 |
@@ -1,29 +0,0 @@
|
|||||||
function fish_prompt -d "Write out the prompt"
|
|
||||||
# This shows up as USER@HOST /home/user/ >, with the directory colored
|
|
||||||
# $USER and $hostname are set by fish, so you can just use them
|
|
||||||
# instead of using `whoami` and `hostname`
|
|
||||||
printf '%s@%s %s%s%s > ' $USER $hostname \
|
|
||||||
(set_color $fish_color_cwd) (prompt_pwd) (set_color normal)
|
|
||||||
end
|
|
||||||
|
|
||||||
if status is-interactive
|
|
||||||
# Commands to run in interactive sessions can go here
|
|
||||||
set fish_greeting
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
starship init fish | source
|
|
||||||
if test -f ~/.local/state/quickshell/user/generated/terminal/sequences.txt
|
|
||||||
cat ~/.local/state/quickshell/user/generated/terminal/sequences.txt
|
|
||||||
end
|
|
||||||
|
|
||||||
alias pamcan pacman
|
|
||||||
alias ls 'eza --icons'
|
|
||||||
alias clear "printf '\033[2J\033[3J\033[1;1H'"
|
|
||||||
alias q 'qs -c ii'
|
|
||||||
|
|
||||||
|
|
||||||
# function fish_prompt
|
|
||||||
# set_color cyan; echo (pwd)
|
|
||||||
# set_color green; echo '> '
|
|
||||||
# end
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# This file sources other files in `hyprland` and `custom` folders
|
|
||||||
# You wanna add your stuff in files in `custom`
|
|
||||||
|
|
||||||
$qsConfig = ii
|
|
||||||
exec = hyprctl dispatch submap global # DO NOT REMOVE THIS OR YOU WON'T BE ABLE TO USE ANY KEYBIND
|
|
||||||
submap = global # This is required for catchall to work
|
|
||||||
|
|
||||||
# Defaults
|
|
||||||
source=~/.config/hypr/hyprland/env.conf
|
|
||||||
source=~/.config/hypr/hyprland/execs.conf
|
|
||||||
source=~/.config/hypr/hyprland/general.conf
|
|
||||||
source=~/.config/hypr/hyprland/rules.conf
|
|
||||||
source=~/.config/hypr/hyprland/colors.conf
|
|
||||||
source=~/.config/hypr/hyprland/keybinds.conf
|
|
||||||
|
|
||||||
# Custom
|
|
||||||
source=~/.config/hypr/custom/env.conf
|
|
||||||
source=~/.config/hypr/custom/execs.conf
|
|
||||||
source=~/.config/hypr/custom/general.conf
|
|
||||||
source=~/.config/hypr/custom/rules.conf
|
|
||||||
source=~/.config/hypr/custom/keybinds.conf
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
getdate() {
|
|
||||||
date '+%Y-%m-%d_%H.%M.%S'
|
|
||||||
}
|
|
||||||
getaudiooutput() {
|
|
||||||
pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2
|
|
||||||
}
|
|
||||||
getactivemonitor() {
|
|
||||||
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
|
||||||
}
|
|
||||||
|
|
||||||
xdgvideo="$(xdg-user-dir VIDEOS)"
|
|
||||||
if [[ $xdgvideo = "$HOME" ]]; then
|
|
||||||
unset xdgvideo
|
|
||||||
fi
|
|
||||||
mkdir -p "${xdgvideo:-$HOME/Videos}"
|
|
||||||
cd "${xdgvideo:-$HOME/Videos}" || exit
|
|
||||||
|
|
||||||
if pgrep wf-recorder > /dev/null; then
|
|
||||||
notify-send "Recording Stopped" "Stopped" -a 'Recorder' &
|
|
||||||
pkill wf-recorder &
|
|
||||||
else
|
|
||||||
if [[ "$1" == "--fullscreen-sound" ]]; then
|
|
||||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
|
||||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)"
|
|
||||||
elif [[ "$1" == "--fullscreen" ]]; then
|
|
||||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
|
||||||
wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t
|
|
||||||
else
|
|
||||||
if ! region="$(slurp 2>&1)"; then
|
|
||||||
notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown
|
|
||||||
if [[ "$1" == "--sound" ]]; then
|
|
||||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)"
|
|
||||||
else
|
|
||||||
wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
hyprctl dispatch "$1" $(((($(hyprctl activeworkspace -j | jq -r .id) - 1) / 10) * 10 + $2))
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// vim: set ft=glsl:
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
varying highp vec2 v_texcoord;
|
|
||||||
uniform highp sampler2D tex;
|
|
||||||
|
|
||||||
#define STRENGTH 0.0027
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 center = vec2(0.5, 0.5);
|
|
||||||
vec2 offset = (v_texcoord - center) * STRENGTH;
|
|
||||||
|
|
||||||
float rSquared = dot(offset, offset);
|
|
||||||
float distortion = 1.0 + 1.0 * rSquared;
|
|
||||||
vec2 distortedOffset = offset * distortion;
|
|
||||||
|
|
||||||
vec2 redOffset = vec2(distortedOffset.x, distortedOffset.y);
|
|
||||||
vec2 blueOffset = vec2(distortedOffset.x, distortedOffset.y);
|
|
||||||
|
|
||||||
vec4 redColor = texture2D(tex, v_texcoord + redOffset);
|
|
||||||
vec4 blueColor = texture2D(tex, v_texcoord + blueOffset);
|
|
||||||
|
|
||||||
gl_FragColor = vec4(redColor.r, texture2D(tex, v_texcoord).g, blueColor.b, 1.0);
|
|
||||||
}
|
|
||||||
@@ -1,511 +0,0 @@
|
|||||||
#version 100
|
|
||||||
precision highp float;
|
|
||||||
varying highp vec2 v_texcoord;
|
|
||||||
varying highp vec3 v_pos;
|
|
||||||
uniform highp sampler2D tex;
|
|
||||||
uniform lowp float time;
|
|
||||||
|
|
||||||
#define BORDER_COLOR vec4(vec3(0.0, 0.0, 0.0), 1.0) // black border
|
|
||||||
#define BORDER_RADIUS 1.0 // larger vignette radius
|
|
||||||
#define BORDER_SIZE 0.01 // small border size
|
|
||||||
#define CHROMATIC_ABERRATION_STRENGTH 0.00
|
|
||||||
#define DENOISE_INTENSITY 0.0001 //
|
|
||||||
#define DISTORTION_AMOUNT 0.00 // moderate distortion amount
|
|
||||||
#define HDR_BLOOM 0.75 // bloom intensity
|
|
||||||
#define HDR_BRIGHTNESS 0.011 // brightness
|
|
||||||
#define HDR_CONTRAST 0.011 // contrast
|
|
||||||
#define HDR_SATURATION 1.0// saturation
|
|
||||||
#define LENS_DISTORTION_AMOUNT 0.0
|
|
||||||
#define NOISE_THRESHOLD 0.0001
|
|
||||||
#define PHOSPHOR_BLUR_AMOUNT 0.77 // Amount of blur for phosphor glow
|
|
||||||
#define PHOSPHOR_GLOW_AMOUNT 0.77 // Amount of phosphor glow
|
|
||||||
#define SAMPLING_RADIUS 0.0001
|
|
||||||
#define SCANLINE_FREQUENCY 540.0
|
|
||||||
#define SCANLINE_THICKNESS 0.0507
|
|
||||||
#define SCANLINE_TIME time * 471.24
|
|
||||||
#define SHARPNESS 0.25
|
|
||||||
#define SUPERSAMPLING_SAMPLES 16.0
|
|
||||||
#define VIGNETTE_RADIUS 0.0 // larger vignette radius
|
|
||||||
#define PI 3.14159265359
|
|
||||||
#define TWOPI 6.28318530718
|
|
||||||
|
|
||||||
vec2 applyBarrelDistortion(vec2 coord, float amt) {
|
|
||||||
vec2 p = coord.xy / vec2(1.0);
|
|
||||||
vec2 v = p * 2.0 - vec2(1.0);
|
|
||||||
float r = dot(v, v);
|
|
||||||
float k = 1.0 + pow(r, 2.0) * pow(amt, 2.0);
|
|
||||||
vec2 result = v * k;
|
|
||||||
return vec2(0.5, 0.5) + 0.5 * result.xy;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 applyColorCorrection(vec4 color) {
|
|
||||||
color.rgb *= vec3(1.0, 0.79, 0.89);
|
|
||||||
return vec4(color.rgb, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 applyBorder(vec2 tc, vec4 color, float borderSize, vec4 borderColor) {
|
|
||||||
float dist_x = min(tc.x, 1.0 - tc.x);
|
|
||||||
float dist_y = min(tc.y, 1.0 - tc.y);
|
|
||||||
float dist = min(dist_x, dist_y) * -1.0;
|
|
||||||
float border = smoothstep(borderSize, 0.0, dist);
|
|
||||||
border += smoothstep(borderSize, 0.0, dist);
|
|
||||||
return mix(color, borderColor, border);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 applyFakeHDR(vec4 color, float brightness, float contrast, float saturation, float bloom) {
|
|
||||||
color.rgb = (color.rgb - vec3(0.5)) * exp2(brightness) + vec3(0.5);
|
|
||||||
vec3 crtfactor = vec3(1.05, 0.92, 1.0);
|
|
||||||
color.rgb = pow(color.rgb, crtfactor);
|
|
||||||
// // NTSC
|
|
||||||
// vec3 lumCoeff = vec3(0.2125, 0.7154, 0.0721);
|
|
||||||
|
|
||||||
// // BT.709
|
|
||||||
// vec3 lumCoeff = vec3(0.299, 0.587, 0.114);
|
|
||||||
|
|
||||||
// BT.2020
|
|
||||||
vec3 lumCoeff = vec3(0.2627, 0.6780, 0.0593);
|
|
||||||
|
|
||||||
// // Warm NTSC
|
|
||||||
// vec3 lumCoeff = vec3(0.2125, 0.7010, 0.0865);
|
|
||||||
|
|
||||||
float luminance = dot(color.rgb, lumCoeff);
|
|
||||||
luminance = pow(luminance, 2.2);
|
|
||||||
color.rgb = mix(vec3(luminance), color.rgb, saturation);
|
|
||||||
color.rgb = mix(color.rgb, vec3(1.0), pow(max(0.0, luminance - 1.0 + bloom), 4.0));
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 applyVignette(vec4 color) {
|
|
||||||
vec2 center = vec2(0.5, 0.5); // center of screen
|
|
||||||
float radius = VIGNETTE_RADIUS; // radius of vignette effect
|
|
||||||
float softness = 1.0; // softness of vignette effect
|
|
||||||
float intensity = 0.7; // intensity of vignette effect
|
|
||||||
vec2 offset = v_texcoord - center; // offset from center of screen
|
|
||||||
float distance = length(offset); // distance from center of screen
|
|
||||||
float alpha = smoothstep(radius, radius - radius * softness, distance) * intensity; // calculate alpha value for vignette effect
|
|
||||||
return mix(vec4(0.0, 0.0, 0.0, alpha), color, alpha); // mix black with color using calculated alpha value
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 applyPhosphorGlow(vec2 tc, vec4 color, sampler2D tex) {
|
|
||||||
// Calculate average color value of the texture
|
|
||||||
vec4 texelColor = color;
|
|
||||||
float averageColor = (texelColor.r + texelColor.g + texelColor.b) / 3.0;
|
|
||||||
|
|
||||||
// Determine brightness-dependent color factor
|
|
||||||
float factor = mix(
|
|
||||||
mix(0.09,
|
|
||||||
mix(0.005, 0.0075, (averageColor - 0.1) / 0.1),
|
|
||||||
step(0.01, averageColor)), 0.0005,
|
|
||||||
step(0.02, averageColor));
|
|
||||||
// Apply phosphor glow effect
|
|
||||||
vec4 sum = vec4(0.0);
|
|
||||||
vec4 pixels[9];
|
|
||||||
pixels[0] = texture2D(tex, tc - vec2(0.001, 0.001));
|
|
||||||
pixels[1] = texture2D(tex, tc - vec2(0.001, 0.0));
|
|
||||||
pixels[2] = texture2D(tex, tc - vec2(0.001, -0.001));
|
|
||||||
pixels[3] = texture2D(tex, tc - vec2(0.0, 0.001));
|
|
||||||
pixels[4] = texture2D(tex, tc);
|
|
||||||
pixels[5] = texture2D(tex, tc + vec2(0.001, 0.001));
|
|
||||||
pixels[6] = texture2D(tex, tc + vec2(0.001, 0.0));
|
|
||||||
pixels[7] = texture2D(tex, tc + vec2(0.001, -0.001));
|
|
||||||
pixels[8] = texture2D(tex, tc + vec2(0.0, 0.001));
|
|
||||||
|
|
||||||
// Perform operations on input pixels in parallel
|
|
||||||
sum = pixels[0]
|
|
||||||
+ pixels[1]
|
|
||||||
+ pixels[2]
|
|
||||||
+ pixels[3]
|
|
||||||
+ pixels[4]
|
|
||||||
+ pixels[5]
|
|
||||||
+ pixels[6]
|
|
||||||
+ pixels[7]
|
|
||||||
+ pixels[8];
|
|
||||||
sum /= 9.0;
|
|
||||||
sum += texture2D(tex, tc - vec2(0.01, 0.01)) * 0.001;
|
|
||||||
sum += texture2D(tex, tc - vec2(0.0, 0.01)) * 0.001;
|
|
||||||
sum += texture2D(tex, tc - vec2(-0.01, 0.01)) * 0.001;
|
|
||||||
sum += texture2D(tex, tc - vec2(0.01, 0.0)) * 0.001;
|
|
||||||
sum += color * PHOSPHOR_BLUR_AMOUNT;
|
|
||||||
sum += texture2D(tex, tc - vec2(-0.01, 0.0)) * 0.001;
|
|
||||||
sum += texture2D(tex, tc - vec2(0.01, -0.01)) * 0.001;
|
|
||||||
sum += texture2D(tex, tc - vec2(0.0, -0.01)) * 0.001;
|
|
||||||
sum += texture2D(tex, tc - vec2(-0.01, -0.01)) * 0.001;
|
|
||||||
sum *= PHOSPHOR_GLOW_AMOUNT;
|
|
||||||
|
|
||||||
// Initialize sum_sum_factor to zero
|
|
||||||
vec4 sum_sum_factor = vec4(0.0);
|
|
||||||
// Compute sum_j for i = -1
|
|
||||||
vec4 sum_j = vec4(0.0);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(-1, -1) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(0, -1) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(1, -1) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(0, 0) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01);
|
|
||||||
sum_sum_factor += sum_j * vec4(0.011);
|
|
||||||
|
|
||||||
// Compute sum_j for i = 0
|
|
||||||
sum_j = vec4(0.0);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(0, 0) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01);
|
|
||||||
sum_sum_factor += sum_j * vec4(0.011);
|
|
||||||
|
|
||||||
// Compute sum_j for i = 1
|
|
||||||
sum_j = vec4(0.0);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01);
|
|
||||||
sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01);
|
|
||||||
sum_sum_factor += sum_j * vec4(0.011);
|
|
||||||
color += mix(sum_sum_factor * sum_sum_factor * vec4(factor), sum, 0.5);
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 applyAdaptiveSharpen(vec2 tc, vec4 color, sampler2D tex) {
|
|
||||||
vec4 color_tl = texture2D(tex, tc + vec2(-1.0, -1.0) * 0.5 / 2160.0);
|
|
||||||
vec4 color_tr = texture2D(tex, tc + vec2(1.0, -1.0) * 0.5 / 2160.0);
|
|
||||||
vec4 color_bl = texture2D(tex, tc + vec2(-1.0, 1.0) * 0.5 / 2160.0);
|
|
||||||
vec4 color_br = texture2D(tex, tc + vec2(1.0, 1.0) * 0.5 / 2160.0);
|
|
||||||
float sharpness = SHARPNESS;
|
|
||||||
vec3 color_no_alpha = color.rgb;
|
|
||||||
vec3 color_tl_no_alpha = color_tl.rgb;
|
|
||||||
vec3 color_tr_no_alpha = color_tr.rgb;
|
|
||||||
vec3 color_bl_no_alpha = color_bl.rgb;
|
|
||||||
vec3 color_br_no_alpha = color_br.rgb;
|
|
||||||
float delta = (dot(color_no_alpha, vec3(0.333333)) + dot(color_tl_no_alpha, vec3(0.333333)) + dot(color_tr_no_alpha, vec3(0.333333)) + dot(color_bl_no_alpha, vec3(0.333333)) + dot(color_br_no_alpha, vec3(0.333333))) * 0.2 - dot(color_no_alpha, vec3(0.333333));
|
|
||||||
vec3 sharp_color_no_alpha = color_no_alpha + min(vec3(0.0), vec3(delta * sharpness));
|
|
||||||
vec4 sharp_color = vec4(sharp_color_no_alpha, color.a);
|
|
||||||
return sharp_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 applyScanlines(vec2 tc, vec4 color) {
|
|
||||||
float scanline = (cos(tc.y * SCANLINE_FREQUENCY + SCANLINE_TIME) *
|
|
||||||
sin(tc.y * SCANLINE_FREQUENCY + SCANLINE_TIME)) * SCANLINE_THICKNESS;
|
|
||||||
float alpha = clamp(1.0 - abs(scanline), 0.0, 1.0);
|
|
||||||
return vec4(color.rgb * alpha, color.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 applyChromaticAberration(vec2 uv, vec4 color) {
|
|
||||||
vec2 center = vec2(0.5, 0.5); // center of the screen
|
|
||||||
vec2 offset = (uv - center) * CHROMATIC_ABERRATION_STRENGTH; // calculate the offset from the center
|
|
||||||
|
|
||||||
// apply lens distortion
|
|
||||||
float rSquared = dot(offset, offset);
|
|
||||||
float distortion = 1.0 + LENS_DISTORTION_AMOUNT * rSquared;
|
|
||||||
vec2 distortedOffset = offset * distortion;
|
|
||||||
|
|
||||||
// apply chromatic aberration
|
|
||||||
vec2 redOffset = vec2(distortedOffset.x * 1.00, distortedOffset.y * 1.00);
|
|
||||||
vec2 blueOffset = vec2(distortedOffset.x * 1.00, distortedOffset.y * 1.00);
|
|
||||||
|
|
||||||
vec4 redColor = texture2D(tex, uv + redOffset);
|
|
||||||
vec4 blueColor = texture2D(tex, uv + blueOffset);
|
|
||||||
|
|
||||||
vec4 result = vec4(redColor.r, color.g, blueColor.b, color.a);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 reduceGlare(vec4 color) {
|
|
||||||
// Calculate the intensity of the color by taking the average of the RGB components
|
|
||||||
float intensity = (color.r + color.g + color.b) / 3.0;
|
|
||||||
// Set the maximum intensity that can be considered for glare
|
|
||||||
float maxIntensity = 0.98;
|
|
||||||
// Use smoothstep to create a smooth transition from no glare to full glare
|
|
||||||
// based on the intensity of the color and the maximum intensity
|
|
||||||
float glareIntensity = smoothstep(maxIntensity - 0.02, maxIntensity, intensity);
|
|
||||||
// Set the amount of glare to apply to the color
|
|
||||||
float glareAmount = 0.02;
|
|
||||||
// Mix the original color with the reduced color that has glare applied to it
|
|
||||||
vec3 reducedColor = mix(color.rgb, vec3(glareIntensity), glareAmount);
|
|
||||||
// Return the reduced color with the original alpha value
|
|
||||||
return vec4(reducedColor, color.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply a fake HDR effect to the input color.
|
|
||||||
// Parameters:
|
|
||||||
// - inputColor: the color to apply the effect to.
|
|
||||||
// - brightness: the brightness of the image. Should be a value between 0 and 1.
|
|
||||||
// - contrast: the contrast of the image. Should be a value between 0 and 1.
|
|
||||||
// - saturation: the saturation of the image. Should be a value between 0 and 2.
|
|
||||||
// - bloom: the intensity of the bloom effect. Should be a value between 0 and 1.
|
|
||||||
vec4 applyFakeHDREffect(vec4 inputColor, float brightness, float contrast, float saturation, float bloom) {
|
|
||||||
const float minBrightness = 0.0;
|
|
||||||
const float maxBrightness = 1.0;
|
|
||||||
const float minContrast = 0.0;
|
|
||||||
const float maxContrast = 1.0;
|
|
||||||
const float minSaturation = 0.0;
|
|
||||||
const float maxSaturation = 2.0;
|
|
||||||
const float minBloom = 0.0;
|
|
||||||
const float maxBloom = 1.0;
|
|
||||||
|
|
||||||
// Check input parameters for validity
|
|
||||||
if (brightness < minBrightness || brightness > maxBrightness) {
|
|
||||||
return vec4(0.0, 0.0, 0.0, 1.0); // Return black with alpha of 1.0 to indicate error
|
|
||||||
}
|
|
||||||
if (contrast < minContrast || contrast > maxContrast) {
|
|
||||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
if (saturation < minSaturation || saturation > maxSaturation) {
|
|
||||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
if (bloom < minBloom || bloom > maxBloom) {
|
|
||||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply brightness and contrast
|
|
||||||
vec3 color = inputColor.rgb;
|
|
||||||
color = (color - vec3(0.5)) * exp2(brightness * 10.0) + vec3(0.5);
|
|
||||||
color = mix(vec3(0.5), color, pow(contrast * 4.0 + 1.0, 2.0));
|
|
||||||
|
|
||||||
// // NTSC
|
|
||||||
// vec3 lumCoeff = vec3(0.2125, 0.7154, 0.0721);
|
|
||||||
|
|
||||||
// // BT.709
|
|
||||||
// vec3 lumCoeff = vec3(0.299, 0.587, 0.114);
|
|
||||||
|
|
||||||
// // BT.2020
|
|
||||||
// vec3 lumCoeff = vec3(0.2627, 0.6780, 0.0593);
|
|
||||||
|
|
||||||
// Warm NTSC
|
|
||||||
vec3 lumCoeff = vec3(0.2125, 0.7010, 0.0865);
|
|
||||||
|
|
||||||
// Apply saturation
|
|
||||||
float luminance = dot(color, lumCoeff);
|
|
||||||
vec3 grey = vec3(luminance);
|
|
||||||
color = mix(grey, color, saturation);
|
|
||||||
|
|
||||||
// Apply bloom effect
|
|
||||||
float threshold = 1.0 - bloom;
|
|
||||||
vec3 bloomColor = max(color - threshold, vec3(0.0));
|
|
||||||
bloomColor = pow(bloomColor, vec3(2.0));
|
|
||||||
bloomColor = mix(vec3(0.0), bloomColor, pow(min(luminance, threshold), 4.0));
|
|
||||||
color += bloomColor;
|
|
||||||
|
|
||||||
return vec4(color, inputColor.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 bilateralFilter(sampler2D tex, vec2 uv, vec4 color, float sampleRadius, float noiseThreshold, float intensity) {
|
|
||||||
vec4 filteredColor = vec4(0.0);
|
|
||||||
float totalWeight = 0.0;
|
|
||||||
|
|
||||||
// Top-left pixel
|
|
||||||
vec4 sample = texture2D(tex, uv + vec2(-1.0, -1.0));
|
|
||||||
float dist = length(vec2(-1.0, -1.0));
|
|
||||||
float colorDist = length(sample - color);
|
|
||||||
float weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
|
||||||
filteredColor += sample * weight;
|
|
||||||
totalWeight += weight;
|
|
||||||
|
|
||||||
// Top pixel
|
|
||||||
sample = texture2D(tex, uv + vec2(0.0, -1.0));
|
|
||||||
dist = length(vec2(0.0, -1.0));
|
|
||||||
colorDist = length(sample - color);
|
|
||||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
|
||||||
filteredColor += sample * weight;
|
|
||||||
totalWeight += weight;
|
|
||||||
|
|
||||||
// Top-right pixel
|
|
||||||
sample = texture2D(tex, uv + vec2(1.0, -1.0));
|
|
||||||
dist = length(vec2(1.0, -1.0));
|
|
||||||
colorDist = length(sample - color);
|
|
||||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
|
||||||
filteredColor += sample * weight;
|
|
||||||
totalWeight += weight;
|
|
||||||
|
|
||||||
// Left pixel
|
|
||||||
sample = texture2D(tex, uv + vec2(-1.0, 0.0));
|
|
||||||
dist = length(vec2(-1.0, 0.0));
|
|
||||||
colorDist = length(sample - color);
|
|
||||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
|
||||||
filteredColor += sample * weight;
|
|
||||||
totalWeight += weight;
|
|
||||||
|
|
||||||
// Center pixel
|
|
||||||
sample = texture2D(tex, uv);
|
|
||||||
dist = 0.0;
|
|
||||||
colorDist = length(sample - color);
|
|
||||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
|
||||||
filteredColor += sample * weight;
|
|
||||||
totalWeight += weight;
|
|
||||||
|
|
||||||
// Right pixel
|
|
||||||
sample = texture2D(tex, uv + vec2(1.0, 0.0));
|
|
||||||
dist = length(vec2(1.0, 0.0));
|
|
||||||
colorDist = length(sample - color);
|
|
||||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
|
||||||
filteredColor += sample * weight;
|
|
||||||
totalWeight += weight;
|
|
||||||
|
|
||||||
// Bottom-left pixel
|
|
||||||
sample = texture2D(tex, uv + vec2(-1.0, 1.0));
|
|
||||||
dist = length(vec2(-1.0, 1.0));
|
|
||||||
colorDist = length(sample - color);
|
|
||||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
|
||||||
filteredColor += sample * weight;
|
|
||||||
totalWeight += weight;
|
|
||||||
|
|
||||||
// Bottom pixel
|
|
||||||
sample = texture2D(tex, uv + vec2(0.0, sampleRadius));
|
|
||||||
dist = length(vec2(0.0, sampleRadius));
|
|
||||||
colorDist = length(sample - color);
|
|
||||||
weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius));
|
|
||||||
filteredColor += sample * weight;
|
|
||||||
totalWeight += weight;
|
|
||||||
|
|
||||||
filteredColor /= totalWeight;
|
|
||||||
return mix(color, filteredColor, step(noiseThreshold, length(filteredColor - color)));
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 supersample(sampler2D tex, vec2 uv, float sampleRadius, float noiseThreshold, float intensity) {
|
|
||||||
float radiusSq = sampleRadius * sampleRadius;
|
|
||||||
vec2 poissonDisk;
|
|
||||||
vec4 color = vec4(0.0);
|
|
||||||
|
|
||||||
float r1_0 = sqrt(0.0 / 16.0);
|
|
||||||
float r2_0 = fract(1.0 / 3.0);
|
|
||||||
float theta_0 = TWOPI * r2_0;
|
|
||||||
poissonDisk = vec2(r1_0 * cos(theta_0), r1_0 * sin(theta_0));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_1 = sqrt(1.0 / 16.0);
|
|
||||||
float r2_1 = fract(2.0 / 3.0);
|
|
||||||
float theta_1 = TWOPI * r2_1;
|
|
||||||
poissonDisk = vec2(r1_1 * cos(theta_1), r1_1 * sin(theta_1));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_2 = sqrt(2.0 / 16.0);
|
|
||||||
float r2_2 = fract(3.0 / 3.0);
|
|
||||||
float theta_2 = TWOPI * r2_2;
|
|
||||||
poissonDisk = vec2(r1_2 * cos(theta_2), r1_2 * sin(theta_2));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_3 = sqrt(3.0 / 16.0);
|
|
||||||
float r2_3 = fract(4.0 / 3.0);
|
|
||||||
float theta_3 = TWOPI * r2_3;
|
|
||||||
poissonDisk = vec2(r1_3 * cos(theta_3), r1_3 * sin(theta_3));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_4 = sqrt(4.0 / 16.0);
|
|
||||||
float r2_4 = fract(5.0 / 3.0);
|
|
||||||
float theta_4 = TWOPI * r2_4;
|
|
||||||
poissonDisk = vec2(r1_4 * cos(theta_4), r1_4 * sin(theta_4));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_5 = sqrt(5.0 / 16.0);
|
|
||||||
float r2_5 = fract(6.0 / 3.0);
|
|
||||||
float theta_5 = TWOPI * r2_5;
|
|
||||||
poissonDisk = vec2(r1_5 * cos(theta_5), r1_5 * sin(theta_5));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_6 = sqrt(6.0 / 16.0);
|
|
||||||
float r2_6 = fract(7.0 / 3.0);
|
|
||||||
float theta_6 = TWOPI * r2_6;
|
|
||||||
poissonDisk = vec2(r1_6 * cos(theta_6), r1_6 * sin(theta_6));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_7 = sqrt(7.0 / 16.0);
|
|
||||||
float r2_7 = fract(8.0 / 3.0);
|
|
||||||
float theta_7 = TWOPI * r2_7;
|
|
||||||
poissonDisk = vec2(r1_7 * cos(theta_7), r1_7 * sin(theta_7));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_8 = sqrt(8.0 / 16.0);
|
|
||||||
float r2_8 = fract(9.0 / 3.0);
|
|
||||||
float theta_8 = TWOPI * r2_8;
|
|
||||||
poissonDisk = vec2(r1_8 * cos(theta_8), r1_8 * sin(theta_8));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_9 = sqrt(9.0 / 16.0);
|
|
||||||
float r2_9 = fract(10.0 / 3.0);
|
|
||||||
float theta_9 = TWOPI * r2_9;
|
|
||||||
poissonDisk = vec2(r1_9 * cos(theta_9), r1_9 * sin(theta_9));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_10 = sqrt(10.0 / 16.0);
|
|
||||||
float r2_10 = fract(11.0 / 3.0);
|
|
||||||
float theta_10 = TWOPI * r2_10;
|
|
||||||
poissonDisk = vec2(r1_10 * cos(theta_10), r1_10 * sin(theta_10));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_11 = sqrt(11.0 / 16.0);
|
|
||||||
float r2_11 = fract(12.0 / 3.0);
|
|
||||||
float theta_11 = TWOPI * r2_11;
|
|
||||||
poissonDisk = vec2(r1_11 * cos(theta_11), r1_11 * sin(theta_11));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_12 = sqrt(12.0 / 16.0);
|
|
||||||
float r2_12 = fract(13.0 / 3.0);
|
|
||||||
float theta_12 = TWOPI * r2_12;
|
|
||||||
poissonDisk = vec2(r1_12 * cos(theta_12), r1_12 * sin(theta_12));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_13 = sqrt(13.0 / 16.0);
|
|
||||||
float r2_13 = fract(14.0 / 3.0);
|
|
||||||
float theta_13 = TWOPI * r2_13;
|
|
||||||
poissonDisk = vec2(r1_13 * cos(theta_13), r1_13 * sin(theta_13));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_14 = sqrt(14.0 / 16.0);
|
|
||||||
float r2_14 = fract(15.0 / 3.0);
|
|
||||||
float theta_14 = TWOPI * r2_14;
|
|
||||||
poissonDisk = vec2(r1_14 * cos(theta_14), r1_14 * sin(theta_14));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
float r1_15 = sqrt(15.0 / 16.0);
|
|
||||||
float r2_15 = fract(16.0 / 3.0);
|
|
||||||
float theta_15 = TWOPI * r2_15;
|
|
||||||
poissonDisk = vec2(r1_15 * cos(theta_15), r1_15 * sin(theta_15));
|
|
||||||
color += texture2D(tex, uv + poissonDisk * sampleRadius);
|
|
||||||
|
|
||||||
return bilateralFilter(tex, uv, color, sampleRadius, noiseThreshold, intensity);
|
|
||||||
}
|
|
||||||
void main() {
|
|
||||||
vec2 tc_no_dist = v_texcoord;
|
|
||||||
|
|
||||||
vec2 tc = applyBarrelDistortion(tc_no_dist, DISTORTION_AMOUNT);
|
|
||||||
|
|
||||||
// [-1, 1]
|
|
||||||
vec2 tc_no_dist_symmetric = tc_no_dist * 2.0 - 1.0;
|
|
||||||
|
|
||||||
// [0,1]
|
|
||||||
vec2 tc_no_dist_normalized = (tc_no_dist_symmetric + 1.0) / 2.0;
|
|
||||||
|
|
||||||
// vec4 color = texture2D(tex, tc);
|
|
||||||
vec4 color = supersample(tex, tc, SAMPLING_RADIUS, NOISE_THRESHOLD, DENOISE_INTENSITY);
|
|
||||||
|
|
||||||
color = applyAdaptiveSharpen(tc, color, tex);
|
|
||||||
|
|
||||||
color = applyPhosphorGlow(tc, color, tex);
|
|
||||||
|
|
||||||
color = reduceGlare(color);
|
|
||||||
|
|
||||||
color = mix(applyFakeHDREffect(color, HDR_BRIGHTNESS, HDR_CONTRAST, HDR_SATURATION, HDR_BLOOM), color, 0.5);
|
|
||||||
|
|
||||||
color = applyColorCorrection(color);
|
|
||||||
|
|
||||||
color /= SUPERSAMPLING_SAMPLES;
|
|
||||||
|
|
||||||
color = mix(applyChromaticAberration(tc, color), color, 0.25);
|
|
||||||
|
|
||||||
color = mix(color, applyVignette(color), 0.37);
|
|
||||||
|
|
||||||
color = applyBorder(tc_no_dist_normalized, color, 1.0 - BORDER_SIZE * BORDER_RADIUS, BORDER_COLOR);
|
|
||||||
|
|
||||||
color = mix(applyBorder(tc, color, BORDER_SIZE, BORDER_COLOR), color, 0.05);
|
|
||||||
|
|
||||||
color = applyScanlines(tc, color);
|
|
||||||
|
|
||||||
gl_FragColor = color;
|
|
||||||
gl_FragColor.a = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
|
|
||||||
precision highp float;
|
|
||||||
varying vec2 v_texcoord;
|
|
||||||
uniform sampler2D tex;
|
|
||||||
uniform float time;
|
|
||||||
|
|
||||||
void warpco(inout vec2 tc) {
|
|
||||||
tc -= 0.5;
|
|
||||||
tc *= length(tc) * 2.0;
|
|
||||||
tc += 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
float rand1d(float seed) {
|
|
||||||
return sin(seed*1454.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
float rand2d(vec2 co)
|
|
||||||
{
|
|
||||||
return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 rgb(in vec2 tc, float freq, float amp, inout vec4 centre) {
|
|
||||||
vec2 off = vec2(1.0/800.0, 0.0) * sin(tc.t * freq + time) * amp;
|
|
||||||
vec2 off2 = vec2(1.0/800.0, 0.0) * sin(tc.t * freq - time * 1.5) * amp;
|
|
||||||
centre = texture2D(tex, tc);
|
|
||||||
return vec3(texture2D(tex, tc-off).r, centre.g, texture2D(tex, tc+off2).b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// vec2 px = 1.0 / textureSize(tex, 0).st;
|
|
||||||
vec2 tc = v_texcoord;
|
|
||||||
warpco(tc);
|
|
||||||
tc = mix(v_texcoord, tc, sin(time * 2.0)*0.07);
|
|
||||||
tc.x += rand2d(floor(tc * 20.0 + floor(time * 2.5))) * 0.01;
|
|
||||||
tc.x += rand1d(floor(tc.x * 40.0)) * 0.005 * rand1d(time * 0.001);
|
|
||||||
tc.y += sin(tc.x + time) * 0.02;
|
|
||||||
vec4 centre;
|
|
||||||
vec3 bent = rgb(tc, 100.0, 5.0, centre);
|
|
||||||
vec3 col = mix(centre.rgb, bent, sin(time));
|
|
||||||
gl_FragColor = vec4(col, centre.a);
|
|
||||||
// gl_FragColor = vec4(texture2D(tex, v_texcoord));
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
// vim: set ft=glsl:
|
|
||||||
// blue light filter shader
|
|
||||||
// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux
|
|
||||||
|
|
||||||
precision mediump float;
|
|
||||||
varying vec2 v_texcoord;
|
|
||||||
uniform sampler2D tex;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
|
|
||||||
vec4 pixColor = texture2D(tex, v_texcoord);
|
|
||||||
|
|
||||||
// red
|
|
||||||
pixColor[0] *= 0.7;
|
|
||||||
// green
|
|
||||||
pixColor[1] *= 0.6;
|
|
||||||
// blue
|
|
||||||
pixColor[2] *= 0.5;
|
|
||||||
|
|
||||||
gl_FragColor = pixColor;
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
// vim: set ft=glsl:
|
|
||||||
// blue light filter shader
|
|
||||||
// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux
|
|
||||||
|
|
||||||
precision mediump float;
|
|
||||||
varying vec2 v_texcoord;
|
|
||||||
uniform sampler2D tex;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 pixColor = texture2D(tex, v_texcoord);
|
|
||||||
pixColor.rgb = 1.0 - pixColor.rgb;
|
|
||||||
gl_FragColor = pixColor;
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// -*- mode:c -*-
|
|
||||||
precision lowp float;
|
|
||||||
varying vec2 v_texcoord;
|
|
||||||
uniform sampler2D tex;
|
|
||||||
|
|
||||||
float distanceSquared(vec3 pixColor, vec3 solarizedColor) {
|
|
||||||
vec3 distanceVector = pixColor - solarizedColor;
|
|
||||||
return dot(distanceVector, distanceVector);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec3 solarized[16];
|
|
||||||
solarized[0] = vec3(0.,0.169,0.212);
|
|
||||||
solarized[1] = vec3(0.027,0.212,0.259);
|
|
||||||
solarized[2] = vec3(0.345,0.431,0.459);
|
|
||||||
solarized[3] = vec3(0.396,0.482,0.514);
|
|
||||||
solarized[4] = vec3(0.514,0.58,0.588);
|
|
||||||
solarized[5] = vec3(0.576,0.631,0.631);
|
|
||||||
solarized[6] = vec3(0.933,0.91,0.835);
|
|
||||||
solarized[7] = vec3(0.992,0.965,0.89);
|
|
||||||
solarized[8] = vec3(0.71,0.537,0.);
|
|
||||||
solarized[9] = vec3(0.796,0.294,0.086);
|
|
||||||
solarized[10] = vec3(0.863,0.196,0.184);
|
|
||||||
solarized[11] = vec3(0.827,0.212,0.51);
|
|
||||||
solarized[12] = vec3(0.424,0.443,0.769);
|
|
||||||
solarized[13] = vec3(0.149,0.545,0.824);
|
|
||||||
solarized[14] = vec3(0.165,0.631,0.596);
|
|
||||||
solarized[15] = vec3(0.522,0.6,0.);
|
|
||||||
|
|
||||||
vec3 pixColor = vec3(texture2D(tex, v_texcoord));
|
|
||||||
int closest = 0;
|
|
||||||
float closestDistanceSquared = distanceSquared(pixColor, solarized[0]);
|
|
||||||
for (int i = 1; i < 15; i++) {
|
|
||||||
float newDistanceSquared = distanceSquared(pixColor, solarized[i]);
|
|
||||||
if (newDistanceSquared < closestDistanceSquared) {
|
|
||||||
closest = i;
|
|
||||||
closestDistanceSquared = newDistanceSquared;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gl_FragColor = vec4(solarized[closest], 1.);
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
$text_color = rgba({{colors.primary_fixed.default.hex_stripped}}FF)
|
|
||||||
$entry_background_color = rgba({{colors.on_primary_fixed.default.hex_stripped}}11)
|
|
||||||
$entry_border_color = rgba({{colors.outline.default.hex_stripped}}55)
|
|
||||||
$entry_color = rgba({{colors.primary_fixed.default.hex_stripped}}FF)
|
|
||||||
$font_family = Rubik
|
|
||||||
$font_family_clock = Space Grotesk DemiBold
|
|
||||||
$font_material_symbols = Material Symbols Rounded
|
|
||||||
|
|
||||||
background {
|
|
||||||
color = rgba(181818FF)
|
|
||||||
|
|
||||||
path = {{image}}
|
|
||||||
blur_size = 15
|
|
||||||
blur_passes = 4
|
|
||||||
brightness = 0.33
|
|
||||||
}
|
|
||||||
input-field {
|
|
||||||
monitor =
|
|
||||||
size = 250, 50
|
|
||||||
outline_thickness = 2
|
|
||||||
dots_size = 0.1
|
|
||||||
dots_spacing = 0.3
|
|
||||||
outer_color = $entry_border_color
|
|
||||||
inner_color = $entry_background_color
|
|
||||||
font_color = $entry_color
|
|
||||||
fade_on_empty = true
|
|
||||||
|
|
||||||
position = 0, 20
|
|
||||||
halign = center
|
|
||||||
valign = center
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
monitor =
|
|
||||||
text = $LAYOUT
|
|
||||||
color = $text_color
|
|
||||||
font_size = 14
|
|
||||||
font_family = $font_family
|
|
||||||
position = -30, 30
|
|
||||||
halign = right
|
|
||||||
valign = bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
label { # Caps Lock Warning
|
|
||||||
monitor =
|
|
||||||
text = cmd[update:250] ${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprlock/check-capslock.sh
|
|
||||||
color = $text_color
|
|
||||||
font_size = 13
|
|
||||||
font_family = $font_family
|
|
||||||
position = 0, -25
|
|
||||||
halign = center
|
|
||||||
valign = center
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
label { # Clock
|
|
||||||
monitor =
|
|
||||||
text = $TIME
|
|
||||||
color = $text_color
|
|
||||||
font_size = 65
|
|
||||||
font_family = $font_family_clock
|
|
||||||
|
|
||||||
position = 0, 300
|
|
||||||
halign = center
|
|
||||||
valign = center
|
|
||||||
}
|
|
||||||
label { # Date
|
|
||||||
monitor =
|
|
||||||
text = cmd[update:5000] date +"%A, %B %d"
|
|
||||||
color = $text_color
|
|
||||||
font_size = 17
|
|
||||||
font_family = $font_family_clock
|
|
||||||
|
|
||||||
position = 0, 240
|
|
||||||
halign = center
|
|
||||||
valign = center
|
|
||||||
}
|
|
||||||
|
|
||||||
label { # User
|
|
||||||
monitor =
|
|
||||||
text = $USER
|
|
||||||
color = $text_color
|
|
||||||
outline_thickness = 2
|
|
||||||
dots_size = 0.2 # Scale of input-field height, 0.2 - 0.8
|
|
||||||
dots_spacing = 0.2 # Scale of dots' absolute size, 0.0 - 1.0
|
|
||||||
dots_center = true
|
|
||||||
font_size = 20
|
|
||||||
font_family = $font_family
|
|
||||||
position = 0, 50
|
|
||||||
halign = center
|
|
||||||
valign = bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
label { # Status
|
|
||||||
monitor =
|
|
||||||
text = cmd[update:5000] ${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprlock/status.sh
|
|
||||||
color = $text_color
|
|
||||||
font_size = 14
|
|
||||||
font_family = $font_family
|
|
||||||
|
|
||||||
position = 30, -30
|
|
||||||
halign = left
|
|
||||||
valign = top
|
|
||||||
}
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import qs.modules.common
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var translations: ({})
|
|
||||||
property string currentLanguage: "en_US"
|
|
||||||
property var availableLanguages: ["en_US"]
|
|
||||||
property bool isScanning: false
|
|
||||||
property bool isLoading: false
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: scanLanguagesProcess
|
|
||||||
command: ["find", Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", ""), "-name", "*.json", "-exec", "basename", "{}", ".json", ";"]
|
|
||||||
running: false
|
|
||||||
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
if (data.trim().length === 0) return
|
|
||||||
|
|
||||||
var files = data.trim().split('\n')
|
|
||||||
|
|
||||||
for (var i = 0; i < files.length; i++) {
|
|
||||||
var lang = files[i].trim()
|
|
||||||
if (lang.length > 0 && root.availableLanguages.indexOf(lang) === -1) {
|
|
||||||
root.availableLanguages.push(lang)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: (exitCode, exitStatus) => {
|
|
||||||
root.isScanning = false
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
root.availableLanguages = ["en_US"]
|
|
||||||
}
|
|
||||||
root.loadTranslations()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
|
||||||
id: translationFileView
|
|
||||||
onLoaded: {
|
|
||||||
var textContent = ""
|
|
||||||
try {
|
|
||||||
textContent = text()
|
|
||||||
} catch (e) {
|
|
||||||
root.translations = {}
|
|
||||||
root.isLoading = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (textContent.length === 0) {
|
|
||||||
root.translations = {}
|
|
||||||
root.isLoading = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var jsonData = JSON.parse(textContent)
|
|
||||||
root.translations = jsonData
|
|
||||||
root.isLoading = false
|
|
||||||
} catch (e) {
|
|
||||||
root.translations = {}
|
|
||||||
root.isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onLoadFailed: (error) => {
|
|
||||||
root.translations = {}
|
|
||||||
root.isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function detectSystemLanguage() {
|
|
||||||
var locale = Qt.locale().name
|
|
||||||
return locale
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLanguageCode() {
|
|
||||||
var configLang = "auto"
|
|
||||||
try {
|
|
||||||
configLang = ConfigOptions.language.ui
|
|
||||||
} catch (e) {
|
|
||||||
configLang = "auto"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configLang === "auto") {
|
|
||||||
return detectSystemLanguage()
|
|
||||||
} else {
|
|
||||||
if (root.availableLanguages.indexOf(configLang) !== -1) {
|
|
||||||
return configLang
|
|
||||||
} else {
|
|
||||||
return detectSystemLanguage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadTranslations() {
|
|
||||||
if (root.isScanning) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetLang = getLanguageCode()
|
|
||||||
root.currentLanguage = targetLang
|
|
||||||
|
|
||||||
// Use empty translations for English (default language)
|
|
||||||
if (targetLang === "en_US" || targetLang === "en") {
|
|
||||||
root.translations = {}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if target language is available
|
|
||||||
if (root.availableLanguages.indexOf(targetLang) === -1) {
|
|
||||||
root.currentLanguage = "en_US"
|
|
||||||
root.translations = {}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load translation file
|
|
||||||
root.isLoading = true
|
|
||||||
var translationsPath = Qt.resolvedUrl(Directories.config + "/quickshell/translations/" + targetLang + ".json")
|
|
||||||
translationFileView.path = translationsPath
|
|
||||||
}
|
|
||||||
|
|
||||||
function tr(text) {
|
|
||||||
if (!text) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = text.toString()
|
|
||||||
|
|
||||||
if (root.isLoading) {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root.currentLanguage === "en_US" || root.currentLanguage === "en" || !root.translations) {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root.translations.hasOwnProperty(key)) {
|
|
||||||
var translation = root.translations[key]
|
|
||||||
if (translation && translation.toString().trim().length > 0) {
|
|
||||||
var str = translation.toString().trim()
|
|
||||||
if (str.endsWith("/*keep*/")) {
|
|
||||||
return str.substring(0, str.length - 8).trim()
|
|
||||||
} else {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return translation.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return key // Fallback to key name
|
|
||||||
}
|
|
||||||
|
|
||||||
function reloadTranslations() {
|
|
||||||
root.scanLanguages()
|
|
||||||
}
|
|
||||||
|
|
||||||
function scanLanguages() {
|
|
||||||
var translationsDir = Qt.resolvedUrl(Directories.config + "/quickshell/translations/").toString().replace("file://", "")
|
|
||||||
root.isScanning = true
|
|
||||||
scanLanguagesProcess.running = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
root.scanLanguages()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 66 KiB |
@@ -1,293 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions as CF
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
|
||||||
|
|
||||||
Variants {
|
|
||||||
id: root
|
|
||||||
readonly property bool fixedClockPosition: Config.options.background.fixedClockPosition
|
|
||||||
readonly property real fixedClockX: Config.options.background.clockX
|
|
||||||
readonly property real fixedClockY: Config.options.background.clockY
|
|
||||||
model: Quickshell.screens
|
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: bgRoot
|
|
||||||
|
|
||||||
required property var modelData
|
|
||||||
|
|
||||||
// Hide when fullscreen
|
|
||||||
property list<HyprlandWorkspace> workspacesForMonitor: Hyprland.workspaces.values.filter(workspace=>workspace.monitor && workspace.monitor.name == monitor.name)
|
|
||||||
property var activeWorkspaceWithFullscreen: workspacesForMonitor.filter(workspace=>((workspace.toplevels.values.filter(window=>window.wayland.fullscreen)[0] != undefined) && workspace.active))[0]
|
|
||||||
visible: !(activeWorkspaceWithFullscreen != undefined)
|
|
||||||
|
|
||||||
// Workspaces
|
|
||||||
property HyprlandMonitor monitor: Hyprland.monitorFor(modelData)
|
|
||||||
property list<var> relevantWindows: HyprlandData.windowList.filter(win => win.monitor == monitor?.id && win.workspace.id >= 0).sort((a, b) => a.workspace.id - b.workspace.id)
|
|
||||||
property int firstWorkspaceId: relevantWindows[0]?.workspace.id || 1
|
|
||||||
property int lastWorkspaceId: relevantWindows[relevantWindows.length - 1]?.workspace.id || 10
|
|
||||||
// Wallpaper
|
|
||||||
property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4")
|
|
||||||
|| Config.options.background.wallpaperPath.endsWith(".webm")
|
|
||||||
|| Config.options.background.wallpaperPath.endsWith(".mkv")
|
|
||||||
|| Config.options.background.wallpaperPath.endsWith(".avi")
|
|
||||||
|| Config.options.background.wallpaperPath.endsWith(".mov")
|
|
||||||
property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath
|
|
||||||
property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom
|
|
||||||
property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated
|
|
||||||
property int wallpaperWidth: modelData.width // Some reasonable init value, to be updated
|
|
||||||
property int wallpaperHeight: modelData.height // Some reasonable init value, to be updated
|
|
||||||
property real movableXSpace: (Math.min(wallpaperWidth * effectiveWallpaperScale, screen.width * preferredWallpaperScale) - screen.width) / 2
|
|
||||||
property real movableYSpace: (Math.min(wallpaperHeight * effectiveWallpaperScale, screen.height * preferredWallpaperScale) - screen.height) / 2
|
|
||||||
// Position
|
|
||||||
property real clockX: (modelData.width / 2) + ((Math.random() < 0.5 ? -1 : 1) * modelData.width)
|
|
||||||
property real clockY: (modelData.height / 2) + ((Math.random() < 0.5 ? -1 : 1) * modelData.height)
|
|
||||||
property var textHorizontalAlignment: clockX < screen.width / 3 ? Text.AlignLeft :
|
|
||||||
(clockX > screen.width * 2 / 3 ? Text.AlignRight : Text.AlignHCenter)
|
|
||||||
// Colors
|
|
||||||
property color dominantColor: Appearance.colors.colPrimary
|
|
||||||
property bool dominantColorIsDark: dominantColor.hslLightness < 0.5
|
|
||||||
property color colText: CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12))
|
|
||||||
|
|
||||||
// Layer props
|
|
||||||
screen: modelData
|
|
||||||
exclusionMode: ExclusionMode.Ignore
|
|
||||||
WlrLayershell.layer: GlobalStates.screenLocked ? WlrLayer.Top : WlrLayer.Bottom
|
|
||||||
// WlrLayershell.layer: WlrLayer.Bottom
|
|
||||||
WlrLayershell.namespace: "quickshell:background"
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
bottom: true
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
}
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
onWallpaperPathChanged: {
|
|
||||||
bgRoot.updateZoomScale()
|
|
||||||
// Clock position gets updated after zoom scale is updated
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wallpaper zoom scale
|
|
||||||
function updateZoomScale() {
|
|
||||||
getWallpaperSizeProc.path = bgRoot.wallpaperPath
|
|
||||||
getWallpaperSizeProc.running = true;
|
|
||||||
}
|
|
||||||
Process {
|
|
||||||
id: getWallpaperSizeProc
|
|
||||||
property string path: bgRoot.wallpaperPath
|
|
||||||
command: [ "magick", "identify", "-format", "%w %h", path ]
|
|
||||||
stdout: StdioCollector {
|
|
||||||
id: wallpaperSizeOutputCollector
|
|
||||||
onStreamFinished: {
|
|
||||||
const output = wallpaperSizeOutputCollector.text
|
|
||||||
const [width, height] = output.split(" ").map(Number);
|
|
||||||
bgRoot.wallpaperWidth = width
|
|
||||||
bgRoot.wallpaperHeight = height
|
|
||||||
bgRoot.effectiveWallpaperScale = Math.max(1, Math.min(
|
|
||||||
bgRoot.preferredWallpaperScale,
|
|
||||||
width / bgRoot.screen.width,
|
|
||||||
height / bgRoot.screen.height
|
|
||||||
));
|
|
||||||
|
|
||||||
bgRoot.updateClockPosition()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clock positioning
|
|
||||||
function updateClockPosition() {
|
|
||||||
// Somehow all this manual setting is needed to make the proc correctly use the new values
|
|
||||||
leastBusyRegionProc.path = bgRoot.wallpaperPath
|
|
||||||
leastBusyRegionProc.contentWidth = clock.implicitWidth
|
|
||||||
leastBusyRegionProc.contentHeight = clock.implicitHeight
|
|
||||||
leastBusyRegionProc.horizontalPadding = (effectiveWallpaperScale - 1) / 2 * screen.width + 100
|
|
||||||
leastBusyRegionProc.verticalPadding = (effectiveWallpaperScale - 1) / 2 * screen.height + 100
|
|
||||||
leastBusyRegionProc.running = false;
|
|
||||||
leastBusyRegionProc.running = true;
|
|
||||||
}
|
|
||||||
Process {
|
|
||||||
id: leastBusyRegionProc
|
|
||||||
property string path: bgRoot.wallpaperPath
|
|
||||||
property int contentWidth: 300
|
|
||||||
property int contentHeight: 300
|
|
||||||
property int horizontalPadding: bgRoot.movableXSpace
|
|
||||||
property int verticalPadding: bgRoot.movableYSpace
|
|
||||||
command: [Quickshell.shellPath("scripts/images/least_busy_region.py"),
|
|
||||||
"--screen-width", bgRoot.screen.width,
|
|
||||||
"--screen-height", bgRoot.screen.height,
|
|
||||||
"--width", contentWidth,
|
|
||||||
"--height", contentHeight,
|
|
||||||
"--horizontal-padding", horizontalPadding,
|
|
||||||
"--vertical-padding", verticalPadding,
|
|
||||||
path
|
|
||||||
]
|
|
||||||
stdout: StdioCollector {
|
|
||||||
id: leastBusyRegionOutputCollector
|
|
||||||
onStreamFinished: {
|
|
||||||
const output = leastBusyRegionOutputCollector.text
|
|
||||||
// console.log("[Background] Least busy region output:", output)
|
|
||||||
if (output.length === 0) return;
|
|
||||||
const parsedContent = JSON.parse(output)
|
|
||||||
bgRoot.clockX = parsedContent.center_x
|
|
||||||
bgRoot.clockY = parsedContent.center_y
|
|
||||||
bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wallpaper
|
|
||||||
Image {
|
|
||||||
id: wallpaper
|
|
||||||
visible: opacity > 0
|
|
||||||
opacity: (status === Image.Ready && !bgRoot.wallpaperIsVideo) ? 1 : 0
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
property real value // 0 to 1, for offset
|
|
||||||
asynchronous: true
|
|
||||||
value: {
|
|
||||||
// Range = groups that workspaces span on
|
|
||||||
const chunkSize = Config?.options.bar.workspaces.shown ?? 10;
|
|
||||||
const lower = Math.floor(bgRoot.firstWorkspaceId / chunkSize) * chunkSize;
|
|
||||||
const upper = Math.ceil(bgRoot.lastWorkspaceId / chunkSize) * chunkSize;
|
|
||||||
const range = upper - lower;
|
|
||||||
return (Config.options.background.parallax.enableWorkspace ? ((bgRoot.monitor.activeWorkspace?.id - lower) / range) : 0.5)
|
|
||||||
+ (0.15 * GlobalStates.sidebarRightOpen * Config.options.background.parallax.enableSidebar)
|
|
||||||
- (0.15 * GlobalStates.sidebarLeftOpen * Config.options.background.parallax.enableSidebar)
|
|
||||||
}
|
|
||||||
property real effectiveValue: Math.max(0, Math.min(1, value))
|
|
||||||
x: -(bgRoot.movableXSpace) - (effectiveValue - 0.5) * 2 * bgRoot.movableXSpace
|
|
||||||
y: -(bgRoot.movableYSpace)
|
|
||||||
source: bgRoot.wallpaperPath
|
|
||||||
fillMode: Image.PreserveAspectCrop
|
|
||||||
Behavior on x {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 600
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sourceSize {
|
|
||||||
width: bgRoot.screen.width * bgRoot.effectiveWallpaperScale
|
|
||||||
height: bgRoot.screen.height * bgRoot.effectiveWallpaperScale
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The clock
|
|
||||||
Item {
|
|
||||||
id: clock
|
|
||||||
anchors {
|
|
||||||
left: wallpaper.left
|
|
||||||
top: wallpaper.top
|
|
||||||
leftMargin: ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) - (wallpaper.effectiveValue * bgRoot.movableXSpace)
|
|
||||||
topMargin: ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2)
|
|
||||||
Behavior on leftMargin {
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
Behavior on topMargin {
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: clockColumn.implicitWidth
|
|
||||||
implicitHeight: clockColumn.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: clockColumn
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: bgRoot.textHorizontalAlignment
|
|
||||||
font {
|
|
||||||
family: Appearance.font.family.expressive
|
|
||||||
pixelSize: 90
|
|
||||||
weight: Font.Bold
|
|
||||||
}
|
|
||||||
color: bgRoot.colText
|
|
||||||
style: Text.Raised
|
|
||||||
styleColor: Appearance.colors.colShadow
|
|
||||||
text: DateTime.time
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: -5
|
|
||||||
horizontalAlignment: bgRoot.textHorizontalAlignment
|
|
||||||
font {
|
|
||||||
family: Appearance.font.family.expressive
|
|
||||||
pixelSize: 20
|
|
||||||
weight: Font.DemiBold
|
|
||||||
}
|
|
||||||
color: bgRoot.colText
|
|
||||||
style: Text.Raised
|
|
||||||
styleColor: Appearance.colors.colShadow
|
|
||||||
text: DateTime.date
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors {
|
|
||||||
top: clockColumn.bottom
|
|
||||||
left: bgRoot.textHorizontalAlignment === Text.AlignLeft ? clockColumn.left : undefined
|
|
||||||
right: bgRoot.textHorizontalAlignment === Text.AlignRight ? clockColumn.right : undefined
|
|
||||||
horizontalCenter: bgRoot.textHorizontalAlignment === Text.AlignHCenter ? clockColumn.horizontalCenter : undefined
|
|
||||||
topMargin: 5
|
|
||||||
leftMargin: -5
|
|
||||||
rightMargin: -5
|
|
||||||
}
|
|
||||||
opacity: GlobalStates.screenLocked ? 1 : 0
|
|
||||||
visible: opacity > 0
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft; implicitWidth: 1 }
|
|
||||||
MaterialSymbol {
|
|
||||||
text: "lock"
|
|
||||||
Layout.fillWidth: false
|
|
||||||
iconSize: Appearance.font.pixelSize.huge
|
|
||||||
color: bgRoot.colText
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
Layout.fillWidth: false
|
|
||||||
text: "Locked"
|
|
||||||
color: bgRoot.colText
|
|
||||||
font {
|
|
||||||
pixelSize: Appearance.font.pixelSize.larger
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight; implicitWidth: 1 }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Password prompt
|
|
||||||
StyledText {
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
bottom: parent.bottom
|
|
||||||
bottomMargin: 30
|
|
||||||
}
|
|
||||||
opacity: (GlobalStates.screenLocked && !GlobalStates.screenLockContainsCharacters) ? 1 : 0
|
|
||||||
scale: opacity
|
|
||||||
visible: opacity > 0
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
text: "Enter password"
|
|
||||||
color: CF.ColorUtils.transparentize(bgRoot.colText, 0.3)
|
|
||||||
font {
|
|
||||||
pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,448 +0,0 @@
|
|||||||
import "./weather"
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Services.UPower
|
|
||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
|
|
||||||
Item { // Bar content region
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var screen: root.QsWindow.window?.screen
|
|
||||||
property var brightnessMonitor: Brightness.getMonitorForScreen(screen)
|
|
||||||
property real useShortenedForm: (Appearance.sizes.barHellaShortenScreenWidthThreshold >= screen?.width) ? 2 : (Appearance.sizes.barShortenScreenWidthThreshold >= screen?.width) ? 1 : 0
|
|
||||||
readonly property int centerSideModuleWidth: (useShortenedForm == 2) ? Appearance.sizes.barCenterSideModuleWidthHellaShortened : (useShortenedForm == 1) ? Appearance.sizes.barCenterSideModuleWidthShortened : Appearance.sizes.barCenterSideModuleWidth
|
|
||||||
|
|
||||||
component VerticalBarSeparator: Rectangle {
|
|
||||||
Layout.topMargin: Appearance.sizes.baseBarHeight / 3
|
|
||||||
Layout.bottomMargin: Appearance.sizes.baseBarHeight / 3
|
|
||||||
Layout.fillHeight: true
|
|
||||||
implicitWidth: 1
|
|
||||||
color: Appearance.colors.colOutlineVariant
|
|
||||||
}
|
|
||||||
|
|
||||||
// Background shadow
|
|
||||||
Loader {
|
|
||||||
active: Config.options.bar.showBackground && Config.options.bar.cornerStyle === 1
|
|
||||||
anchors.fill: barBackground
|
|
||||||
sourceComponent: StyledRectangularShadow {
|
|
||||||
anchors.fill: undefined // The loader's anchors act on this, and this should not have any anchor
|
|
||||||
target: barBackground
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Background
|
|
||||||
Rectangle {
|
|
||||||
id: barBackground
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
margins: Config.options.bar.cornerStyle === 1 ? (Appearance.sizes.hyprlandGapsOut) : 0 // idk why but +1 is needed
|
|
||||||
}
|
|
||||||
color: Config.options.bar.showBackground ? Appearance.colors.colLayer0 : "transparent"
|
|
||||||
radius: Config.options.bar.cornerStyle === 1 ? Appearance.rounding.windowRounding : 0
|
|
||||||
border.width: Config.options.bar.cornerStyle === 1 ? 1 : 0
|
|
||||||
border.color: Appearance.colors.colLayer0Border
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea { // Left side | scroll to change brightness
|
|
||||||
id: barLeftSideMouseArea
|
|
||||||
anchors.left: parent.left
|
|
||||||
implicitHeight: Appearance.sizes.baseBarHeight
|
|
||||||
height: Appearance.sizes.barHeight
|
|
||||||
width: (root.width - middleSection.width) / 2
|
|
||||||
property bool hovered: false
|
|
||||||
property real lastScrollX: 0
|
|
||||||
property real lastScrollY: 0
|
|
||||||
property bool trackingScroll: false
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
hoverEnabled: true
|
|
||||||
propagateComposedEvents: true
|
|
||||||
onEntered: event => {
|
|
||||||
barLeftSideMouseArea.hovered = true;
|
|
||||||
}
|
|
||||||
onExited: event => {
|
|
||||||
barLeftSideMouseArea.hovered = false;
|
|
||||||
barLeftSideMouseArea.trackingScroll = false;
|
|
||||||
}
|
|
||||||
onPressed: event => {
|
|
||||||
if (event.button === Qt.LeftButton) {
|
|
||||||
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Scroll to change brightness
|
|
||||||
WheelHandler {
|
|
||||||
onWheel: event => {
|
|
||||||
if (event.angleDelta.y < 0)
|
|
||||||
root.brightnessMonitor.setBrightness(root.brightnessMonitor.brightness - 0.05);
|
|
||||||
else if (event.angleDelta.y > 0)
|
|
||||||
root.brightnessMonitor.setBrightness(root.brightnessMonitor.brightness + 0.05);
|
|
||||||
// Store the mouse position and start tracking
|
|
||||||
barLeftSideMouseArea.lastScrollX = event.x;
|
|
||||||
barLeftSideMouseArea.lastScrollY = event.y;
|
|
||||||
barLeftSideMouseArea.trackingScroll = true;
|
|
||||||
}
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
|
||||||
}
|
|
||||||
onPositionChanged: mouse => {
|
|
||||||
if (barLeftSideMouseArea.trackingScroll) {
|
|
||||||
const dx = mouse.x - barLeftSideMouseArea.lastScrollX;
|
|
||||||
const dy = mouse.y - barLeftSideMouseArea.lastScrollY;
|
|
||||||
if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) {
|
|
||||||
GlobalStates.osdBrightnessOpen = false;
|
|
||||||
barLeftSideMouseArea.trackingScroll = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
// Left section
|
|
||||||
anchors.fill: parent
|
|
||||||
implicitHeight: leftSectionRowLayout.implicitHeight
|
|
||||||
implicitWidth: leftSectionRowLayout.implicitWidth
|
|
||||||
|
|
||||||
ScrollHint {
|
|
||||||
reveal: barLeftSideMouseArea.hovered
|
|
||||||
icon: "light_mode"
|
|
||||||
tooltipText: Translation.tr("Scroll to change brightness")
|
|
||||||
side: "left"
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout { // Content
|
|
||||||
id: leftSectionRowLayout
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
RippleButton {
|
|
||||||
// Left sidebar button
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
|
||||||
Layout.leftMargin: Appearance.rounding.screenRounding
|
|
||||||
Layout.fillWidth: false
|
|
||||||
property real buttonPadding: 5
|
|
||||||
implicitWidth: distroIcon.width + buttonPadding * 2
|
|
||||||
implicitHeight: distroIcon.height + buttonPadding * 2
|
|
||||||
|
|
||||||
buttonRadius: Appearance.rounding.full
|
|
||||||
colBackground: barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
|
||||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
|
||||||
colRipple: Appearance.colors.colLayer1Active
|
|
||||||
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
|
||||||
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
|
||||||
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
|
||||||
toggled: GlobalStates.sidebarLeftOpen
|
|
||||||
property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomIcon {
|
|
||||||
id: distroIcon
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: 19.5
|
|
||||||
height: 19.5
|
|
||||||
source: Config.options.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : "spark-symbolic"
|
|
||||||
colorize: true
|
|
||||||
color: Appearance.colors.colOnLayer0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ActiveWindow {
|
|
||||||
visible: root.useShortenedForm === 0
|
|
||||||
Layout.rightMargin: Appearance.rounding.screenRounding
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout { // Middle section
|
|
||||||
id: middleSection
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Config.options?.bar.borderless ? 4 : 8
|
|
||||||
|
|
||||||
BarGroup {
|
|
||||||
id: leftCenterGroup
|
|
||||||
Layout.preferredWidth: root.centerSideModuleWidth
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
Resources {
|
|
||||||
alwaysShowAllResources: root.useShortenedForm === 2
|
|
||||||
Layout.fillWidth: root.useShortenedForm === 2
|
|
||||||
}
|
|
||||||
|
|
||||||
Media {
|
|
||||||
visible: root.useShortenedForm < 2
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VerticalBarSeparator {
|
|
||||||
visible: Config.options?.bar.borderless
|
|
||||||
}
|
|
||||||
|
|
||||||
BarGroup {
|
|
||||||
id: middleCenterGroup
|
|
||||||
padding: workspacesWidget.widgetPadding
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
Workspaces {
|
|
||||||
id: workspacesWidget
|
|
||||||
Layout.fillHeight: true
|
|
||||||
MouseArea {
|
|
||||||
// Right-click to toggle overview
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
|
|
||||||
onPressed: event => {
|
|
||||||
if (event.button === Qt.RightButton) {
|
|
||||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VerticalBarSeparator {
|
|
||||||
visible: Config.options?.bar.borderless
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: rightCenterGroup
|
|
||||||
implicitWidth: rightCenterGroupContent.implicitWidth
|
|
||||||
implicitHeight: rightCenterGroupContent.implicitHeight
|
|
||||||
Layout.preferredWidth: root.centerSideModuleWidth
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
BarGroup {
|
|
||||||
id: rightCenterGroupContent
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
ClockWidget {
|
|
||||||
showDate: (Config.options.bar.verbose && root.useShortenedForm < 2)
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
UtilButtons {
|
|
||||||
visible: (Config.options.bar.verbose && root.useShortenedForm === 0)
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
BatteryIndicator {
|
|
||||||
visible: (root.useShortenedForm < 2 && UPower.displayDevice.isLaptopBattery)
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VerticalBarSeparator {
|
|
||||||
visible: Config.options.bar.borderless && Config.options.bar.weather.enable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea { // Right side | scroll to change volume
|
|
||||||
id: barRightSideMouseArea
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
|
||||||
implicitHeight: Appearance.sizes.baseBarHeight
|
|
||||||
height: Appearance.sizes.barHeight
|
|
||||||
width: (root.width - middleSection.width) / 2
|
|
||||||
|
|
||||||
property bool hovered: false
|
|
||||||
property real lastScrollX: 0
|
|
||||||
property real lastScrollY: 0
|
|
||||||
property bool trackingScroll: false
|
|
||||||
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
hoverEnabled: true
|
|
||||||
propagateComposedEvents: true
|
|
||||||
onEntered: event => {
|
|
||||||
barRightSideMouseArea.hovered = true;
|
|
||||||
}
|
|
||||||
onExited: event => {
|
|
||||||
barRightSideMouseArea.hovered = false;
|
|
||||||
barRightSideMouseArea.trackingScroll = false;
|
|
||||||
}
|
|
||||||
onPressed: event => {
|
|
||||||
if (event.button === Qt.LeftButton) {
|
|
||||||
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
|
|
||||||
} else if (event.button === Qt.RightButton) {
|
|
||||||
MprisController.activePlayer.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Scroll to change volume
|
|
||||||
WheelHandler {
|
|
||||||
onWheel: event => {
|
|
||||||
const currentVolume = Audio.value;
|
|
||||||
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
|
|
||||||
if (event.angleDelta.y < 0)
|
|
||||||
Audio.sink.audio.volume -= step;
|
|
||||||
else if (event.angleDelta.y > 0)
|
|
||||||
Audio.sink.audio.volume = Math.min(1, Audio.sink.audio.volume + step);
|
|
||||||
// Store the mouse position and start tracking
|
|
||||||
barRightSideMouseArea.lastScrollX = event.x;
|
|
||||||
barRightSideMouseArea.lastScrollY = event.y;
|
|
||||||
barRightSideMouseArea.trackingScroll = true;
|
|
||||||
}
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
|
||||||
}
|
|
||||||
onPositionChanged: mouse => {
|
|
||||||
if (barRightSideMouseArea.trackingScroll) {
|
|
||||||
const dx = mouse.x - barRightSideMouseArea.lastScrollX;
|
|
||||||
const dy = mouse.y - barRightSideMouseArea.lastScrollY;
|
|
||||||
if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) {
|
|
||||||
GlobalStates.osdVolumeOpen = false;
|
|
||||||
barRightSideMouseArea.trackingScroll = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors.fill: parent
|
|
||||||
implicitHeight: rightSectionRowLayout.implicitHeight
|
|
||||||
implicitWidth: rightSectionRowLayout.implicitWidth
|
|
||||||
|
|
||||||
ScrollHint {
|
|
||||||
reveal: barRightSideMouseArea.hovered
|
|
||||||
icon: "volume_up"
|
|
||||||
tooltipText: Translation.tr("Scroll to change volume")
|
|
||||||
side: "right"
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: rightSectionRowLayout
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 5
|
|
||||||
layoutDirection: Qt.RightToLeft
|
|
||||||
|
|
||||||
RippleButton { // Right sidebar button
|
|
||||||
id: rightSidebarButton
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
Layout.rightMargin: Appearance.rounding.screenRounding
|
|
||||||
Layout.fillWidth: false
|
|
||||||
|
|
||||||
implicitWidth: indicatorsRowLayout.implicitWidth + 10 * 2
|
|
||||||
implicitHeight: indicatorsRowLayout.implicitHeight + 5 * 2
|
|
||||||
|
|
||||||
buttonRadius: Appearance.rounding.full
|
|
||||||
colBackground: barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
|
||||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
|
||||||
colRipple: Appearance.colors.colLayer1Active
|
|
||||||
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
|
||||||
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
|
||||||
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
|
||||||
toggled: GlobalStates.sidebarRightOpen
|
|
||||||
property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0
|
|
||||||
|
|
||||||
Behavior on colText {
|
|
||||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: indicatorsRowLayout
|
|
||||||
anchors.centerIn: parent
|
|
||||||
property real realSpacing: 15
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Revealer {
|
|
||||||
reveal: Audio.sink?.audio?.muted ?? false
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0
|
|
||||||
Behavior on Layout.rightMargin {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveFast.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MaterialSymbol {
|
|
||||||
text: "volume_off"
|
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
|
||||||
color: rightSidebarButton.colText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Revealer {
|
|
||||||
reveal: Audio.source?.audio?.muted ?? false
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.rightMargin: reveal ? indicatorsRowLayout.realSpacing : 0
|
|
||||||
Behavior on Layout.rightMargin {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveFast.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MaterialSymbol {
|
|
||||||
text: "mic_off"
|
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
|
||||||
color: rightSidebarButton.colText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loader {
|
|
||||||
active: HyprlandXkb.layoutCodes.length > 1
|
|
||||||
visible: active
|
|
||||||
Layout.rightMargin: indicatorsRowLayout.realSpacing
|
|
||||||
sourceComponent: StyledText {
|
|
||||||
text: HyprlandXkb.currentLayoutCode
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.small
|
|
||||||
color: rightSidebarButton.colText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MaterialSymbol {
|
|
||||||
Layout.rightMargin: indicatorsRowLayout.realSpacing
|
|
||||||
text: Network.materialSymbol
|
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
|
||||||
color: rightSidebarButton.colText
|
|
||||||
}
|
|
||||||
MaterialSymbol {
|
|
||||||
text: Bluetooth.bluetoothConnected ? "bluetooth_connected" : Bluetooth.bluetoothEnabled ? "bluetooth" : "bluetooth_disabled"
|
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
|
||||||
color: rightSidebarButton.colText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SysTray {
|
|
||||||
visible: root.useShortenedForm === 0
|
|
||||||
Layout.fillWidth: false
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weather
|
|
||||||
Loader {
|
|
||||||
Layout.leftMargin: 8
|
|
||||||
Layout.fillHeight: true
|
|
||||||
active: Config.options.bar.weather.enable
|
|
||||||
sourceComponent: BarGroup {
|
|
||||||
implicitHeight: Appearance.sizes.baseBarHeight
|
|
||||||
WeatherBar {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
property real padding: 5
|
|
||||||
implicitHeight: Appearance.sizes.baseBarHeight
|
|
||||||
height: Appearance.sizes.barHeight
|
|
||||||
implicitWidth: rowLayout.implicitWidth + padding * 2
|
|
||||||
default property alias items: rowLayout.children
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: background
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
topMargin: 4
|
|
||||||
bottomMargin: 4
|
|
||||||
}
|
|
||||||
color: Config.options?.bar.borderless ? "transparent" : Appearance.colors.colLayer1
|
|
||||||
radius: Appearance.rounding.small
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: rowLayout
|
|
||||||
anchors {
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
leftMargin: root.padding
|
|
||||||
rightMargin: root.padding
|
|
||||||
}
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.services
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
property bool borderless: Config.options.bar.borderless
|
|
||||||
readonly property var chargeState: Battery.chargeState
|
|
||||||
readonly property bool isCharging: Battery.isCharging
|
|
||||||
readonly property bool isPluggedIn: Battery.isPluggedIn
|
|
||||||
readonly property real percentage: Battery.percentage
|
|
||||||
readonly property bool isLow: percentage <= Config.options.battery.low / 100
|
|
||||||
readonly property color batteryLowBackground: Appearance.m3colors.darkmode ? Appearance.m3colors.m3error : Appearance.m3colors.m3errorContainer
|
|
||||||
readonly property color batteryLowOnBackground: Appearance.m3colors.darkmode ? Appearance.m3colors.m3errorContainer : Appearance.m3colors.m3error
|
|
||||||
|
|
||||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
|
||||||
implicitHeight: 32
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: rowLayout
|
|
||||||
|
|
||||||
spacing: 4
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
implicitWidth: (isCharging ? (boltIconLoader?.item?.width ?? 0) : 0)
|
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: Appearance.colors.colOnLayer1
|
|
||||||
text: `${Math.round(percentage * 100)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
CircularProgress {
|
|
||||||
enableAnimation: false
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
lineWidth: 2
|
|
||||||
value: percentage
|
|
||||||
implicitSize: 26
|
|
||||||
colSecondary: (isLow && !isCharging) ? batteryLowBackground : Appearance.colors.colSecondaryContainer
|
|
||||||
colPrimary: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer
|
|
||||||
fill: (isLow && !isCharging)
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
fill: 1
|
|
||||||
text: "battery_full"
|
|
||||||
iconSize: Appearance.font.pixelSize.normal
|
|
||||||
color: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: boltIconLoader
|
|
||||||
active: true
|
|
||||||
anchors.left: rowLayout.left
|
|
||||||
anchors.verticalCenter: rowLayout.verticalCenter
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onIsChargingChanged() {
|
|
||||||
if (isCharging) boltIconLoader.active = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceComponent: MaterialSymbol {
|
|
||||||
id: boltIcon
|
|
||||||
|
|
||||||
text: "bolt"
|
|
||||||
iconSize: Appearance.font.pixelSize.large
|
|
||||||
color: Appearance.m3colors.m3onSecondaryContainer
|
|
||||||
visible: opacity > 0 // Only show when charging
|
|
||||||
opacity: isCharging ? 1 : 0 // Keep opacity for visibility
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (!visible) boltIconLoader.active = false
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
Item {
|
|
||||||
required property string iconName
|
|
||||||
required property double percentage
|
|
||||||
property bool shown: true
|
|
||||||
clip: true
|
|
||||||
visible: width > 0 && height > 0
|
|
||||||
implicitWidth: resourceRowLayout.x < 0 ? 0 : childrenRect.width
|
|
||||||
implicitHeight: childrenRect.height
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
spacing: 4
|
|
||||||
id: resourceRowLayout
|
|
||||||
x: shown ? 0 : -resourceRowLayout.width
|
|
||||||
|
|
||||||
CircularProgress {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
lineWidth: 2
|
|
||||||
value: percentage
|
|
||||||
implicitSize: 26
|
|
||||||
colSecondary: Appearance.colors.colSecondaryContainer
|
|
||||||
colPrimary: Appearance.m3colors.m3onSecondaryContainer
|
|
||||||
enableAnimation: false
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
fill: 1
|
|
||||||
text: iconName
|
|
||||||
iconSize: Appearance.font.pixelSize.normal
|
|
||||||
color: Appearance.m3colors.m3onSecondaryContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: Appearance.colors.colOnLayer1
|
|
||||||
text: `${Math.round(percentage * 100)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on x {
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell.Services.SystemTray
|
|
||||||
|
|
||||||
// TODO: More fancy animation
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
height: parent.height
|
|
||||||
implicitWidth: rowLayout.implicitWidth
|
|
||||||
Layout.leftMargin: Appearance.rounding.screenRounding
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: rowLayout
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 15
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: SystemTray.items
|
|
||||||
|
|
||||||
SysTrayItem {
|
|
||||||
required property SystemTrayItem modelData
|
|
||||||
item: modelData
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.larger
|
|
||||||
color: Appearance.colors.colSubtext
|
|
||||||
text: "•"
|
|
||||||
visible: {
|
|
||||||
SystemTray.items.values.length > 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Services.SystemTray
|
|
||||||
import Quickshell.Widgets
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var bar: root.QsWindow.window
|
|
||||||
required property SystemTrayItem item
|
|
||||||
property bool targetMenuOpen: false
|
|
||||||
property int trayItemWidth: Appearance.font.pixelSize.larger
|
|
||||||
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
Layout.fillHeight: true
|
|
||||||
implicitWidth: trayItemWidth
|
|
||||||
onClicked: (event) => {
|
|
||||||
switch (event.button) {
|
|
||||||
case Qt.LeftButton:
|
|
||||||
item.activate();
|
|
||||||
break;
|
|
||||||
case Qt.RightButton:
|
|
||||||
if (item.hasMenu) menu.open();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QsMenuAnchor {
|
|
||||||
id: menu
|
|
||||||
|
|
||||||
menu: root.item.menu
|
|
||||||
anchor.window: bar
|
|
||||||
anchor.rect.x: root.x + bar.width
|
|
||||||
anchor.rect.y: root.y
|
|
||||||
anchor.rect.height: root.height
|
|
||||||
anchor.edges: Edges.Bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
IconImage {
|
|
||||||
id: trayIcon
|
|
||||||
visible: !Config.options.bar.tray.monochromeIcons
|
|
||||||
source: root.item.icon
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width
|
|
||||||
height: parent.height
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
active: Config.options.bar.tray.monochromeIcons
|
|
||||||
anchors.fill: trayIcon
|
|
||||||
sourceComponent: Item {
|
|
||||||
Desaturate {
|
|
||||||
id: desaturatedIcon
|
|
||||||
visible: false // There's already color overlay
|
|
||||||
anchors.fill: parent
|
|
||||||
source: trayIcon
|
|
||||||
desaturation: 0.8 // 1.0 means fully grayscale
|
|
||||||
}
|
|
||||||
ColorOverlay {
|
|
||||||
anchors.fill: desaturatedIcon
|
|
||||||
source: desaturatedIcon
|
|
||||||
color: ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.9)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.services
|
|
||||||
import Quickshell
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: root
|
|
||||||
property real margin: 10
|
|
||||||
property bool hovered: false
|
|
||||||
implicitWidth: rowLayout.implicitWidth + margin * 2
|
|
||||||
implicitHeight: rowLayout.implicitHeight
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: rowLayout
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
fill: 0
|
|
||||||
text: WeatherIcons.codeToName[Weather.data?.wCode] ?? "question_mark"
|
|
||||||
iconSize: Appearance.font.pixelSize.large
|
|
||||||
color: Appearance.colors.colOnLayer1
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
visible: true
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.small
|
|
||||||
color: Appearance.colors.colOnLayer1
|
|
||||||
text: Weather.data?.temp ?? "--°"
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LazyLoader {
|
|
||||||
id: popupLoader
|
|
||||||
active: root.containsMouse
|
|
||||||
|
|
||||||
component: PopupWindow {
|
|
||||||
id: popupWindow
|
|
||||||
visible: true
|
|
||||||
implicitWidth: weatherPopup.implicitWidth
|
|
||||||
implicitHeight: weatherPopup.implicitHeight
|
|
||||||
anchor.item: root
|
|
||||||
anchor.edges: Edges.Top
|
|
||||||
anchor.rect.x: (root.implicitWidth - popupWindow.implicitWidth) / 2
|
|
||||||
anchor.rect.y: Config.options.bar.bottom ?
|
|
||||||
(-weatherPopup.implicitHeight - 15) :
|
|
||||||
(root.implicitHeight + 15 )
|
|
||||||
color: "transparent"
|
|
||||||
WeatherPopup {
|
|
||||||
id: weatherPopup
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
// credits: calestia
|
|
||||||
// this snippet is taken from
|
|
||||||
// https://github.com/caelestia-dots/shell
|
|
||||||
readonly property var codeToName: ({
|
|
||||||
"113": "clear_day",
|
|
||||||
"116": "partly_cloudy_day",
|
|
||||||
"119": "cloud",
|
|
||||||
"122": "cloud",
|
|
||||||
"143": "foggy",
|
|
||||||
"176": "rainy",
|
|
||||||
"179": "rainy",
|
|
||||||
"182": "rainy",
|
|
||||||
"185": "rainy",
|
|
||||||
"200": "thunderstorm",
|
|
||||||
"227": "cloudy_snowing",
|
|
||||||
"230": "snowing_heavy",
|
|
||||||
"248": "foggy",
|
|
||||||
"260": "foggy",
|
|
||||||
"263": "rainy",
|
|
||||||
"266": "rainy",
|
|
||||||
"281": "rainy",
|
|
||||||
"284": "rainy",
|
|
||||||
"293": "rainy",
|
|
||||||
"296": "rainy",
|
|
||||||
"299": "rainy",
|
|
||||||
"302": "weather_hail",
|
|
||||||
"305": "rainy",
|
|
||||||
"308": "weather_hail",
|
|
||||||
"311": "rainy",
|
|
||||||
"314": "rainy",
|
|
||||||
"317": "rainy",
|
|
||||||
"320": "cloudy_snowing",
|
|
||||||
"323": "cloudy_snowing",
|
|
||||||
"326": "cloudy_snowing",
|
|
||||||
"329": "snowing_heavy",
|
|
||||||
"332": "snowing_heavy",
|
|
||||||
"335": "snowing",
|
|
||||||
"338": "snowing_heavy",
|
|
||||||
"350": "rainy",
|
|
||||||
"353": "rainy",
|
|
||||||
"356": "rainy",
|
|
||||||
"359": "weather_hail",
|
|
||||||
"362": "rainy",
|
|
||||||
"365": "rainy",
|
|
||||||
"368": "cloudy_snowing",
|
|
||||||
"371": "snowing",
|
|
||||||
"374": "rainy",
|
|
||||||
"377": "rainy",
|
|
||||||
"386": "thunderstorm",
|
|
||||||
"389": "thunderstorm",
|
|
||||||
"392": "thunderstorm",
|
|
||||||
"395": "snowing"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
property string filePath: Directories.shellConfigPath
|
|
||||||
property alias options: configOptionsJsonAdapter
|
|
||||||
property bool ready: false
|
|
||||||
|
|
||||||
function setNestedValue(nestedKey, value) {
|
|
||||||
let keys = nestedKey.split(".");
|
|
||||||
let obj = root.options;
|
|
||||||
let parents = [obj];
|
|
||||||
|
|
||||||
// Traverse and collect parent objects
|
|
||||||
for (let i = 0; i < keys.length - 1; ++i) {
|
|
||||||
if (!obj[keys[i]] || typeof obj[keys[i]] !== "object") {
|
|
||||||
obj[keys[i]] = {};
|
|
||||||
}
|
|
||||||
obj = obj[keys[i]];
|
|
||||||
parents.push(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert value to correct type using JSON.parse when safe
|
|
||||||
let convertedValue = value;
|
|
||||||
if (typeof value === "string") {
|
|
||||||
let trimmed = value.trim();
|
|
||||||
if (trimmed === "true" || trimmed === "false" || !isNaN(Number(trimmed))) {
|
|
||||||
try {
|
|
||||||
convertedValue = JSON.parse(trimmed);
|
|
||||||
} catch (e) {
|
|
||||||
convertedValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
obj[keys[keys.length - 1]] = convertedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
|
||||||
path: root.filePath
|
|
||||||
watchChanges: true
|
|
||||||
onFileChanged: reload()
|
|
||||||
onAdapterUpdated: writeAdapter()
|
|
||||||
onLoaded: root.ready = true
|
|
||||||
onLoadFailed: error => {
|
|
||||||
if (error == FileViewError.FileNotFound) {
|
|
||||||
writeAdapter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonAdapter {
|
|
||||||
id: configOptionsJsonAdapter
|
|
||||||
property JsonObject policies: JsonObject {
|
|
||||||
property int ai: 1 // 0: No | 1: Yes | 2: Local
|
|
||||||
property int weeb: 1 // 0: No | 1: Open | 2: Closet
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject ai: JsonObject {
|
|
||||||
property string systemPrompt: "## Style\n- Use casual tone, don't be formal! Make sure you answer precisely without hallucination and prefer bullet points over walls of text. You can have a friendly greeting at the beginning of the conversation, but don't repeat the user's question\n\n## Context (ignore when irrelevant)\n- You are a helpful and inspiring sidebar assistant on a {DISTRO} Linux system\n- Desktop environment: {DE}\n- Current date & time: {DATETIME}\n- Focused app: {WINDOWCLASS}\n\n## Presentation\n- Use Markdown features in your response: \n - **Bold** text to **highlight keywords** in your response\n - **Split long information into small sections** with h2 headers and a relevant emoji at the start of it (for example `## 🐧 Linux`). Bullet points are preferred over long paragraphs, unless you're offering writing support or instructed otherwise by the user.\n- Asked to compare different options? You should firstly use a table to compare the main aspects, then elaborate or include relevant comments from online forums *after* the table. Make sure to provide a final recommendation for the user's use case!\n- Use LaTeX formatting for mathematical and scientific notations whenever appropriate. Enclose all LaTeX '$$' delimiters. NEVER generate LaTeX code in a latex block unless the user explicitly asks for it. DO NOT use LaTeX for regular documents (resumes, letters, essays, CVs, etc.).\n"
|
|
||||||
property string tool: "functions" // search, functions, or none
|
|
||||||
property list<var> extraModels: [
|
|
||||||
{
|
|
||||||
"api_format": "openai", // Most of the time you want "openai". Use "gemini" for Google's models
|
|
||||||
"description": "This is a custom model. Edit the config to add more! | Anyway, this is DeepSeek R1 Distill LLaMA 70B",
|
|
||||||
"endpoint": "https://openrouter.ai/api/v1/chat/completions",
|
|
||||||
"homepage": "https://openrouter.ai/deepseek/deepseek-r1-distill-llama-70b:free", // Not mandatory
|
|
||||||
"icon": "spark-symbolic", // Not mandatory
|
|
||||||
"key_get_link": "https://openrouter.ai/settings/keys", // Not mandatory
|
|
||||||
"key_id": "openrouter",
|
|
||||||
"model": "deepseek/deepseek-r1-distill-llama-70b:free",
|
|
||||||
"name": "Custom: DS R1 Dstl. LLaMA 70B",
|
|
||||||
"requires_key": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject appearance: JsonObject {
|
|
||||||
property bool extraBackgroundTint: true
|
|
||||||
property int fakeScreenRounding: 2 // 0: None | 1: Always | 2: When not fullscreen
|
|
||||||
property bool transparency: false
|
|
||||||
property JsonObject wallpaperTheming: JsonObject {
|
|
||||||
property bool enableAppsAndShell: true
|
|
||||||
property bool enableQtApps: true
|
|
||||||
property bool enableTerminal: true
|
|
||||||
}
|
|
||||||
property JsonObject palette: JsonObject {
|
|
||||||
property string type: "auto" // Allowed: auto, scheme-content, scheme-expressive, scheme-fidelity, scheme-fruit-salad, scheme-monochrome, scheme-neutral, scheme-rainbow, scheme-tonal-spot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject audio: JsonObject {
|
|
||||||
// Values in %
|
|
||||||
property JsonObject protection: JsonObject {
|
|
||||||
// Prevent sudden bangs
|
|
||||||
property bool enable: true
|
|
||||||
property real maxAllowedIncrease: 10
|
|
||||||
property real maxAllowed: 90 // Realistically should already provide some protection when it's 99...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject apps: JsonObject {
|
|
||||||
property string bluetooth: "kcmshell6 kcm_bluetooth"
|
|
||||||
property string network: "plasmawindowed org.kde.plasma.networkmanagement"
|
|
||||||
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
|
|
||||||
property string taskManager: "plasma-systemmonitor --page-name Processes"
|
|
||||||
property string terminal: "kitty -1" // This is only for shell actions
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject background: JsonObject {
|
|
||||||
property bool fixedClockPosition: false
|
|
||||||
property real clockX: -500
|
|
||||||
property real clockY: -500
|
|
||||||
property string wallpaperPath: ""
|
|
||||||
property string thumbnailPath: ""
|
|
||||||
property JsonObject parallax: JsonObject {
|
|
||||||
property bool enableWorkspace: true
|
|
||||||
property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size
|
|
||||||
property bool enableSidebar: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject bar: JsonObject {
|
|
||||||
property JsonObject autoHide: JsonObject {
|
|
||||||
property bool enable: false
|
|
||||||
property bool pushWindows: false
|
|
||||||
property JsonObject showWhenPressingSuper: JsonObject {
|
|
||||||
property bool enable: true
|
|
||||||
property int delay: 100
|
|
||||||
}
|
|
||||||
}
|
|
||||||
property bool bottom: false // Instead of top
|
|
||||||
property int cornerStyle: 0 // 0: Hug | 1: Float | 2: Plain rectangle
|
|
||||||
property bool borderless: false // true for no grouping of items
|
|
||||||
property string topLeftIcon: "spark" // Options: distro, spark
|
|
||||||
property bool showBackground: true
|
|
||||||
property bool verbose: true
|
|
||||||
property JsonObject resources: JsonObject {
|
|
||||||
property bool alwaysShowSwap: true
|
|
||||||
property bool alwaysShowCpu: false
|
|
||||||
}
|
|
||||||
property list<string> screenList: [] // List of names, like "eDP-1", find out with 'hyprctl monitors' command
|
|
||||||
property JsonObject utilButtons: JsonObject {
|
|
||||||
property bool showScreenSnip: true
|
|
||||||
property bool showColorPicker: false
|
|
||||||
property bool showMicToggle: false
|
|
||||||
property bool showKeyboardToggle: true
|
|
||||||
property bool showDarkModeToggle: true
|
|
||||||
property bool showPerformanceProfileToggle: false
|
|
||||||
}
|
|
||||||
property JsonObject tray: JsonObject {
|
|
||||||
property bool monochromeIcons: true
|
|
||||||
}
|
|
||||||
property JsonObject workspaces: JsonObject {
|
|
||||||
property bool monochromeIcons: true
|
|
||||||
property int shown: 10
|
|
||||||
property bool showAppIcons: true
|
|
||||||
property bool alwaysShowNumbers: false
|
|
||||||
property int showNumberDelay: 300 // milliseconds
|
|
||||||
}
|
|
||||||
property JsonObject weather: JsonObject {
|
|
||||||
property bool enable: false
|
|
||||||
property bool enableGPS: true // gps based location
|
|
||||||
property string city: "" // When 'enableGPS' is false
|
|
||||||
property bool useUSCS: false // Instead of metric (SI) units
|
|
||||||
property int fetchInterval: 10 // minutes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject battery: JsonObject {
|
|
||||||
property int low: 20
|
|
||||||
property int critical: 5
|
|
||||||
property bool automaticSuspend: true
|
|
||||||
property int suspend: 3
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject dock: JsonObject {
|
|
||||||
property bool enable: false
|
|
||||||
property bool monochromeIcons: true
|
|
||||||
property real height: 60
|
|
||||||
property real hoverRegionHeight: 2
|
|
||||||
property bool pinnedOnStartup: false
|
|
||||||
property bool hoverToReveal: true // When false, only reveals on empty workspace
|
|
||||||
property list<string> pinnedApps: [ // IDs of pinned entries
|
|
||||||
"org.kde.dolphin", "kitty",]
|
|
||||||
property list<string> ignoredAppRegexes: []
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject interactions: JsonObject {
|
|
||||||
property JsonObject scrolling: JsonObject {
|
|
||||||
property int mouseScrollDeltaThreshold: 120 // delta >= this then it gets detected as mouse scroll rather than touchpad
|
|
||||||
property int mouseScrollFactor: 120
|
|
||||||
property int touchpadScrollFactor: 450
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject language: JsonObject {
|
|
||||||
property JsonObject translator: JsonObject {
|
|
||||||
property string engine: "auto" // Run `trans -list-engines` for available engines. auto should use google
|
|
||||||
property string targetLanguage: "auto" // Run `trans -list-all` for available languages
|
|
||||||
property string sourceLanguage: "auto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject light: JsonObject {
|
|
||||||
property JsonObject night: JsonObject {
|
|
||||||
property bool automatic: true
|
|
||||||
property string from: "19:00" // Format: "HH:mm", 24-hour time
|
|
||||||
property string to: "06:30" // Format: "HH:mm", 24-hour time
|
|
||||||
property int colorTemperature: 5000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject media: JsonObject {
|
|
||||||
// Attempt to remove dupes (the aggregator playerctl one and browsers' native ones when there's plasma browser integration)
|
|
||||||
property bool filterDuplicatePlayers: true
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject networking: JsonObject {
|
|
||||||
property string userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject osd: JsonObject {
|
|
||||||
property int timeout: 1000
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject osk: JsonObject {
|
|
||||||
property string layout: "qwerty_full"
|
|
||||||
property bool pinnedOnStartup: false
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject overview: JsonObject {
|
|
||||||
property bool enable: true
|
|
||||||
property real scale: 0.18 // Relative to screen size
|
|
||||||
property real rows: 2
|
|
||||||
property real columns: 5
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject resources: JsonObject {
|
|
||||||
property int updateInterval: 3000
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject search: JsonObject {
|
|
||||||
property int nonAppResultDelay: 30 // This prevents lagging when typing
|
|
||||||
property string engineBaseUrl: "https://www.google.com/search?q="
|
|
||||||
property list<string> excludedSites: ["quora.com"]
|
|
||||||
property bool sloppy: false // Uses levenshtein distance based scoring instead of fuzzy sort. Very weird.
|
|
||||||
property JsonObject prefix: JsonObject {
|
|
||||||
property string action: "/"
|
|
||||||
property string clipboard: ";"
|
|
||||||
property string emojis: ":"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject sidebar: JsonObject {
|
|
||||||
property bool keepRightSidebarLoaded: true
|
|
||||||
property JsonObject translator: JsonObject {
|
|
||||||
property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag.
|
|
||||||
}
|
|
||||||
property JsonObject booru: JsonObject {
|
|
||||||
property bool allowNsfw: false
|
|
||||||
property string defaultProvider: "yandere"
|
|
||||||
property int limit: 20
|
|
||||||
property JsonObject zerochan: JsonObject {
|
|
||||||
property string username: "[unset]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject time: JsonObject {
|
|
||||||
// https://doc.qt.io/qt-6/qtime.html#toString
|
|
||||||
property string format: "hh:mm"
|
|
||||||
property string dateFormat: "ddd, dd/MM"
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject windows: JsonObject {
|
|
||||||
property bool showTitlebar: true // Client-side decoration for shell apps
|
|
||||||
property bool centerTitle: true
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject hacks: JsonObject {
|
|
||||||
property int arbitraryRaceConditionDelay: 20 // milliseconds
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject screenshotTool: JsonObject {
|
|
||||||
property bool showContentRegions: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
property alias states: persistentStatesJsonAdapter
|
|
||||||
property string fileDir: Directories.state
|
|
||||||
property string fileName: "states.json"
|
|
||||||
property string filePath: `${root.fileDir}/${root.fileName}`
|
|
||||||
|
|
||||||
FileView {
|
|
||||||
path: root.filePath
|
|
||||||
|
|
||||||
watchChanges: true
|
|
||||||
onFileChanged: reload()
|
|
||||||
onAdapterUpdated: {
|
|
||||||
writeAdapter()
|
|
||||||
}
|
|
||||||
onLoadFailed: error => {
|
|
||||||
console.log("Failed to load persistent states file:", error);
|
|
||||||
if (error == FileViewError.FileNotFound) {
|
|
||||||
writeAdapter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
adapter: JsonAdapter {
|
|
||||||
id: persistentStatesJsonAdapter
|
|
||||||
property JsonObject ai: JsonObject {
|
|
||||||
property string model
|
|
||||||
property real temperature: 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject sidebar: JsonObject {
|
|
||||||
property JsonObject bottomGroup: JsonObject {
|
|
||||||
property bool collapsed: false
|
|
||||||
property int tab: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property JsonObject booru: JsonObject {
|
|
||||||
property bool allowNsfw: false
|
|
||||||
property string provider: "yandere"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trims the File protocol off the input string
|
|
||||||
* @param {string} str
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function trimFileProtocol(str) {
|
|
||||||
return str.startsWith("file://") ? str.slice(7) : str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts the file name from a file path
|
|
||||||
* @param {string} str
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function fileNameForPath(str) {
|
|
||||||
if (typeof str !== "string") return "";
|
|
||||||
const trimmed = trimFileProtocol(str);
|
|
||||||
return trimmed.split(/[\\/]/).pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the file extension from a file path or name
|
|
||||||
* @param {string} str
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function trimFileExt(str) {
|
|
||||||
if (typeof str !== "string") return "";
|
|
||||||
const trimmed = trimFileProtocol(str);
|
|
||||||
const lastDot = trimmed.lastIndexOf(".");
|
|
||||||
if (lastDot > -1 && lastDot > trimmed.lastIndexOf("/")) {
|
|
||||||
return trimmed.slice(0, lastDot);
|
|
||||||
}
|
|
||||||
return trimmed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
property string entry
|
|
||||||
property real maxWidth
|
|
||||||
property real maxHeight
|
|
||||||
|
|
||||||
property string imageDecodePath: Directories.cliphistDecode
|
|
||||||
property string imageDecodeFileName: `${entryNumber}`
|
|
||||||
property string imageDecodeFilePath: `${imageDecodePath}/${imageDecodeFileName}`
|
|
||||||
property string source
|
|
||||||
|
|
||||||
property int entryNumber: {
|
|
||||||
if (!root.entry) return 0
|
|
||||||
const match = root.entry.match(/^(\d+)\t/)
|
|
||||||
return match ? parseInt(match[1]) : 0
|
|
||||||
}
|
|
||||||
property int imageWidth: {
|
|
||||||
if (!root.entry) return 0
|
|
||||||
const match = root.entry.match(/(\d+)x(\d+)/)
|
|
||||||
return match ? parseInt(match[1]) : 0
|
|
||||||
}
|
|
||||||
property int imageHeight: {
|
|
||||||
if (!root.entry) return 0
|
|
||||||
const match = root.entry.match(/(\d+)x(\d+)/)
|
|
||||||
return match ? parseInt(match[2]) : 0
|
|
||||||
}
|
|
||||||
property real scale: {
|
|
||||||
return Math.min(
|
|
||||||
root.maxWidth / imageWidth,
|
|
||||||
root.maxHeight / imageHeight,
|
|
||||||
1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
color: Appearance.colors.colLayer1
|
|
||||||
radius: Appearance.rounding.small
|
|
||||||
implicitHeight: imageHeight * scale
|
|
||||||
implicitWidth: imageWidth * scale
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
decodeImageProcess.running = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: decodeImageProcess
|
|
||||||
command: ["bash", "-c",
|
|
||||||
`[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | cliphist decode > '${imageDecodeFilePath}'`
|
|
||||||
]
|
|
||||||
onExited: (exitCode, exitStatus) => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
root.source = imageDecodeFilePath
|
|
||||||
} else {
|
|
||||||
console.error("[CliphistImage] Failed to decode image for entry:", root.entry)
|
|
||||||
root.source = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onDestruction: {
|
|
||||||
Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`])
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: image
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
source: Qt.resolvedUrl(root.source)
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
antialiasing: true
|
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
width: root.imageWidth * root.scale
|
|
||||||
height: root.imageHeight * root.scale
|
|
||||||
sourceSize.width: width
|
|
||||||
sourceSize.height: height
|
|
||||||
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: OpacityMask {
|
|
||||||
maskSource: Rectangle {
|
|
||||||
width: image.width
|
|
||||||
height: image.height
|
|
||||||
radius: root.radius
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
property string title
|
|
||||||
default property alias data: sectionContent.data
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: 8
|
|
||||||
StyledText {
|
|
||||||
text: root.title
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.larger
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
id: sectionContent
|
|
||||||
spacing: 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: root
|
|
||||||
property real iconSize: Appearance?.font.pixelSize.small ?? 16
|
|
||||||
property real fill: 0
|
|
||||||
property real truncatedFill: Math.round(fill * 100) / 100 // Reduce memory consumption spikes from constant font remapping
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
font {
|
|
||||||
hintingPreference: Font.PreferFullHinting
|
|
||||||
family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded"
|
|
||||||
pixelSize: iconSize
|
|
||||||
weight: Font.Normal + (Font.DemiBold - Font.Normal) * fill
|
|
||||||
variableAxes: {
|
|
||||||
"FILL": truncatedFill,
|
|
||||||
// "wght": font.weight,
|
|
||||||
// "GRAD": 0,
|
|
||||||
"opsz": iconSize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
color: Appearance.m3colors.m3onBackground
|
|
||||||
|
|
||||||
// Behavior on fill {
|
|
||||||
// NumberAnimation {
|
|
||||||
// duration: Appearance?.animation.elementMoveFast.duration ?? 200
|
|
||||||
// easing.type: Appearance?.animation.elementMoveFast.type ?? Easing.BezierSpline
|
|
||||||
// easing.bezierCurve: Appearance?.animation.elementMoveFast.bezierCurve ?? [0.34, 0.80, 0.34, 1.00, 1, 1]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
spacing: 0
|
|
||||||
required property var tabButtonList // Something like [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}]
|
|
||||||
required property var externalTrackedTab
|
|
||||||
property bool enableIndicatorAnimation: false
|
|
||||||
property color colIndicator: Appearance?.colors.colPrimary ?? "#65558F"
|
|
||||||
property color colBorder: Appearance?.m3colors.m3outlineVariant ?? "#C6C6D0"
|
|
||||||
signal currentIndexChanged(int index)
|
|
||||||
|
|
||||||
property bool centerTabBar: parent.width > 500
|
|
||||||
Layout.fillWidth: !centerTabBar
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
implicitWidth: Math.max(tabBar.implicitWidth, 600)
|
|
||||||
|
|
||||||
TabBar {
|
|
||||||
id: tabBar
|
|
||||||
Layout.fillWidth: true
|
|
||||||
currentIndex: root.externalTrackedTab
|
|
||||||
onCurrentIndexChanged: {
|
|
||||||
root.onCurrentIndexChanged(currentIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Item {
|
|
||||||
WheelHandler {
|
|
||||||
onWheel: (event) => {
|
|
||||||
if (event.angleDelta.y < 0)
|
|
||||||
tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1)
|
|
||||||
else if (event.angleDelta.y > 0)
|
|
||||||
tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0)
|
|
||||||
}
|
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: root.tabButtonList
|
|
||||||
delegate: PrimaryTabButton {
|
|
||||||
selected: (index == root.externalTrackedTab)
|
|
||||||
buttonText: modelData.name
|
|
||||||
buttonIcon: modelData.icon
|
|
||||||
minimumWidth: 160
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item { // Tab indicator
|
|
||||||
id: tabIndicator
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 3
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onExternalTrackedTabChanged() {
|
|
||||||
root.enableIndicatorAnimation = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: indicator
|
|
||||||
property int tabCount: root.tabButtonList.length
|
|
||||||
property real fullTabSize: root.width / tabCount;
|
|
||||||
property real targetWidth: tabBar.contentItem?.children[0]?.children[tabBar.currentIndex]?.tabContentWidth ?? 0
|
|
||||||
|
|
||||||
implicitWidth: targetWidth
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
bottom: parent.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2
|
|
||||||
|
|
||||||
color: root.colIndicator
|
|
||||||
radius: Appearance?.rounding.full ?? 9999
|
|
||||||
|
|
||||||
Behavior on x {
|
|
||||||
animation: Appearance?.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
|
||||||
animation: Appearance?.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle { // Tabbar bottom border
|
|
||||||
id: tabBarBottomBorder
|
|
||||||
Layout.fillWidth: true
|
|
||||||
implicitHeight: 1
|
|
||||||
color: root.colBorder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
TabButton {
|
|
||||||
id: button
|
|
||||||
property string buttonText
|
|
||||||
property string buttonIcon
|
|
||||||
property real minimumWidth: 110
|
|
||||||
property bool selected: false
|
|
||||||
property int tabContentWidth: contentItem.children[0].implicitWidth
|
|
||||||
property int rippleDuration: 1200
|
|
||||||
height: buttonBackground.height
|
|
||||||
implicitWidth: Math.max(tabContentWidth, buttonBackground.implicitWidth, minimumWidth)
|
|
||||||
|
|
||||||
property color colBackground: ColorUtils.transparentize(Appearance?.colors.colLayer1Hover, 1) || "transparent"
|
|
||||||
property color colBackgroundHover: Appearance?.colors.colLayer1Hover ?? "#E5DFED"
|
|
||||||
property color colRipple: Appearance?.colors.colLayer1Active ?? "#D6CEE2"
|
|
||||||
property color colActive: Appearance?.colors.colPrimary ?? "#65558F"
|
|
||||||
property color colInactive: Appearance?.colors.colOnLayer1 ?? "#45464F"
|
|
||||||
|
|
||||||
component RippleAnim: NumberAnimation {
|
|
||||||
duration: rippleDuration
|
|
||||||
easing.type: Appearance?.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance?.animationCurves.standardDecel
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onPressed: (event) => {
|
|
||||||
const {x,y} = event
|
|
||||||
const stateY = buttonBackground.y;
|
|
||||||
rippleAnim.x = x;
|
|
||||||
rippleAnim.y = y - stateY;
|
|
||||||
|
|
||||||
const dist = (ox,oy) => ox*ox + oy*oy
|
|
||||||
const stateEndY = stateY + buttonBackground.height
|
|
||||||
rippleAnim.radius = Math.sqrt(Math.max(dist(0, stateY), dist(0, stateEndY), dist(width, stateY), dist(width, stateEndY)))
|
|
||||||
|
|
||||||
rippleFadeAnim.complete();
|
|
||||||
rippleAnim.restart();
|
|
||||||
}
|
|
||||||
onReleased: (event) => {
|
|
||||||
button.click() // Because the MouseArea already consumed the event
|
|
||||||
rippleFadeAnim.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RippleAnim {
|
|
||||||
id: rippleFadeAnim
|
|
||||||
target: ripple
|
|
||||||
property: "opacity"
|
|
||||||
to: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
id: rippleAnim
|
|
||||||
|
|
||||||
property real x
|
|
||||||
property real y
|
|
||||||
property real radius
|
|
||||||
|
|
||||||
PropertyAction {
|
|
||||||
target: ripple
|
|
||||||
property: "x"
|
|
||||||
value: rippleAnim.x
|
|
||||||
}
|
|
||||||
PropertyAction {
|
|
||||||
target: ripple
|
|
||||||
property: "y"
|
|
||||||
value: rippleAnim.y
|
|
||||||
}
|
|
||||||
PropertyAction {
|
|
||||||
target: ripple
|
|
||||||
property: "opacity"
|
|
||||||
value: 1
|
|
||||||
}
|
|
||||||
ParallelAnimation {
|
|
||||||
RippleAnim {
|
|
||||||
target: ripple
|
|
||||||
properties: "implicitWidth,implicitHeight"
|
|
||||||
from: 0
|
|
||||||
to: rippleAnim.radius * 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
id: buttonBackground
|
|
||||||
radius: Appearance?.rounding.small
|
|
||||||
implicitHeight: 50
|
|
||||||
color: (button.hovered ? button.colBackgroundHover : button.colBackground)
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: OpacityMask {
|
|
||||||
maskSource: Rectangle {
|
|
||||||
width: buttonBackground.width
|
|
||||||
height: buttonBackground.height
|
|
||||||
radius: buttonBackground.radius
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: ripple
|
|
||||||
width: ripple.implicitWidth
|
|
||||||
height: ripple.implicitHeight
|
|
||||||
opacity: 0
|
|
||||||
|
|
||||||
property real implicitWidth: 0
|
|
||||||
property real implicitHeight: 0
|
|
||||||
visible: width > 0 && height > 0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
RadialGradient {
|
|
||||||
anchors.fill: parent
|
|
||||||
gradient: Gradient {
|
|
||||||
GradientStop { position: 0.0; color: button.colRipple }
|
|
||||||
GradientStop { position: 0.3; color: button.colRipple }
|
|
||||||
GradientStop { position: 0.5 ; color: Qt.rgba(button.colRipple.r, button.colRipple.g, button.colRipple.b, 0) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
transform: Translate {
|
|
||||||
x: -ripple.width / 2
|
|
||||||
y: -ripple.height / 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
anchors.centerIn: buttonBackground
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 0
|
|
||||||
MaterialSymbol {
|
|
||||||
visible: buttonIcon?.length > 0
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: buttonIcon
|
|
||||||
iconSize: Appearance?.font.pixelSize.hugeass ?? 25
|
|
||||||
fill: selected ? 1 : 0
|
|
||||||
color: selected ? button.colActive : button.colInactive
|
|
||||||
Behavior on color {
|
|
||||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
id: buttonTextWidget
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
font.pixelSize: Appearance?.font.pixelSize.small
|
|
||||||
color: selected ? button.colActive : button.colInactive
|
|
||||||
text: buttonText
|
|
||||||
Behavior on color {
|
|
||||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
|
|
||||||
GroupButton {
|
|
||||||
id: root
|
|
||||||
horizontalPadding: 12
|
|
||||||
verticalPadding: 8
|
|
||||||
bounce: false
|
|
||||||
property bool leftmost: false
|
|
||||||
property bool rightmost: false
|
|
||||||
leftRadius: (toggled || leftmost) ? (height / 2) : Appearance.rounding.unsharpenmore
|
|
||||||
rightRadius: (toggled || rightmost) ? (height / 2) : Appearance.rounding.unsharpenmore
|
|
||||||
colBackground: Appearance.colors.colSecondaryContainer
|
|
||||||
contentItem: StyledText {
|
|
||||||
color: parent.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
|
|
||||||
text: root.buttonText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Widgets
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Material 3 progress bar. See https://m3.material.io/components/progress-indicators/overview
|
|
||||||
*/
|
|
||||||
ProgressBar {
|
|
||||||
id: root
|
|
||||||
property real valueBarWidth: 120
|
|
||||||
property real valueBarHeight: 4
|
|
||||||
property real valueBarGap: 4
|
|
||||||
property color highlightColor: Appearance?.colors.colPrimary ?? "#685496"
|
|
||||||
property color trackColor: Appearance?.m3colors.m3secondaryContainer ?? "#F1D3F9"
|
|
||||||
property bool sperm: false // If true, the progress bar will have a wavy fill effect
|
|
||||||
property bool animateSperm: true
|
|
||||||
property real spermAmplitudeMultiplier: sperm ? 0.5 : 0
|
|
||||||
property real spermFrequency: 6
|
|
||||||
property real spermFps: 60
|
|
||||||
|
|
||||||
Behavior on spermAmplitudeMultiplier {
|
|
||||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on value {
|
|
||||||
animation: Appearance?.animation.elementMoveEnter.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Item {
|
|
||||||
anchors.fill: parent
|
|
||||||
implicitHeight: valueBarHeight
|
|
||||||
implicitWidth: valueBarWidth
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Canvas {
|
|
||||||
id: wavyFill
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
height: parent.height * 6
|
|
||||||
onPaint: {
|
|
||||||
var ctx = getContext("2d");
|
|
||||||
ctx.clearRect(0, 0, width, height);
|
|
||||||
|
|
||||||
var progress = root.visualPosition;
|
|
||||||
var fillWidth = progress * width;
|
|
||||||
var amplitude = parent.height * root.spermAmplitudeMultiplier;
|
|
||||||
var frequency = root.spermFrequency;
|
|
||||||
var phase = Date.now() / 400.0;
|
|
||||||
var centerY = height / 2;
|
|
||||||
|
|
||||||
ctx.strokeStyle = root.highlightColor;
|
|
||||||
ctx.lineWidth = parent.height;
|
|
||||||
ctx.lineCap = "round";
|
|
||||||
ctx.beginPath();
|
|
||||||
for (var x = ctx.lineWidth / 2; x <= fillWidth; x += 1) {
|
|
||||||
var waveY = centerY + amplitude * Math.sin(frequency * 2 * Math.PI * x / width + phase);
|
|
||||||
if (x === 0)
|
|
||||||
ctx.moveTo(x, waveY);
|
|
||||||
else
|
|
||||||
ctx.lineTo(x, waveY);
|
|
||||||
}
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onValueChanged() { wavyFill.requestPaint(); }
|
|
||||||
function onHighlightColorChanged() { wavyFill.requestPaint(); }
|
|
||||||
}
|
|
||||||
Timer {
|
|
||||||
interval: 1000 / root.spermFps
|
|
||||||
running: root.animateSperm
|
|
||||||
repeat: root.sperm
|
|
||||||
onTriggered: wavyFill.requestPaint()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Rectangle { // Right remaining part fill
|
|
||||||
anchors.right: parent.right
|
|
||||||
width: (1 - root.visualPosition) * parent.width - valueBarGap
|
|
||||||
height: parent.height
|
|
||||||
radius: Appearance?.rounding.full ?? 9999
|
|
||||||
color: root.trackColor
|
|
||||||
}
|
|
||||||
Rectangle { // Stop point
|
|
||||||
anchors.right: parent.right
|
|
||||||
width: valueBarGap
|
|
||||||
height: valueBarGap
|
|
||||||
radius: Appearance?.rounding.full ?? 9999
|
|
||||||
color: root.highlightColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
Text {
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
font {
|
|
||||||
hintingPreference: Font.PreferFullHinting
|
|
||||||
family: Appearance?.font.family.main ?? "sans-serif"
|
|
||||||
pixelSize: Appearance?.font.pixelSize.small ?? 15
|
|
||||||
}
|
|
||||||
color: Appearance?.m3colors.m3onBackground ?? "black"
|
|
||||||
linkColor: Appearance?.m3colors.m3primary
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
ToolTip {
|
|
||||||
id: root
|
|
||||||
property string content
|
|
||||||
property bool extraVisibleCondition: true
|
|
||||||
property bool alternativeVisibleCondition: false
|
|
||||||
property bool internalVisibleCondition: {
|
|
||||||
const ans = (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition
|
|
||||||
return ans
|
|
||||||
}
|
|
||||||
verticalPadding: 5
|
|
||||||
horizontalPadding: 10
|
|
||||||
opacity: internalVisibleCondition ? 1 : 0
|
|
||||||
visible: opacity > 0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
background: null
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
id: contentItemBackground
|
|
||||||
implicitWidth: tooltipTextObject.width + 2 * root.horizontalPadding
|
|
||||||
implicitHeight: tooltipTextObject.height + 2 * root.verticalPadding
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: backgroundRectangle
|
|
||||||
anchors.bottom: contentItemBackground.bottom
|
|
||||||
anchors.horizontalCenter: contentItemBackground.horizontalCenter
|
|
||||||
color: Appearance?.colors.colTooltip ?? "#3C4043"
|
|
||||||
radius: Appearance?.rounding.verysmall ?? 7
|
|
||||||
width: internalVisibleCondition ? (tooltipTextObject.width + 2 * padding) : 0
|
|
||||||
height: internalVisibleCondition ? (tooltipTextObject.height + 2 * padding) : 0
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
Behavior on height {
|
|
||||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: tooltipTextObject
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: content
|
|
||||||
font.pixelSize: Appearance?.font.pixelSize.smaller ?? 14
|
|
||||||
font.hintingPreference: Font.PreferNoHinting // Prevent shaky text
|
|
||||||
color: Appearance?.colors.colOnTooltip ?? "#FFFFFF"
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
import qs
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import qs.modules.lock
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
|
||||||
Scope {
|
|
||||||
id: root
|
|
||||||
// This stores all the information shared between the lock surfaces on each screen.
|
|
||||||
// https://github.com/quickshell-mirror/quickshell-examples/tree/master/lockscreen
|
|
||||||
LockContext {
|
|
||||||
id: lockContext
|
|
||||||
|
|
||||||
onUnlocked: {
|
|
||||||
// Unlock the screen before exiting, or the compositor will display a
|
|
||||||
// fallback lock you can't interact with.
|
|
||||||
GlobalStates.screenLocked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WlSessionLock {
|
|
||||||
id: lock
|
|
||||||
locked: GlobalStates.screenLocked
|
|
||||||
|
|
||||||
WlSessionLockSurface {
|
|
||||||
color: "transparent"
|
|
||||||
Loader {
|
|
||||||
active: GlobalStates.screenLocked
|
|
||||||
anchors.fill: parent
|
|
||||||
opacity: active ? 1 : 0
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
sourceComponent: LockSurface {
|
|
||||||
context: lockContext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blur layer hack
|
|
||||||
Variants {
|
|
||||||
model: Quickshell.screens
|
|
||||||
|
|
||||||
LazyLoader {
|
|
||||||
id: blurLayerLoader
|
|
||||||
required property var modelData
|
|
||||||
active: GlobalStates.screenLocked
|
|
||||||
component: PanelWindow {
|
|
||||||
screen: blurLayerLoader.modelData
|
|
||||||
WlrLayershell.namespace: "quickshell:lockWindowPusher"
|
|
||||||
color: "transparent"
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
}
|
|
||||||
// implicitHeight: lockContext.currentText == "" ? 1 : screen.height
|
|
||||||
implicitHeight: 1
|
|
||||||
exclusiveZone: screen.height * 3 // For some reason if we don't multiply by some number it would look really weird
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHandler {
|
|
||||||
target: "lock"
|
|
||||||
|
|
||||||
function activate(): void {
|
|
||||||
GlobalStates.screenLocked = true;
|
|
||||||
}
|
|
||||||
function focus(): void {
|
|
||||||
lockContext.shouldReFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "lock"
|
|
||||||
description: "Locks the screen"
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
GlobalStates.screenLocked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "lockFocus"
|
|
||||||
description: "Re-focuses the lock screen. This is because Hyprland after waking up for whatever reason"
|
|
||||||
+ "decides to keyboard-unfocus the lock screen"
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
// console.log("I BEG FOR PLEAS REFOCUZ")
|
|
||||||
lockContext.shouldReFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import qs
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Services.Pam
|
|
||||||
|
|
||||||
Scope {
|
|
||||||
id: root
|
|
||||||
signal shouldReFocus()
|
|
||||||
signal unlocked()
|
|
||||||
signal failed()
|
|
||||||
|
|
||||||
// These properties are in the context and not individual lock surfaces
|
|
||||||
// so all surfaces can share the same state.
|
|
||||||
property string currentText: ""
|
|
||||||
property bool unlockInProgress: false
|
|
||||||
property bool showFailure: false
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: passwordClearTimer
|
|
||||||
interval: 10000
|
|
||||||
onTriggered: {
|
|
||||||
root.currentText = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onCurrentTextChanged: {
|
|
||||||
showFailure = false; // Clear the failure text once the user starts typing.
|
|
||||||
GlobalStates.screenLockContainsCharacters = currentText.length > 0;
|
|
||||||
passwordClearTimer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryUnlock() {
|
|
||||||
if (currentText === "") return;
|
|
||||||
|
|
||||||
root.unlockInProgress = true;
|
|
||||||
pam.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
PamContext {
|
|
||||||
id: pam
|
|
||||||
|
|
||||||
// Its best to have a custom pam config for quickshell, as the system one
|
|
||||||
// might not be what your interface expects, and break in some way.
|
|
||||||
// This particular example only supports passwords.
|
|
||||||
configDirectory: "pam"
|
|
||||||
config: "password.conf"
|
|
||||||
|
|
||||||
// pam_unix will ask for a response for the password prompt
|
|
||||||
onPamMessage: {
|
|
||||||
if (this.responseRequired) {
|
|
||||||
this.respond(root.currentText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pam_unix won't send any important messages so all we need is the completion status.
|
|
||||||
onCompleted: result => {
|
|
||||||
if (result == PamResult.Success) {
|
|
||||||
root.unlocked();
|
|
||||||
} else {
|
|
||||||
root.showFailure = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
root.currentText = "";
|
|
||||||
root.unlockInProgress = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: root
|
|
||||||
required property LockContext context
|
|
||||||
property bool active: false
|
|
||||||
property bool showInputField: active || context.currentText.length > 0
|
|
||||||
|
|
||||||
function forceFieldFocus() {
|
|
||||||
passwordBox.forceActiveFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
forceFieldFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: context
|
|
||||||
function onShouldReFocus() {
|
|
||||||
forceFieldFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: (event) => { // Esc to clear
|
|
||||||
// console.log("KEY!!")
|
|
||||||
if (event.key === Qt.Key_Escape) {
|
|
||||||
root.context.currentText = ""
|
|
||||||
}
|
|
||||||
forceFieldFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
onPressed: (mouse) => {
|
|
||||||
forceFieldFocus();
|
|
||||||
// console.log("Pressed")
|
|
||||||
}
|
|
||||||
onPositionChanged: (mouse) => {
|
|
||||||
forceFieldFocus();
|
|
||||||
// console.log(JSON.stringify(mouse))
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
// RippleButton {
|
|
||||||
// anchors {
|
|
||||||
// top: parent.top
|
|
||||||
// left: parent.left
|
|
||||||
// leftMargin: 10
|
|
||||||
// topMargin: 10
|
|
||||||
// }
|
|
||||||
// implicitHeight: 40
|
|
||||||
// colBackground: Appearance.colors.colLayer2
|
|
||||||
// onClicked: context.unlocked()
|
|
||||||
// contentItem: StyledText {
|
|
||||||
// text: "[[ DEBUG BYPASS ]]"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Password entry
|
|
||||||
Rectangle {
|
|
||||||
id: passwordBoxContainer
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
bottom: parent.bottom
|
|
||||||
bottomMargin: root.showInputField ? 20 : -height
|
|
||||||
}
|
|
||||||
Behavior on anchors.bottomMargin {
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
color: Appearance.colors.colLayer2
|
|
||||||
implicitWidth: 160
|
|
||||||
implicitHeight: 44
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
visible: root.context.showFailure && passwordBox.text.length == 0
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "Incorrect"
|
|
||||||
color: Appearance.m3colors.m3error
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledTextInput {
|
|
||||||
id: passwordBox
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
margins: 10
|
|
||||||
}
|
|
||||||
clip: true
|
|
||||||
horizontalAlignment: TextInput.AlignHCenter
|
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
|
||||||
focus: true
|
|
||||||
onFocusChanged: root.forceFieldFocus();
|
|
||||||
color: Appearance.colors.colOnLayer2
|
|
||||||
font {
|
|
||||||
pixelSize: 10
|
|
||||||
}
|
|
||||||
|
|
||||||
// Password
|
|
||||||
enabled: !root.context.unlockInProgress
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
inputMethodHints: Qt.ImhSensitiveData
|
|
||||||
|
|
||||||
// Synchronizing (across monitors) and unlocking
|
|
||||||
onTextChanged: root.context.currentText = this.text
|
|
||||||
onAccepted: root.context.tryUnlock()
|
|
||||||
Connections {
|
|
||||||
target: root.context
|
|
||||||
function onCurrentTextChanged() {
|
|
||||||
passwordBox.text = root.context.currentText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RippleButton {
|
|
||||||
anchors {
|
|
||||||
verticalCenter: passwordBoxContainer.verticalCenter
|
|
||||||
left: passwordBoxContainer.right
|
|
||||||
leftMargin: 5
|
|
||||||
}
|
|
||||||
|
|
||||||
visible: opacity > 0
|
|
||||||
implicitHeight: passwordBoxContainer.implicitHeight - 12
|
|
||||||
implicitWidth: implicitHeight
|
|
||||||
toggled: true
|
|
||||||
buttonRadius: passwordBoxContainer.radius
|
|
||||||
colBackground: Appearance.colors.colLayer2
|
|
||||||
onClicked: root.context.tryUnlock()
|
|
||||||
|
|
||||||
contentItem: MaterialSymbol {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
iconSize: 24
|
|
||||||
text: "arrow_right_alt"
|
|
||||||
color: Appearance.colors.colOnPrimary
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
auth required pam_unix.so
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Wayland
|
|
||||||
|
|
||||||
Scope {
|
|
||||||
id: root
|
|
||||||
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
|
||||||
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
|
|
||||||
|
|
||||||
function triggerOsd() {
|
|
||||||
GlobalStates.osdBrightnessOpen = true
|
|
||||||
osdTimeout.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: osdTimeout
|
|
||||||
interval: Config.options.osd.timeout
|
|
||||||
repeat: false
|
|
||||||
running: false
|
|
||||||
onTriggered: {
|
|
||||||
GlobalStates.osdBrightnessOpen = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Audio.sink?.audio ?? null
|
|
||||||
function onVolumeChanged() {
|
|
||||||
if (!Audio.ready) return
|
|
||||||
GlobalStates.osdBrightnessOpen = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Brightness
|
|
||||||
function onBrightnessChanged() {
|
|
||||||
if (!root.brightnessMonitor.ready) return
|
|
||||||
root.triggerOsd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: osdLoader
|
|
||||||
active: GlobalStates.osdBrightnessOpen
|
|
||||||
|
|
||||||
sourceComponent: PanelWindow {
|
|
||||||
id: osdRoot
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onFocusedScreenChanged() {
|
|
||||||
osdRoot.screen = root.focusedScreen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exclusionMode: ExclusionMode.Normal
|
|
||||||
WlrLayershell.namespace: "quickshell:onScreenDisplay"
|
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: !Config.options.bar.bottom
|
|
||||||
bottom: Config.options.bar.bottom
|
|
||||||
}
|
|
||||||
mask: Region {
|
|
||||||
item: osdValuesWrapper
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: columnLayout.implicitWidth
|
|
||||||
implicitHeight: columnLayout.implicitHeight
|
|
||||||
visible: osdLoader.active
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: columnLayout
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
Item {
|
|
||||||
id: osdValuesWrapper
|
|
||||||
// Extra space for shadow
|
|
||||||
implicitHeight: osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2
|
|
||||||
implicitWidth: osdValues.implicitWidth
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
onEntered: GlobalStates.osdBrightnessOpen = false
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.menuDecel.duration
|
|
||||||
easing.type: Appearance.animation.menuDecel.type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OsdValueIndicator {
|
|
||||||
id: osdValues
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Appearance.sizes.elevationMargin
|
|
||||||
value: root.brightnessMonitor?.brightness ?? 50
|
|
||||||
icon: "light_mode"
|
|
||||||
rotateIcon: true
|
|
||||||
scaleIcon: true
|
|
||||||
name: Translation.tr("Brightness")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHandler {
|
|
||||||
target: "osdBrightness"
|
|
||||||
|
|
||||||
function trigger() {
|
|
||||||
root.triggerOsd()
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide() {
|
|
||||||
GlobalStates.osdBrightnessOpen = false
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggle() {
|
|
||||||
GlobalStates.osdBrightnessOpen = !GlobalStates.osdBrightnessOpen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "osdBrightnessTrigger"
|
|
||||||
description: "Triggers brightness OSD on press"
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
root.triggerOsd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "osdBrightnessHide"
|
|
||||||
description: "Hides brightness OSD on press"
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
GlobalStates.osdBrightnessOpen = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Wayland
|
|
||||||
|
|
||||||
Item { // Window
|
|
||||||
id: root
|
|
||||||
property var toplevel
|
|
||||||
property var windowData
|
|
||||||
property var monitorData
|
|
||||||
property var scale
|
|
||||||
property var availableWorkspaceWidth
|
|
||||||
property var availableWorkspaceHeight
|
|
||||||
property bool restrictToWorkspace: true
|
|
||||||
property real initX: Math.max((windowData?.at[0] - (monitorData?.x ?? 0) - monitorData?.reserved[0]) * root.scale, 0) + xOffset
|
|
||||||
property real initY: Math.max((windowData?.at[1] - (monitorData?.y ?? 0) - monitorData?.reserved[1]) * root.scale, 0) + yOffset
|
|
||||||
property real xOffset: 0
|
|
||||||
property real yOffset: 0
|
|
||||||
property int widgetMonitorId: 0
|
|
||||||
|
|
||||||
property var targetWindowWidth: windowData?.size[0] * scale
|
|
||||||
property var targetWindowHeight: windowData?.size[1] * scale
|
|
||||||
property bool hovered: false
|
|
||||||
property bool pressed: false
|
|
||||||
|
|
||||||
property var iconToWindowRatio: 0.35
|
|
||||||
property var xwaylandIndicatorToIconRatio: 0.35
|
|
||||||
property var iconToWindowRatioCompact: 0.6
|
|
||||||
property var iconPath: Quickshell.iconPath(AppSearch.guessIcon(windowData?.class), "image-missing")
|
|
||||||
property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth
|
|
||||||
|
|
||||||
property bool indicateXWayland: windowData?.xwayland ?? false
|
|
||||||
|
|
||||||
x: initX
|
|
||||||
y: initY
|
|
||||||
width: windowData?.size[0] * root.scale
|
|
||||||
height: windowData?.size[1] * root.scale
|
|
||||||
opacity: windowData.monitor == widgetMonitorId ? 1 : 0.4
|
|
||||||
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: OpacityMask {
|
|
||||||
maskSource: Rectangle {
|
|
||||||
width: root.width
|
|
||||||
height: root.height
|
|
||||||
radius: Appearance.rounding.windowRounding * root.scale
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on x {
|
|
||||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
Behavior on y {
|
|
||||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
Behavior on width {
|
|
||||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
Behavior on height {
|
|
||||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
ScreencopyView {
|
|
||||||
id: windowPreview
|
|
||||||
anchors.fill: parent
|
|
||||||
captureSource: GlobalStates.overviewOpen ? root.toplevel : null
|
|
||||||
live: true
|
|
||||||
|
|
||||||
// Color overlay for interactions
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: Appearance.rounding.windowRounding * root.scale
|
|
||||||
color: pressed ? ColorUtils.transparentize(Appearance.colors.colLayer2Active, 0.5) :
|
|
||||||
hovered ? ColorUtils.transparentize(Appearance.colors.colLayer2Hover, 0.7) :
|
|
||||||
ColorUtils.transparentize(Appearance.colors.colLayer2)
|
|
||||||
border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.7)
|
|
||||||
border.width : 1
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
spacing: Appearance.font.pixelSize.smaller * 0.5
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: windowIcon
|
|
||||||
property var iconSize: {
|
|
||||||
// console.log("-=-=-", root.toplevel.title, "-=-=-")
|
|
||||||
// console.log("Target window size:", targetWindowWidth, targetWindowHeight)
|
|
||||||
// console.log("Icon ratio:", root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio)
|
|
||||||
// console.log("Scale:", root.monitorData.scale)
|
|
||||||
// console.log("Final:", Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / root.monitorData.scale)
|
|
||||||
return Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / root.monitorData.scale;
|
|
||||||
}
|
|
||||||
// mipmap: true
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
source: root.iconPath
|
|
||||||
width: iconSize
|
|
||||||
height: iconSize
|
|
||||||
sourceSize: Qt.size(iconSize, iconSize)
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
Behavior on height {
|
|
||||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
|
||||||
Scope {
|
|
||||||
id: screenCorners
|
|
||||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
|
||||||
|
|
||||||
component CornerPanelWindow: PanelWindow {
|
|
||||||
id: cornerPanelWindow
|
|
||||||
property bool fullscreen
|
|
||||||
visible: (Config.options.appearance.fakeScreenRounding === 1 || (Config.options.appearance.fakeScreenRounding === 2 && !fullscreen))
|
|
||||||
property var corner
|
|
||||||
|
|
||||||
exclusionMode: ExclusionMode.Ignore
|
|
||||||
mask: Region {
|
|
||||||
item: null
|
|
||||||
}
|
|
||||||
WlrLayershell.namespace: "quickshell:screenCorners"
|
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: cornerPanelWindow.corner === RoundCorner.CornerEnum.TopLeft || cornerPanelWindow.corner === RoundCorner.CornerEnum.TopRight
|
|
||||||
left: cornerPanelWindow.corner === RoundCorner.CornerEnum.TopLeft || cornerPanelWindow.corner === RoundCorner.CornerEnum.BottomLeft
|
|
||||||
bottom: cornerPanelWindow.corner === RoundCorner.CornerEnum.BottomLeft || cornerPanelWindow.corner === RoundCorner.CornerEnum.BottomRight
|
|
||||||
right: cornerPanelWindow.corner === RoundCorner.CornerEnum.TopRight || cornerPanelWindow.corner === RoundCorner.CornerEnum.BottomRight
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: cornerWidget.implicitWidth
|
|
||||||
implicitHeight: cornerWidget.implicitHeight
|
|
||||||
RoundCorner {
|
|
||||||
id: cornerWidget
|
|
||||||
implicitSize: Appearance.rounding.screenRounding
|
|
||||||
corner: cornerPanelWindow.corner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Variants {
|
|
||||||
model: Quickshell.screens
|
|
||||||
|
|
||||||
Scope {
|
|
||||||
id: monitorScope
|
|
||||||
required property var modelData
|
|
||||||
property HyprlandMonitor monitor: Hyprland.monitorFor(modelData)
|
|
||||||
|
|
||||||
// Hide when fullscreen
|
|
||||||
property list<HyprlandWorkspace> workspacesForMonitor: Hyprland.workspaces.values.filter(workspace=>workspace.monitor && workspace.monitor.name == monitor.name)
|
|
||||||
property var activeWorkspaceWithFullscreen: workspacesForMonitor.filter(workspace=>((workspace.toplevels.values.filter(window=>window.wayland.fullscreen)[0] != undefined) && workspace.active))[0]
|
|
||||||
property bool fullscreen: activeWorkspaceWithFullscreen != undefined
|
|
||||||
|
|
||||||
CornerPanelWindow {
|
|
||||||
screen: modelData
|
|
||||||
corner: RoundCorner.CornerEnum.TopLeft
|
|
||||||
fullscreen: monitorScope.fullscreen
|
|
||||||
}
|
|
||||||
CornerPanelWindow {
|
|
||||||
screen: modelData
|
|
||||||
corner: RoundCorner.CornerEnum.TopRight
|
|
||||||
fullscreen: monitorScope.fullscreen
|
|
||||||
}
|
|
||||||
CornerPanelWindow {
|
|
||||||
screen: modelData
|
|
||||||
corner: RoundCorner.CornerEnum.BottomLeft
|
|
||||||
fullscreen: monitorScope.fullscreen
|
|
||||||
}
|
|
||||||
CornerPanelWindow {
|
|
||||||
screen: modelData
|
|
||||||
corner: RoundCorner.CornerEnum.BottomRight
|
|
||||||
fullscreen: monitorScope.fullscreen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
|
|
||||||
ContentPage {
|
|
||||||
forceWidth: true
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Color generation")
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Shell & utilities")
|
|
||||||
checked: Config.options.appearance.wallpaperTheming.enableAppsAndShell
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.appearance.wallpaperTheming.enableAppsAndShell = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Qt apps")
|
|
||||||
checked: Config.options.appearance.wallpaperTheming.enableQtApps
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.appearance.wallpaperTheming.enableQtApps = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Shell & utilities theming must also be enabled")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Terminal")
|
|
||||||
checked: Config.options.appearance.wallpaperTheming.enableTerminal
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.appearance.wallpaperTheming.enableTerminal = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Shell & utilities theming must also be enabled")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,424 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
|
|
||||||
ContentPage {
|
|
||||||
forceWidth: true
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Policies")
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
ColumnLayout {
|
|
||||||
// Weeb policy
|
|
||||||
ContentSubsectionLabel {
|
|
||||||
text: Translation.tr("Weeb")
|
|
||||||
}
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.policies.weeb
|
|
||||||
configOptionName: "policies.weeb"
|
|
||||||
onSelected: newValue => {
|
|
||||||
Config.options.policies.weeb = newValue;
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("No"),
|
|
||||||
value: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Yes"),
|
|
||||||
value: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Closet"),
|
|
||||||
value: 2
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
// AI policy
|
|
||||||
ContentSubsectionLabel {
|
|
||||||
text: Translation.tr("AI")
|
|
||||||
}
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.policies.ai
|
|
||||||
configOptionName: "policies.ai"
|
|
||||||
onSelected: newValue => {
|
|
||||||
Config.options.policies.ai = newValue;
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("No"),
|
|
||||||
value: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Yes"),
|
|
||||||
value: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Local only"),
|
|
||||||
value: 2
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Bar")
|
|
||||||
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.bar.cornerStyle
|
|
||||||
configOptionName: "bar.cornerStyle"
|
|
||||||
onSelected: newValue => {
|
|
||||||
Config.options.bar.cornerStyle = newValue;
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Hug"),
|
|
||||||
value: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Float"),
|
|
||||||
value: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("Plain rectangle"),
|
|
||||||
value: 2
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Overall appearance")
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr('Borderless')
|
|
||||||
checked: Config.options.bar.borderless
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.borderless = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr('Show background')
|
|
||||||
checked: Config.options.bar.showBackground
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.showBackground = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Note: turning off can hurt readability")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Buttons")
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Screen snip")
|
|
||||||
checked: Config.options.bar.utilButtons.showScreenSnip
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.utilButtons.showScreenSnip = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Color picker")
|
|
||||||
checked: Config.options.bar.utilButtons.showColorPicker
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.utilButtons.showColorPicker = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Mic toggle")
|
|
||||||
checked: Config.options.bar.utilButtons.showMicToggle
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.utilButtons.showMicToggle = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Keyboard toggle")
|
|
||||||
checked: Config.options.bar.utilButtons.showKeyboardToggle
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.utilButtons.showKeyboardToggle = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Dark/Light toggle")
|
|
||||||
checked: Config.options.bar.utilButtons.showDarkModeToggle
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.utilButtons.showDarkModeToggle = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Performance Profile toggle")
|
|
||||||
checked: Config.options.bar.utilButtons.showPerformanceProfileToggle
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.utilButtons.showPerformanceProfileToggle = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Workspaces")
|
|
||||||
tooltip: Translation.tr("Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience")
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr('Show app icons')
|
|
||||||
checked: Config.options.bar.workspaces.showAppIcons
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.workspaces.showAppIcons = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr('Tint app icons')
|
|
||||||
checked: Config.options.bar.workspaces.monochromeIcons
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.workspaces.monochromeIcons = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr('Always show numbers')
|
|
||||||
checked: Config.options.bar.workspaces.alwaysShowNumbers
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.workspaces.alwaysShowNumbers = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Workspaces shown")
|
|
||||||
value: Config.options.bar.workspaces.shown
|
|
||||||
from: 1
|
|
||||||
to: 30
|
|
||||||
stepSize: 1
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.bar.workspaces.shown = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Number show delay when pressing Super (ms)")
|
|
||||||
value: Config.options.bar.workspaces.showNumberDelay
|
|
||||||
from: 0
|
|
||||||
to: 1000
|
|
||||||
stepSize: 50
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.bar.workspaces.showNumberDelay = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Tray")
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr('Tint icons')
|
|
||||||
checked: Config.options.bar.tray.monochromeIcons
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.tray.monochromeIcons = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Weather")
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Enable")
|
|
||||||
checked: Config.options.bar.weather.enable
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.bar.weather.enable = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Battery")
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Low warning")
|
|
||||||
value: Config.options.battery.low
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
stepSize: 5
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.battery.low = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Critical warning")
|
|
||||||
value: Config.options.battery.critical
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
stepSize: 5
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.battery.critical = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Automatic suspend")
|
|
||||||
checked: Config.options.battery.automaticSuspend
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.battery.automaticSuspend = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Automatically suspends the system when battery is low")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Suspend at")
|
|
||||||
value: Config.options.battery.suspend
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
stepSize: 5
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.battery.suspend = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Dock")
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Enable")
|
|
||||||
checked: Config.options.dock.enable
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.dock.enable = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Hover to reveal")
|
|
||||||
checked: Config.options.dock.hoverToReveal
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.dock.hoverToReveal = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Pinned on startup")
|
|
||||||
checked: Config.options.dock.pinnedOnStartup
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.dock.pinnedOnStartup = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Tint app icons")
|
|
||||||
checked: Config.options.dock.monochromeIcons
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.dock.monochromeIcons = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Sidebars")
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr('Keep right sidebar loaded')
|
|
||||||
checked: Config.options.sidebar.keepRightSidebarLoaded
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.sidebar.keepRightSidebarLoaded = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("On-screen display")
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Timeout (ms)")
|
|
||||||
value: Config.options.osd.timeout
|
|
||||||
from: 100
|
|
||||||
to: 3000
|
|
||||||
stepSize: 100
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.osd.timeout = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Overview")
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Enable")
|
|
||||||
checked: Config.options.overview.enable
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.overview.enable = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Scale (%)")
|
|
||||||
value: Config.options.overview.scale * 100
|
|
||||||
from: 1
|
|
||||||
to: 100
|
|
||||||
stepSize: 1
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.overview.scale = value / 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Rows")
|
|
||||||
value: Config.options.overview.rows
|
|
||||||
from: 1
|
|
||||||
to: 20
|
|
||||||
stepSize: 1
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.overview.rows = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Columns")
|
|
||||||
value: Config.options.overview.columns
|
|
||||||
from: 1
|
|
||||||
to: 20
|
|
||||||
stepSize: 1
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.overview.columns = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Screenshot tool")
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr('Show regions of potential interest')
|
|
||||||
checked: Config.options.screenshotTool.showContentRegions
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.screenshotTool.showContentRegions = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
|
|
||||||
ContentPage {
|
|
||||||
forceWidth: true
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Audio")
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Earbang protection")
|
|
||||||
checked: Config.options.audio.protection.enable
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.audio.protection.enable = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Prevents abrupt increments and restricts volume limit")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigRow {
|
|
||||||
// uniform: true
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Max allowed increase")
|
|
||||||
value: Config.options.audio.protection.maxAllowedIncrease
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
stepSize: 2
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.audio.protection.maxAllowedIncrease = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Volume limit")
|
|
||||||
value: Config.options.audio.protection.maxAllowed
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
stepSize: 2
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.audio.protection.maxAllowed = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("AI")
|
|
||||||
MaterialTextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
placeholderText: Translation.tr("System prompt")
|
|
||||||
text: Config.options.ai.systemPrompt
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
onTextChanged: {
|
|
||||||
Qt.callLater(() => {
|
|
||||||
Config.options.ai.systemPrompt = text;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Battery")
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Low warning")
|
|
||||||
value: Config.options.battery.low
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
stepSize: 5
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.battery.low = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Critical warning")
|
|
||||||
value: Config.options.battery.critical
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
stepSize: 5
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.battery.critical = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Automatic suspend")
|
|
||||||
checked: Config.options.battery.automaticSuspend
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.battery.automaticSuspend = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Automatically suspends the system when battery is low")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Suspend at")
|
|
||||||
value: Config.options.battery.suspend
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
stepSize: 5
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.battery.suspend = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Networking")
|
|
||||||
MaterialTextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
placeholderText: Translation.tr("User agent (for services that require it)")
|
|
||||||
text: Config.options.networking.userAgent
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
onTextChanged: {
|
|
||||||
Config.options.networking.userAgent = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Resources")
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Polling interval (ms)")
|
|
||||||
value: Config.options.resources.updateInterval
|
|
||||||
from: 100
|
|
||||||
to: 10000
|
|
||||||
stepSize: 100
|
|
||||||
onValueChanged: {
|
|
||||||
Config.options.resources.updateInterval = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Search")
|
|
||||||
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Use Levenshtein distance-based algorithm instead of fuzzy")
|
|
||||||
checked: Config.options.search.sloppy
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.search.sloppy = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Prefixes")
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
|
|
||||||
MaterialTextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
placeholderText: Translation.tr("Action")
|
|
||||||
text: Config.options.search.prefix.action
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
onTextChanged: {
|
|
||||||
Config.options.search.prefix.action = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MaterialTextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
placeholderText: Translation.tr("Clipboard")
|
|
||||||
text: Config.options.search.prefix.clipboard
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
onTextChanged: {
|
|
||||||
Config.options.search.prefix.clipboard = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MaterialTextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
placeholderText: Translation.tr("Emojis")
|
|
||||||
text: Config.options.search.prefix.emojis
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
onTextChanged: {
|
|
||||||
Config.options.search.prefix.emojis = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Web search")
|
|
||||||
MaterialTextField {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
placeholderText: Translation.tr("Base URL")
|
|
||||||
text: Config.options.search.engineBaseUrl
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
onTextChanged: {
|
|
||||||
Config.options.search.engineBaseUrl = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Time")
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Format")
|
|
||||||
tooltip: ""
|
|
||||||
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.time.format
|
|
||||||
configOptionName: "time.format"
|
|
||||||
onSelected: newValue => {
|
|
||||||
Config.options.time.format = newValue;
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("24h"),
|
|
||||||
value: "hh:mm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("12h am/pm"),
|
|
||||||
value: "h:mm ap"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: Translation.tr("12h AM/PM"),
|
|
||||||
value: "h:mm AP"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,245 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
|
|
||||||
ContentPage {
|
|
||||||
baseWidth: lightDarkButtonGroup.implicitWidth
|
|
||||||
forceWidth: true
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: konachanWallProc
|
|
||||||
property string status: ""
|
|
||||||
command: ["bash", "-c", FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/random_konachan_wall.sh`)]
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
console.log(`Konachan wall proc output: ${data}`);
|
|
||||||
konachanWallProc.status = data.trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Colors & Wallpaper")
|
|
||||||
|
|
||||||
// Light/Dark mode preference
|
|
||||||
ButtonGroup {
|
|
||||||
id: lightDarkButtonGroup
|
|
||||||
Layout.fillWidth: true
|
|
||||||
LightDarkPreferenceButton {
|
|
||||||
dark: false
|
|
||||||
}
|
|
||||||
LightDarkPreferenceButton {
|
|
||||||
dark: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Material palette selection
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Material palette")
|
|
||||||
ConfigSelectionArray {
|
|
||||||
currentValue: Config.options.appearance.palette.type
|
|
||||||
configOptionName: "appearance.palette.type"
|
|
||||||
onSelected: (newValue) => {
|
|
||||||
Config.options.appearance.palette.type = newValue;
|
|
||||||
Quickshell.execDetached(["bash", "-c", `${Directories.wallpaperSwitchScriptPath} --noswitch`])
|
|
||||||
}
|
|
||||||
options: [
|
|
||||||
{"value": "auto", "displayName": Translation.tr("Auto")},
|
|
||||||
{"value": "scheme-content", "displayName": Translation.tr("Content")},
|
|
||||||
{"value": "scheme-expressive", "displayName": Translation.tr("Expressive")},
|
|
||||||
{"value": "scheme-fidelity", "displayName": Translation.tr("Fidelity")},
|
|
||||||
{"value": "scheme-fruit-salad", "displayName": Translation.tr("Fruit Salad")},
|
|
||||||
{"value": "scheme-monochrome", "displayName": Translation.tr("Monochrome")},
|
|
||||||
{"value": "scheme-neutral", "displayName": Translation.tr("Neutral")},
|
|
||||||
{"value": "scheme-rainbow", "displayName": Translation.tr("Rainbow")},
|
|
||||||
{"value": "scheme-tonal-spot", "displayName": Translation.tr("Tonal Spot")}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Wallpaper selection
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Wallpaper")
|
|
||||||
RowLayout {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
RippleButtonWithIcon {
|
|
||||||
id: rndWallBtn
|
|
||||||
buttonRadius: Appearance.rounding.small
|
|
||||||
materialIcon: "wallpaper"
|
|
||||||
mainText: konachanWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: Konachan")
|
|
||||||
onClicked: {
|
|
||||||
console.log(konachanWallProc.command.join(" "))
|
|
||||||
konachanWallProc.running = true;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RippleButtonWithIcon {
|
|
||||||
materialIcon: "wallpaper"
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Pick wallpaper image on your system")
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
Quickshell.execDetached(`${Directories.wallpaperSwitchScriptPath}`)
|
|
||||||
}
|
|
||||||
mainContentComponent: Component {
|
|
||||||
RowLayout {
|
|
||||||
spacing: 10
|
|
||||||
StyledText {
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.small
|
|
||||||
text: Translation.tr("Choose file")
|
|
||||||
color: Appearance.colors.colOnSecondaryContainer
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
spacing: 3
|
|
||||||
KeyboardKey {
|
|
||||||
key: "Ctrl"
|
|
||||||
}
|
|
||||||
KeyboardKey {
|
|
||||||
key: ""
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
text: "+"
|
|
||||||
}
|
|
||||||
KeyboardKey {
|
|
||||||
key: "T"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
Layout.topMargin: 5
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: Translation.tr("Alternatively use /dark, /light, /img in the launcher")
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
|
||||||
color: Appearance.colors.colSubtext
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSection {
|
|
||||||
title: Translation.tr("Decorations & Effects")
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Transparency")
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Enable")
|
|
||||||
checked: Config.options.appearance.transparency
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.appearance.transparency = checked;
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Might look ass. Unsupported.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Fake screen rounding")
|
|
||||||
|
|
||||||
ButtonGroup {
|
|
||||||
id: fakeScreenRoundingButtonGroup
|
|
||||||
property int selectedPolicy: Config.options.appearance.fakeScreenRounding
|
|
||||||
spacing: 2
|
|
||||||
SelectionGroupButton {
|
|
||||||
property int value: 0
|
|
||||||
leftmost: true
|
|
||||||
buttonText: Translation.tr("No")
|
|
||||||
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
|
|
||||||
onClicked: {
|
|
||||||
Config.options.appearance.fakeScreenRounding = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SelectionGroupButton {
|
|
||||||
property int value: 1
|
|
||||||
buttonText: Translation.tr("Yes")
|
|
||||||
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
|
|
||||||
onClicked: {
|
|
||||||
Config.options.appearance.fakeScreenRounding = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SelectionGroupButton {
|
|
||||||
property int value: 2
|
|
||||||
rightmost: true
|
|
||||||
buttonText: Translation.tr("When not fullscreen")
|
|
||||||
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
|
|
||||||
onClicked: {
|
|
||||||
Config.options.appearance.fakeScreenRounding = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Shell windows")
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Title bar")
|
|
||||||
checked: Config.options.windows.showTitlebar
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.windows.showTitlebar = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Center title")
|
|
||||||
checked: Config.options.windows.centerTitle
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.windows.centerTitle = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentSubsection {
|
|
||||||
title: Translation.tr("Wallpaper parallax")
|
|
||||||
|
|
||||||
ConfigRow {
|
|
||||||
uniform: true
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Depends on workspace")
|
|
||||||
checked: Config.options.background.parallax.enableWorkspace
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.parallax.enableWorkspace = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSwitch {
|
|
||||||
text: Translation.tr("Depends on sidebars")
|
|
||||||
checked: Config.options.background.parallax.enableSidebar
|
|
||||||
onCheckedChanged: {
|
|
||||||
Config.options.background.parallax.enableSidebar = checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigSpinBox {
|
|
||||||
text: Translation.tr("Preferred wallpaper zoom (%)")
|
|
||||||
value: Config.options.background.parallax.workspaceZoom * 100
|
|
||||||
from: 100
|
|
||||||
to: 150
|
|
||||||
stepSize: 1
|
|
||||||
onValueChanged: {
|
|
||||||
console.log(value/100)
|
|
||||||
Config.options.background.parallax.workspaceZoom = value / 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.services
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
required property var scopeRoot
|
|
||||||
anchors.fill: parent
|
|
||||||
property var tabButtonList: [
|
|
||||||
...(Config.options.policies.ai !== 0 ? [{"icon": "neurology", "name": Translation.tr("Intelligence")}] : []),
|
|
||||||
{"icon": "translate", "name": Translation.tr("Translator")},
|
|
||||||
...(Config.options.policies.weeb === 1 ? [{"icon": "bookmark_heart", "name": Translation.tr("Anime")}] : [])
|
|
||||||
]
|
|
||||||
property int selectedTab: 0
|
|
||||||
|
|
||||||
function focusActiveItem() {
|
|
||||||
swipeView.currentItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
|
||||||
if (event.modifiers === Qt.ControlModifier) {
|
|
||||||
if (event.key === Qt.Key_PageDown) {
|
|
||||||
root.selectedTab = Math.min(root.selectedTab + 1, root.tabButtonList.length - 1)
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
else if (event.key === Qt.Key_PageUp) {
|
|
||||||
root.selectedTab = Math.max(root.selectedTab - 1, 0)
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
else if (event.key === Qt.Key_Tab) {
|
|
||||||
root.selectedTab = (root.selectedTab + 1) % root.tabButtonList.length;
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
else if (event.key === Qt.Key_Backtab) {
|
|
||||||
root.selectedTab = (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length;
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: sidebarPadding
|
|
||||||
|
|
||||||
spacing: sidebarPadding
|
|
||||||
|
|
||||||
PrimaryTabBar { // Tab strip
|
|
||||||
id: tabBar
|
|
||||||
tabButtonList: root.tabButtonList
|
|
||||||
externalTrackedTab: root.selectedTab
|
|
||||||
function onCurrentIndexChanged(currentIndex) {
|
|
||||||
root.selectedTab = currentIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SwipeView { // Content pages
|
|
||||||
id: swipeView
|
|
||||||
Layout.topMargin: 5
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
currentIndex: tabBar.externalTrackedTab
|
|
||||||
onCurrentIndexChanged: {
|
|
||||||
tabBar.enableIndicatorAnimation = true
|
|
||||||
root.selectedTab = currentIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: OpacityMask {
|
|
||||||
maskSource: Rectangle {
|
|
||||||
width: swipeView.width
|
|
||||||
height: swipeView.height
|
|
||||||
radius: Appearance.rounding.small
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentChildren: [
|
|
||||||
...(Config.options.policies.ai !== 0 ? [aiChat.createObject()] : []),
|
|
||||||
translator.createObject(),
|
|
||||||
...(Config.options.policies.weeb === 0 ? [] : [anime.createObject()])
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: aiChat
|
|
||||||
AiChat {}
|
|
||||||
}
|
|
||||||
Component {
|
|
||||||
id: translator
|
|
||||||
Translator {}
|
|
||||||
}
|
|
||||||
Component {
|
|
||||||
id: anime
|
|
||||||
Anime {}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,302 +0,0 @@
|
|||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
property int messageIndex
|
|
||||||
property var messageData
|
|
||||||
property var messageInputField
|
|
||||||
|
|
||||||
property real messagePadding: 7
|
|
||||||
property real contentSpacing: 3
|
|
||||||
|
|
||||||
property bool enableMouseSelection: false
|
|
||||||
property bool renderMarkdown: true
|
|
||||||
property bool editing: false
|
|
||||||
|
|
||||||
property list<var> messageBlocks: StringUtils.splitMarkdownBlocks(root.messageData?.content)
|
|
||||||
|
|
||||||
anchors.left: parent?.left
|
|
||||||
anchors.right: parent?.right
|
|
||||||
implicitHeight: columnLayout.implicitHeight + root.messagePadding * 2
|
|
||||||
|
|
||||||
radius: Appearance.rounding.normal
|
|
||||||
color: Appearance.colors.colLayer1
|
|
||||||
|
|
||||||
function saveMessage() {
|
|
||||||
if (!root.editing) return;
|
|
||||||
// Get all Loader children (each represents a segment)
|
|
||||||
const segments = messageContentColumnLayout.children
|
|
||||||
.map(child => child.segment)
|
|
||||||
.filter(segment => (segment));
|
|
||||||
|
|
||||||
// Reconstruct markdown
|
|
||||||
const newContent = segments.map(segment => {
|
|
||||||
if (segment.type === "code") {
|
|
||||||
const lang = segment.lang ? segment.lang : "";
|
|
||||||
// Remove trailing newlines
|
|
||||||
const code = segment.content.replace(/\n+$/, "");
|
|
||||||
return "```" + lang + "\n" + code + "\n```";
|
|
||||||
} else {
|
|
||||||
return segment.content;
|
|
||||||
}
|
|
||||||
}).join("");
|
|
||||||
|
|
||||||
root.editing = false
|
|
||||||
root.messageData.content = newContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
|
||||||
if ( // Prevent de-select
|
|
||||||
event.key === Qt.Key_Control ||
|
|
||||||
event.key == Qt.Key_Shift ||
|
|
||||||
event.key == Qt.Key_Alt ||
|
|
||||||
event.key == Qt.Key_Meta
|
|
||||||
) {
|
|
||||||
event.accepted = true
|
|
||||||
}
|
|
||||||
// Ctrl + S to save
|
|
||||||
if ((event.key === Qt.Key_S) && event.modifiers == Qt.ControlModifier) {
|
|
||||||
root.saveMessage();
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout { // Main layout of the whole thing
|
|
||||||
id: columnLayout
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.margins: messagePadding
|
|
||||||
spacing: root.contentSpacing
|
|
||||||
|
|
||||||
RowLayout { // Header
|
|
||||||
spacing: 15
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Rectangle { // Name
|
|
||||||
id: nameWrapper
|
|
||||||
color: Appearance.colors.colSecondaryContainer
|
|
||||||
// color: "transparent"
|
|
||||||
radius: Appearance.rounding.small
|
|
||||||
implicitHeight: Math.max(nameRowLayout.implicitHeight + 5 * 2, 30)
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: nameRowLayout
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.leftMargin: 10
|
|
||||||
anchors.rightMargin: 10
|
|
||||||
spacing: 7
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
Layout.fillHeight: true
|
|
||||||
implicitWidth: messageData?.role == 'assistant' ? modelIcon.width : roleIcon.implicitWidth
|
|
||||||
implicitHeight: messageData?.role == 'assistant' ? modelIcon.height : roleIcon.implicitHeight
|
|
||||||
|
|
||||||
CustomIcon {
|
|
||||||
id: modelIcon
|
|
||||||
anchors.centerIn: parent
|
|
||||||
visible: messageData?.role == 'assistant' && Ai.models[messageData?.model].icon
|
|
||||||
width: Appearance.font.pixelSize.large
|
|
||||||
height: Appearance.font.pixelSize.large
|
|
||||||
source: messageData?.role == 'assistant' ? Ai.models[messageData?.model].icon :
|
|
||||||
messageData?.role == 'user' ? 'linux-symbolic' : 'desktop-symbolic'
|
|
||||||
|
|
||||||
colorize: true
|
|
||||||
color: Appearance.m3colors.m3onSecondaryContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
id: roleIcon
|
|
||||||
anchors.centerIn: parent
|
|
||||||
visible: !modelIcon.visible
|
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
|
||||||
color: Appearance.m3colors.m3onSecondaryContainer
|
|
||||||
text: messageData?.role == 'user' ? 'person' :
|
|
||||||
messageData?.role == 'interface' ? 'settings' :
|
|
||||||
messageData?.role == 'assistant' ? 'neurology' :
|
|
||||||
'computer'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: providerName
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
elide: Text.ElideRight
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
color: Appearance.m3colors.m3onSecondaryContainer
|
|
||||||
text: messageData?.role == 'assistant' ? Ai.models[messageData?.model].name :
|
|
||||||
(messageData?.role == 'user' && SystemInfo.username) ? SystemInfo.username :
|
|
||||||
Translation.tr("Interface")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button { // Not visible to model
|
|
||||||
id: modelVisibilityIndicator
|
|
||||||
visible: messageData?.role == 'interface'
|
|
||||||
implicitWidth: 16
|
|
||||||
implicitHeight: 30
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
|
|
||||||
background: Item
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
id: notVisibleToModelText
|
|
||||||
anchors.centerIn: parent
|
|
||||||
iconSize: Appearance.font.pixelSize.small
|
|
||||||
color: Appearance.colors.colSubtext
|
|
||||||
text: "visibility_off"
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Not visible to model")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
AiMessageControlButton {
|
|
||||||
id: copyButton
|
|
||||||
buttonIcon: activated ? "inventory" : "content_copy"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
Quickshell.clipboardText = root.messageData?.content
|
|
||||||
copyButton.activated = true
|
|
||||||
copyIconTimer.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: copyIconTimer
|
|
||||||
interval: 1500
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
copyButton.activated = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Copy")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AiMessageControlButton {
|
|
||||||
id: editButton
|
|
||||||
activated: root.editing
|
|
||||||
enabled: root.messageData?.done ?? false
|
|
||||||
buttonIcon: "edit"
|
|
||||||
onClicked: {
|
|
||||||
root.editing = !root.editing
|
|
||||||
if (!root.editing) { // Save changes
|
|
||||||
root.saveMessage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: root.editing ? Translation.tr("Save") : Translation.tr("Edit")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AiMessageControlButton {
|
|
||||||
id: toggleMarkdownButton
|
|
||||||
activated: !root.renderMarkdown
|
|
||||||
buttonIcon: "code"
|
|
||||||
onClicked: {
|
|
||||||
root.renderMarkdown = !root.renderMarkdown
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("View Markdown source")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AiMessageControlButton {
|
|
||||||
id: deleteButton
|
|
||||||
buttonIcon: "close"
|
|
||||||
onClicked: {
|
|
||||||
Ai.removeMessage(root.messageIndex)
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Delete")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout { // Message content
|
|
||||||
id: messageContentColumnLayout
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
Repeater {
|
|
||||||
model: root.messageBlocks.length
|
|
||||||
delegate: Loader {
|
|
||||||
required property int index
|
|
||||||
property var thisBlock: root.messageBlocks[index]
|
|
||||||
Layout.fillWidth: true
|
|
||||||
// property var segment: thisBlock
|
|
||||||
property var segmentContent: thisBlock.content
|
|
||||||
property var segmentLang: thisBlock.lang
|
|
||||||
property var messageData: root.messageData
|
|
||||||
property var editing: root.editing
|
|
||||||
property var renderMarkdown: root.renderMarkdown
|
|
||||||
property var enableMouseSelection: root.enableMouseSelection
|
|
||||||
property bool thinking: root.messageData?.thinking ?? true
|
|
||||||
property bool done: root.messageData?.done ?? false
|
|
||||||
property bool completed: thisBlock.completed ?? false
|
|
||||||
|
|
||||||
source: thisBlock.type === "code" ? "MessageCodeBlock.qml" :
|
|
||||||
thisBlock.type === "think" ? "MessageThinkBlock.qml" :
|
|
||||||
"MessageTextBlock.qml"
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Flow { // Annotations
|
|
||||||
visible: root.messageData?.annotationSources?.length > 0
|
|
||||||
spacing: 5
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: ScriptModel {
|
|
||||||
values: root.messageData?.annotationSources || []
|
|
||||||
}
|
|
||||||
delegate: AnnotationSourceButton {
|
|
||||||
required property var modelData
|
|
||||||
displayText: modelData.text
|
|
||||||
url: modelData.url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Flow { // Search queries
|
|
||||||
visible: root.messageData?.searchQueries?.length > 0
|
|
||||||
spacing: 5
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: ScriptModel {
|
|
||||||
values: root.messageData?.searchQueries || []
|
|
||||||
}
|
|
||||||
delegate: SearchQueryButton {
|
|
||||||
required property var modelData
|
|
||||||
query: modelData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
// These are needed on the parent loader
|
|
||||||
property bool editing: parent?.editing ?? false
|
|
||||||
property bool renderMarkdown: parent?.renderMarkdown ?? true
|
|
||||||
property bool enableMouseSelection: parent?.enableMouseSelection ?? false
|
|
||||||
property string segmentContent: parent?.segmentContent ?? ({})
|
|
||||||
property var messageData: parent?.messageData ?? {}
|
|
||||||
property bool done: parent?.done ?? true
|
|
||||||
property list<string> renderedLatexHashes: []
|
|
||||||
|
|
||||||
property string renderedSegmentContent: ""
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: renderTimer
|
|
||||||
interval: 1000
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
renderLatex()
|
|
||||||
for (const hash of renderedLatexHashes) {
|
|
||||||
handleRenderedLatex(hash, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderLatex() {
|
|
||||||
// Regex for $...$, $$...$$, \[...\]
|
|
||||||
// Note: This is a simple approach and may need refinement for edge cases
|
|
||||||
let regex = /(\$\$([\s\S]+?)\$\$)|(\$([^\$]+?)\$)|(\\\[((?:.|\n)+?)\\\])|(\\\(([\s\S]+?)\\\))/g;
|
|
||||||
let match;
|
|
||||||
while ((match = regex.exec(segmentContent)) !== null) {
|
|
||||||
let expression = match[1] || match[2] || match[3] || match[4] || match[5] || match[6] || match[7] || match[8];
|
|
||||||
if (expression) {
|
|
||||||
Qt.callLater(() => {
|
|
||||||
const [renderHash, isNew] = LatexRenderer.requestRender(expression.trim());
|
|
||||||
if (!renderedLatexHashes.includes(renderHash)) {
|
|
||||||
renderedLatexHashes.push(renderHash);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleRenderedLatex(hash, force = false) {
|
|
||||||
if (renderedLatexHashes.includes(hash) || force) {
|
|
||||||
const imagePath = LatexRenderer.renderedImagePaths[hash];
|
|
||||||
const markdownImage = ``;
|
|
||||||
|
|
||||||
const expression = LatexRenderer.processedExpressions[hash];
|
|
||||||
renderedSegmentContent = renderedSegmentContent.replace(expression, markdownImage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onDoneChanged: {
|
|
||||||
renderTimer.restart();
|
|
||||||
}
|
|
||||||
onEditingChanged: {
|
|
||||||
if (!editing) {
|
|
||||||
renderLatex()
|
|
||||||
} else {
|
|
||||||
// console.log("Editing mode enabled", segmentContent)
|
|
||||||
textArea.text = segmentContent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onSegmentContentChanged: {
|
|
||||||
// console.log("Segment content changed: " + segmentContent);
|
|
||||||
renderedSegmentContent = segmentContent;
|
|
||||||
if (!root.editing && segmentContent) {
|
|
||||||
root.renderLatex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRenderedSegmentContentChanged: {
|
|
||||||
// console.log("Rendered segment content changed: " + renderedSegmentContent);
|
|
||||||
if (renderedSegmentContent) {
|
|
||||||
textArea.text = renderedSegmentContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When something finishes rendering
|
|
||||||
// 1. Check if the hash is in the list
|
|
||||||
// 2. If it is, replace the expression with the image path
|
|
||||||
Connections {
|
|
||||||
target: LatexRenderer
|
|
||||||
function onRenderFinished(hash, imagePath) {
|
|
||||||
const expression = LatexRenderer.processedExpressions[hash];
|
|
||||||
// console.log("Render finished: " + hash + " " + expression);
|
|
||||||
handleRenderedLatex(hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextArea {
|
|
||||||
id: textArea
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
readOnly: !editing
|
|
||||||
selectByMouse: enableMouseSelection || editing
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
font.family: Appearance.font.family.reading
|
|
||||||
font.hintingPreference: Font.PreferNoHinting // Prevent weird bold text
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.small
|
|
||||||
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
|
||||||
selectionColor: Appearance.colors.colSecondaryContainer
|
|
||||||
wrapMode: TextEdit.Wrap
|
|
||||||
color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
|
|
||||||
textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText
|
|
||||||
text: Translation.tr("Waiting for response...")
|
|
||||||
|
|
||||||
onTextChanged: {
|
|
||||||
if (!root.editing) return
|
|
||||||
segmentContent = text
|
|
||||||
}
|
|
||||||
|
|
||||||
onLinkActivated: (link) => {
|
|
||||||
Qt.openUrlExternally(link)
|
|
||||||
GlobalStates.sidebarLeftOpen = false
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea { // Pointing hand for links
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.NoButton // Only for hover
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor :
|
|
||||||
(enableMouseSelection || editing) ? Qt.IBeamCursor : Qt.ArrowCursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.services
|
|
||||||
import "./notifications"
|
|
||||||
import "./volumeMixer"
|
|
||||||
import qs
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
radius: Appearance.rounding.normal
|
|
||||||
color: Appearance.colors.colLayer1
|
|
||||||
|
|
||||||
property int selectedTab: 0
|
|
||||||
property var tabButtonList: [{"icon": "notifications", "name": Translation.tr("Notifications")}, {"icon": "volume_up", "name": Translation.tr("Volume mixer")}]
|
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
|
||||||
if (event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) {
|
|
||||||
if (event.key === Qt.Key_PageDown) {
|
|
||||||
root.selectedTab = Math.min(root.selectedTab + 1, root.tabButtonList.length - 1)
|
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
|
||||||
root.selectedTab = Math.max(root.selectedTab - 1, 0)
|
|
||||||
}
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
if (event.modifiers === Qt.ControlModifier) {
|
|
||||||
if (event.key === Qt.Key_Tab) {
|
|
||||||
root.selectedTab = (root.selectedTab + 1) % root.tabButtonList.length
|
|
||||||
} else if (event.key === Qt.Key_Backtab) {
|
|
||||||
root.selectedTab = (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length
|
|
||||||
}
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.margins: 5
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
PrimaryTabBar {
|
|
||||||
id: tabBar
|
|
||||||
tabButtonList: root.tabButtonList
|
|
||||||
externalTrackedTab: root.selectedTab
|
|
||||||
|
|
||||||
function onCurrentIndexChanged(currentIndex) {
|
|
||||||
root.selectedTab = currentIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SwipeView {
|
|
||||||
id: swipeView
|
|
||||||
Layout.topMargin: 5
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
spacing: 10
|
|
||||||
currentIndex: root.selectedTab
|
|
||||||
onCurrentIndexChanged: {
|
|
||||||
tabBar.enableIndicatorAnimation = true
|
|
||||||
root.selectedTab = currentIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: OpacityMask {
|
|
||||||
maskSource: Rectangle {
|
|
||||||
width: swipeView.width
|
|
||||||
height: swipeView.height
|
|
||||||
radius: Appearance.rounding.small
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationList {}
|
|
||||||
VolumeMixer {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import "./quickToggles/"
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
|
||||||
Scope {
|
|
||||||
id: root
|
|
||||||
property int sidebarWidth: Appearance.sizes.sidebarWidth
|
|
||||||
property int sidebarPadding: 12
|
|
||||||
property string settingsQmlPath: Quickshell.shellPath("settings.qml")
|
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: sidebarRoot
|
|
||||||
visible: GlobalStates.sidebarRightOpen
|
|
||||||
|
|
||||||
function hide() {
|
|
||||||
GlobalStates.sidebarRightOpen = false
|
|
||||||
}
|
|
||||||
|
|
||||||
exclusiveZone: 0
|
|
||||||
implicitWidth: sidebarWidth
|
|
||||||
WlrLayershell.namespace: "quickshell:sidebarRight"
|
|
||||||
// Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
|
|
||||||
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
right: true
|
|
||||||
bottom: true
|
|
||||||
}
|
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
id: grab
|
|
||||||
windows: [ sidebarRoot ]
|
|
||||||
active: GlobalStates.sidebarRightOpen
|
|
||||||
onCleared: () => {
|
|
||||||
if (!active) sidebarRoot.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: sidebarContentLoader
|
|
||||||
active: GlobalStates.sidebarRightOpen || Config?.options.sidebar.keepRightSidebarLoaded
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
margins: Appearance.sizes.hyprlandGapsOut
|
|
||||||
leftMargin: Appearance.sizes.elevationMargin
|
|
||||||
}
|
|
||||||
width: sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
|
|
||||||
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
|
||||||
|
|
||||||
focus: GlobalStates.sidebarRightOpen
|
|
||||||
Keys.onPressed: (event) => {
|
|
||||||
if (event.key === Qt.Key_Escape) {
|
|
||||||
sidebarRoot.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceComponent: Item {
|
|
||||||
implicitHeight: sidebarRightBackground.implicitHeight
|
|
||||||
implicitWidth: sidebarRightBackground.implicitWidth
|
|
||||||
|
|
||||||
StyledRectangularShadow {
|
|
||||||
target: sidebarRightBackground
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
id: sidebarRightBackground
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
implicitHeight: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
|
||||||
implicitWidth: sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2
|
|
||||||
color: Appearance.colors.colLayer0
|
|
||||||
border.width: 1
|
|
||||||
border.color: Appearance.colors.colLayer0Border
|
|
||||||
radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: sidebarPadding
|
|
||||||
spacing: sidebarPadding
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillHeight: false
|
|
||||||
spacing: 10
|
|
||||||
Layout.margins: 10
|
|
||||||
Layout.topMargin: 5
|
|
||||||
Layout.bottomMargin: 0
|
|
||||||
|
|
||||||
CustomIcon {
|
|
||||||
id: distroIcon
|
|
||||||
width: 25
|
|
||||||
height: 25
|
|
||||||
source: SystemInfo.distroIcon
|
|
||||||
colorize: true
|
|
||||||
color: Appearance.colors.colOnLayer0
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
color: Appearance.colors.colOnLayer0
|
|
||||||
text: Translation.tr("Up %1").arg(DateTime.uptime)
|
|
||||||
textFormat: Text.MarkdownText
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
|
||||||
QuickToggleButton {
|
|
||||||
toggled: false
|
|
||||||
buttonIcon: "restart_alt"
|
|
||||||
onClicked: {
|
|
||||||
Hyprland.dispatch("reload")
|
|
||||||
Quickshell.reload(true)
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Reload Hyprland & Quickshell")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QuickToggleButton {
|
|
||||||
toggled: false
|
|
||||||
buttonIcon: "settings"
|
|
||||||
onClicked: {
|
|
||||||
GlobalStates.sidebarRightOpen = false
|
|
||||||
Quickshell.execDetached(["qs", "-p", root.settingsQmlPath])
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Settings")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QuickToggleButton {
|
|
||||||
toggled: false
|
|
||||||
buttonIcon: "power_settings_new"
|
|
||||||
onClicked: {
|
|
||||||
GlobalStates.sessionOpen = true
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Session")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
spacing: 5
|
|
||||||
padding: 5
|
|
||||||
color: Appearance.colors.colLayer1
|
|
||||||
|
|
||||||
NetworkToggle {}
|
|
||||||
BluetoothToggle {}
|
|
||||||
NightLight {}
|
|
||||||
GameMode {}
|
|
||||||
IdleInhibitor {}
|
|
||||||
EasyEffectsToggle {}
|
|
||||||
CloudflareWarp {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Center widget group
|
|
||||||
CenterWidgetGroup {
|
|
||||||
focus: sidebarRoot.visible
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
BottomWidgetGroup {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillHeight: false
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: implicitHeight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHandler {
|
|
||||||
target: "sidebarRight"
|
|
||||||
|
|
||||||
function toggle(): void {
|
|
||||||
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
|
|
||||||
if(GlobalStates.sidebarRightOpen) Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
function close(): void {
|
|
||||||
GlobalStates.sidebarRightOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function open(): void {
|
|
||||||
GlobalStates.sidebarRightOpen = true;
|
|
||||||
Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "sidebarRightToggle"
|
|
||||||
description: "Toggles right sidebar on press"
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
|
|
||||||
if(GlobalStates.sidebarRightOpen) Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "sidebarRightOpen"
|
|
||||||
description: "Opens right sidebar on press"
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
GlobalStates.sidebarRightOpen = true;
|
|
||||||
Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "sidebarRightClose"
|
|
||||||
description: "Closes right sidebar on press"
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
GlobalStates.sidebarRightOpen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.services
|
|
||||||
import qs
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
NotificationListView { // Scrollable window
|
|
||||||
id: listview
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: statusRow.top
|
|
||||||
anchors.bottomMargin: 5
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: OpacityMask {
|
|
||||||
maskSource: Rectangle {
|
|
||||||
width: listview.width
|
|
||||||
height: listview.height
|
|
||||||
radius: Appearance.rounding.normal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
popup: false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Placeholder when list is empty
|
|
||||||
Item {
|
|
||||||
anchors.fill: listview
|
|
||||||
|
|
||||||
visible: opacity > 0
|
|
||||||
opacity: (Notifications.list.length === 0) ? 1 : 0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.menuDecel.duration
|
|
||||||
easing.type: Appearance.animation.menuDecel.type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
iconSize: 55
|
|
||||||
color: Appearance.m3colors.m3outline
|
|
||||||
text: "notifications_active"
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
color: Appearance.m3colors.m3outline
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: Translation.tr("No notifications")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: statusRow
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
implicitHeight: Math.max(
|
|
||||||
controls.implicitHeight,
|
|
||||||
statusText.implicitHeight
|
|
||||||
)
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: statusText
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.leftMargin: 10
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: Translation.tr("%1 notifications").arg(Notifications.list.length)
|
|
||||||
|
|
||||||
opacity: Notifications.list.length > 0 ? 1 : 0
|
|
||||||
visible: opacity > 0
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
|
||||||
id: controls
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.rightMargin: 5
|
|
||||||
|
|
||||||
NotificationStatusButton {
|
|
||||||
buttonIcon: "notifications_paused"
|
|
||||||
buttonText: Translation.tr("Silent")
|
|
||||||
toggled: Notifications.silent
|
|
||||||
onClicked: () => {
|
|
||||||
Notifications.silent = !Notifications.silent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NotificationStatusButton {
|
|
||||||
buttonIcon: "clear_all"
|
|
||||||
buttonText: Translation.tr("Clear")
|
|
||||||
onClicked: () => {
|
|
||||||
Notifications.discardAllNotifications()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
|
||||||
QuickToggleButton {
|
|
||||||
toggled: Bluetooth.bluetoothEnabled
|
|
||||||
buttonIcon: Bluetooth.bluetoothConnected ? "bluetooth_connected" : Bluetooth.bluetoothEnabled ? "bluetooth" : "bluetooth_disabled"
|
|
||||||
onClicked: {
|
|
||||||
toggleBluetooth.running = true
|
|
||||||
}
|
|
||||||
altAction: () => {
|
|
||||||
Quickshell.execDetached(["bash", "-c", `${Config.options.apps.bluetooth}`])
|
|
||||||
GlobalStates.sidebarRightOpen = false
|
|
||||||
}
|
|
||||||
Process {
|
|
||||||
id: toggleBluetooth
|
|
||||||
command: ["bash", "-c", `bluetoothctl power ${Bluetooth.bluetoothEnabled ? "off" : "on"}`]
|
|
||||||
onRunningChanged: {
|
|
||||||
if(!running) {
|
|
||||||
Bluetooth.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("%1 | Right-click to configure").arg(
|
|
||||||
(Bluetooth.bluetoothEnabled && Bluetooth.bluetoothDeviceName.length > 0) ?
|
|
||||||
Bluetooth.bluetoothDeviceName : Translation.tr("Bluetooth"))
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import qs.modules.common.widgets
|
|
||||||
import qs
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
|
||||||
QuickToggleButton {
|
|
||||||
id: root
|
|
||||||
toggled: false
|
|
||||||
visible: false
|
|
||||||
buttonIcon: "instant_mix"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (toggled) {
|
|
||||||
root.toggled = false
|
|
||||||
Quickshell.execDetached(["pkill", "easyeffects"])
|
|
||||||
} else {
|
|
||||||
root.toggled = true
|
|
||||||
Quickshell.execDetached(["easyeffects", "--gapplication-service"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
altAction: () => {
|
|
||||||
Quickshell.execDetached(["easyeffects"])
|
|
||||||
GlobalStates.sidebarRightOpen = false
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: fetchAvailability
|
|
||||||
running: true
|
|
||||||
command: ["bash", "-c", "command -v easyeffects"]
|
|
||||||
onExited: (exitCode, exitStatus) => {
|
|
||||||
root.visible = exitCode === 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: fetchActiveState
|
|
||||||
running: true
|
|
||||||
command: ["pidof", "easyeffects"]
|
|
||||||
onExited: (exitCode, exitStatus) => {
|
|
||||||
root.toggled = exitCode === 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("EasyEffects | Right-click to configure")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
QuickToggleButton {
|
|
||||||
id: root
|
|
||||||
toggled: false
|
|
||||||
buttonIcon: "coffee"
|
|
||||||
onClicked: {
|
|
||||||
if (toggled) {
|
|
||||||
root.toggled = false
|
|
||||||
Quickshell.execDetached(["pkill", "wayland-idle"]) // pkill doesn't accept too long names
|
|
||||||
} else {
|
|
||||||
root.toggled = true
|
|
||||||
Quickshell.execDetached([`${Directories.scriptPath}/wayland-idle-inhibitor.py`])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Process {
|
|
||||||
id: fetchActiveState
|
|
||||||
running: true
|
|
||||||
command: ["pidof", "wayland-idle-inhibitor.py"]
|
|
||||||
onExited: (exitCode, exitStatus) => {
|
|
||||||
root.toggled = exitCode === 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("Keep system awake")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import "../"
|
|
||||||
import qs
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
|
||||||
QuickToggleButton {
|
|
||||||
toggled: Network.networkName.length > 0 && Network.networkName != "lo"
|
|
||||||
buttonIcon: Network.materialSymbol
|
|
||||||
onClicked: {
|
|
||||||
toggleNetwork.running = true
|
|
||||||
}
|
|
||||||
altAction: () => {
|
|
||||||
Quickshell.execDetached(["bash", "-c", `${Network.ethernet ? Config.options.apps.networkEthernet : Config.options.apps.network}`])
|
|
||||||
GlobalStates.sidebarRightOpen = false
|
|
||||||
}
|
|
||||||
Process {
|
|
||||||
id: toggleNetwork
|
|
||||||
command: ["bash", "-c", "nmcli radio wifi | grep -q enabled && nmcli radio wifi off || nmcli radio wifi on"]
|
|
||||||
onRunningChanged: {
|
|
||||||
if(!running) {
|
|
||||||
Network.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: Translation.tr("%1 | Right-click to configure").arg(Network.networkName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.services
|
|
||||||
import qs
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Services.Pipewire
|
|
||||||
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
property bool showDeviceSelector: false
|
|
||||||
property bool deviceSelectorInput
|
|
||||||
property int dialogMargins: 16
|
|
||||||
property PwNode selectedDevice
|
|
||||||
readonly property list<PwNode> appPwNodes: Pipewire.nodes.values.filter((node) => {
|
|
||||||
// return node.type == "21" // Alternative, not as clean
|
|
||||||
return node.isSink && node.isStream
|
|
||||||
})
|
|
||||||
|
|
||||||
function showDeviceSelectorDialog(input: bool) {
|
|
||||||
root.selectedDevice = null
|
|
||||||
root.showDeviceSelector = true
|
|
||||||
root.deviceSelectorInput = input
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
|
||||||
// Close dialog on pressing Esc if open
|
|
||||||
if (event.key === Qt.Key_Escape && root.showDeviceSelector) {
|
|
||||||
root.showDeviceSelector = false
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
StyledListView {
|
|
||||||
id: listView
|
|
||||||
model: root.appPwNodes
|
|
||||||
clip: true
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
topMargin: 10
|
|
||||||
bottomMargin: 10
|
|
||||||
}
|
|
||||||
spacing: 6
|
|
||||||
|
|
||||||
delegate: VolumeMixerEntry {
|
|
||||||
// Layout.fillWidth: true
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
leftMargin: 10
|
|
||||||
rightMargin: 10
|
|
||||||
}
|
|
||||||
required property var modelData
|
|
||||||
node: modelData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Placeholder when list is empty
|
|
||||||
Item {
|
|
||||||
anchors.fill: listView
|
|
||||||
|
|
||||||
visible: opacity > 0
|
|
||||||
opacity: (root.appPwNodes.length === 0) ? 1 : 0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.menuDecel.duration
|
|
||||||
easing.type: Appearance.animation.menuDecel.type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
iconSize: 55
|
|
||||||
color: Appearance.m3colors.m3outline
|
|
||||||
text: "brand_awareness"
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
color: Appearance.m3colors.m3outline
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: Translation.tr("No audio source")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Separator
|
|
||||||
Rectangle {
|
|
||||||
color: Appearance.m3colors.m3outlineVariant
|
|
||||||
implicitHeight: 1
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Device selector
|
|
||||||
ButtonGroup {
|
|
||||||
id: deviceSelectorRowLayout
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: false
|
|
||||||
AudioDeviceSelectorButton {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
input: false
|
|
||||||
onClicked: root.showDeviceSelectorDialog(input)
|
|
||||||
}
|
|
||||||
AudioDeviceSelectorButton {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
input: true
|
|
||||||
onClicked: root.showDeviceSelectorDialog(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device selector dialog
|
|
||||||
Item {
|
|
||||||
anchors.fill: parent
|
|
||||||
z: 9999
|
|
||||||
|
|
||||||
visible: opacity > 0
|
|
||||||
opacity: root.showDeviceSelector ? 1 : 0
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveFast.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle { // Scrim
|
|
||||||
id: scrimOverlay
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: Appearance.rounding.small
|
|
||||||
color: Appearance.colors.colScrim
|
|
||||||
MouseArea {
|
|
||||||
hoverEnabled: true
|
|
||||||
anchors.fill: parent
|
|
||||||
preventStealing: true
|
|
||||||
propagateComposedEvents: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle { // The dialog
|
|
||||||
id: dialog
|
|
||||||
color: Appearance.colors.colSurfaceContainerHigh
|
|
||||||
radius: Appearance.rounding.normal
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.margins: 30
|
|
||||||
implicitHeight: dialogColumnLayout.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: dialogColumnLayout
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 16
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: dialogTitle
|
|
||||||
Layout.topMargin: dialogMargins
|
|
||||||
Layout.leftMargin: dialogMargins
|
|
||||||
Layout.rightMargin: dialogMargins
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
color: Appearance.m3colors.m3onSurface
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.larger
|
|
||||||
text: root.deviceSelectorInput ? Translation.tr("Select input device") : Translation.tr("Select output device")
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: Appearance.m3colors.m3outline
|
|
||||||
implicitHeight: 1
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: dialogMargins
|
|
||||||
Layout.rightMargin: dialogMargins
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledFlickable {
|
|
||||||
id: dialogFlickable
|
|
||||||
Layout.fillWidth: true
|
|
||||||
clip: true
|
|
||||||
implicitHeight: Math.min(scrimOverlay.height - dialogMargins * 8 - dialogTitle.height - dialogButtonsRowLayout.height, devicesColumnLayout.implicitHeight)
|
|
||||||
|
|
||||||
contentHeight: devicesColumnLayout.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: devicesColumnLayout
|
|
||||||
anchors.fill: parent
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: ScriptModel {
|
|
||||||
values: Pipewire.nodes.values.filter(node => {
|
|
||||||
return !node.isStream && node.isSink !== root.deviceSelectorInput && node.audio
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// This could and should be refractored, but all data becomes null when passed wtf
|
|
||||||
delegate: StyledRadioButton {
|
|
||||||
id: radioButton
|
|
||||||
required property var modelData
|
|
||||||
Layout.leftMargin: root.dialogMargins
|
|
||||||
Layout.rightMargin: root.dialogMargins
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
description: modelData.description
|
|
||||||
checked: modelData.id === Pipewire.defaultAudioSink?.id
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onShowDeviceSelectorChanged() {
|
|
||||||
if(!root.showDeviceSelector) return;
|
|
||||||
radioButton.checked = (modelData.id === Pipewire.defaultAudioSink?.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onCheckedChanged: {
|
|
||||||
if (checked) {
|
|
||||||
root.selectedDevice = modelData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
implicitHeight: dialogMargins
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: Appearance.m3colors.m3outline
|
|
||||||
implicitHeight: 1
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: dialogMargins
|
|
||||||
Layout.rightMargin: dialogMargins
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: dialogButtonsRowLayout
|
|
||||||
Layout.bottomMargin: dialogMargins
|
|
||||||
Layout.leftMargin: dialogMargins
|
|
||||||
Layout.rightMargin: dialogMargins
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
|
|
||||||
DialogButton {
|
|
||||||
buttonText: Translation.tr("Cancel")
|
|
||||||
onClicked: {
|
|
||||||
root.showDeviceSelector = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DialogButton {
|
|
||||||
buttonText: Translation.tr("OK")
|
|
||||||
onClicked: {
|
|
||||||
root.showDeviceSelector = false
|
|
||||||
if (root.selectedDevice) {
|
|
||||||
if (root.deviceSelectorInput) {
|
|
||||||
Pipewire.preferredDefaultAudioSource = root.selectedDevice
|
|
||||||
} else {
|
|
||||||
Pipewire.preferredDefaultAudioSink = root.selectedDevice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,553 +0,0 @@
|
|||||||
//@ pragma UseQApplication
|
|
||||||
//@ pragma Env QS_NO_RELOAD_POPUP=1
|
|
||||||
//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic
|
|
||||||
//@ pragma Env QT_QUICK_FLICKABLE_WHEEL_DECELERATION=10000
|
|
||||||
|
|
||||||
// Adjust this to make it smaller or larger
|
|
||||||
//@ pragma Env QT_SCALE_FACTOR=1
|
|
||||||
|
|
||||||
pragma ComponentBehavior: "Bound"
|
|
||||||
import qs
|
|
||||||
import qs.services
|
|
||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Widgets
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
|
||||||
ShellRoot {
|
|
||||||
id: root
|
|
||||||
property string screenshotDir: Directories.screenshotTemp
|
|
||||||
property color overlayColor: "#77111111"
|
|
||||||
property color genericContentColor: Qt.alpha(root.overlayColor, 0.9)
|
|
||||||
property color genericContentForeground: "#ddffffff"
|
|
||||||
property color selectionBorderColor: "#ddf1f1f1"
|
|
||||||
property color selectionFillColor: "#33ffffff"
|
|
||||||
property color windowBorderColor: "#dda0c0da"
|
|
||||||
property color windowFillColor: "#22a0c0da"
|
|
||||||
property color imageBorderColor: "#ddf1d1ff"
|
|
||||||
property color imageFillColor: "#33f1d1ff"
|
|
||||||
property color onBorderColor: "#ff000000"
|
|
||||||
property real standardRounding: 4
|
|
||||||
readonly property var windows: HyprlandData.windowList
|
|
||||||
readonly property var layers: HyprlandData.layers
|
|
||||||
readonly property real falsePositivePreventionRatio: 0.5
|
|
||||||
|
|
||||||
// Force initialization of some singletons
|
|
||||||
Component.onCompleted: {
|
|
||||||
MaterialThemeLoader.reapplyTheme();
|
|
||||||
}
|
|
||||||
|
|
||||||
component TargetRegion: Rectangle {
|
|
||||||
id: regionRect
|
|
||||||
property bool showIcon: false
|
|
||||||
property bool targeted: false
|
|
||||||
property color borderColor
|
|
||||||
property color fillColor: "transparent"
|
|
||||||
property string text: ""
|
|
||||||
property real textPadding: 10
|
|
||||||
z: 2
|
|
||||||
color: fillColor
|
|
||||||
border.color: borderColor
|
|
||||||
border.width: targeted ? 3 : 1
|
|
||||||
radius: root.standardRounding
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: regionLabelBackground
|
|
||||||
property real verticalPadding: 5
|
|
||||||
property real horizontalPadding: 10
|
|
||||||
radius: 10
|
|
||||||
color: root.genericContentColor
|
|
||||||
border.width: 1
|
|
||||||
border.color: Appearance.m3colors.m3outlineVariant
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
left: parent.left
|
|
||||||
topMargin: regionRect.textPadding
|
|
||||||
leftMargin: regionRect.textPadding
|
|
||||||
}
|
|
||||||
implicitWidth: regionInfoRow.implicitWidth + horizontalPadding * 2
|
|
||||||
implicitHeight: regionInfoRow.implicitHeight + verticalPadding * 2
|
|
||||||
RowLayout {
|
|
||||||
id: regionInfoRow
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: regionIconLoader
|
|
||||||
active: regionRect.showIcon
|
|
||||||
visible: active
|
|
||||||
sourceComponent: IconImage {
|
|
||||||
implicitSize: Appearance.font.pixelSize.larger
|
|
||||||
source: Quickshell.iconPath(AppSearch.guessIcon(regionRect.text), "image-missing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: regionText
|
|
||||||
text: regionRect.text
|
|
||||||
color: root.genericContentForeground
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Variants {
|
|
||||||
model: Quickshell.screens
|
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: panelWindow
|
|
||||||
required property var modelData
|
|
||||||
readonly property HyprlandMonitor hyprlandMonitor: Hyprland.monitorFor(modelData)
|
|
||||||
readonly property real monitorScale: hyprlandMonitor.scale
|
|
||||||
readonly property real monitorOffsetX: hyprlandMonitor.x
|
|
||||||
readonly property real monitorOffsetY: hyprlandMonitor.y
|
|
||||||
property int activeWorkspaceId: hyprlandMonitor.activeWorkspace?.id ?? 0
|
|
||||||
property string screenshotPath: `${root.screenshotDir}/image-${modelData.name}`
|
|
||||||
property real dragStartX: 0
|
|
||||||
property real dragStartY: 0
|
|
||||||
property real draggingX: 0
|
|
||||||
property real draggingY: 0
|
|
||||||
property real dragDiffX: 0
|
|
||||||
property real dragDiffY: 0
|
|
||||||
property bool draggedAway: (dragDiffX !== 0 || dragDiffY !== 0)
|
|
||||||
property bool dragging: false
|
|
||||||
property var mouseButton: null
|
|
||||||
property var imageRegions: []
|
|
||||||
readonly property list<var> windowRegions: filterWindowRegionsByLayers(
|
|
||||||
root.windows.filter(w => w.workspace.id === panelWindow.activeWorkspaceId),
|
|
||||||
panelWindow.layerRegions
|
|
||||||
).map(window => {
|
|
||||||
return {
|
|
||||||
at: [window.at[0] - panelWindow.monitorOffsetX, window.at[1] - panelWindow.monitorOffsetY],
|
|
||||||
size: [window.size[0], window.size[1]],
|
|
||||||
class: window.class,
|
|
||||||
title: window.title,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
readonly property list<var> layerRegions: {
|
|
||||||
const layersOfThisMonitor = root.layers[panelWindow.hyprlandMonitor.name]
|
|
||||||
const topLayers = layersOfThisMonitor.levels["2"]
|
|
||||||
const nonBarTopLayers = topLayers
|
|
||||||
.filter(layer => !(layer.namespace.includes(":bar") || layer.namespace.includes(":dock")))
|
|
||||||
.map(layer => {
|
|
||||||
return {
|
|
||||||
at: [layer.x, layer.y],
|
|
||||||
size: [layer.w, layer.h],
|
|
||||||
namespace: layer.namespace,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const offsetAdjustedLayers = nonBarTopLayers.map(layer => {
|
|
||||||
return {
|
|
||||||
at: [layer.at[0] - panelWindow.monitorOffsetX, layer.at[1] - panelWindow.monitorOffsetY],
|
|
||||||
size: layer.size,
|
|
||||||
namespace: layer.namespace,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return offsetAdjustedLayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
property real targetedRegionX: -1
|
|
||||||
property real targetedRegionY: -1
|
|
||||||
property real targetedRegionWidth: 0
|
|
||||||
property real targetedRegionHeight: 0
|
|
||||||
|
|
||||||
function intersectionOverUnion(regionA, regionB) {
|
|
||||||
// region: { at: [x, y], size: [w, h] }
|
|
||||||
const ax1 = regionA.at[0], ay1 = regionA.at[1];
|
|
||||||
const ax2 = ax1 + regionA.size[0], ay2 = ay1 + regionA.size[1];
|
|
||||||
const bx1 = regionB.at[0], by1 = regionB.at[1];
|
|
||||||
const bx2 = bx1 + regionB.size[0], by2 = by1 + regionB.size[1];
|
|
||||||
|
|
||||||
const interX1 = Math.max(ax1, bx1);
|
|
||||||
const interY1 = Math.max(ay1, by1);
|
|
||||||
const interX2 = Math.min(ax2, bx2);
|
|
||||||
const interY2 = Math.min(ay2, by2);
|
|
||||||
|
|
||||||
const interArea = Math.max(0, interX2 - interX1) * Math.max(0, interY2 - interY1);
|
|
||||||
const areaA = (ax2 - ax1) * (ay2 - ay1);
|
|
||||||
const areaB = (bx2 - bx1) * (by2 - by1);
|
|
||||||
const unionArea = areaA + areaB - interArea;
|
|
||||||
|
|
||||||
return unionArea > 0 ? interArea / unionArea : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterOverlappingImageRegions(regions) {
|
|
||||||
let keep = [];
|
|
||||||
let removed = new Set();
|
|
||||||
for (let i = 0; i < regions.length; ++i) {
|
|
||||||
if (removed.has(i)) continue;
|
|
||||||
let regionA = regions[i];
|
|
||||||
for (let j = i + 1; j < regions.length; ++j) {
|
|
||||||
if (removed.has(j)) continue;
|
|
||||||
let regionB = regions[j];
|
|
||||||
if (intersectionOverUnion(regionA, regionB) > 0) {
|
|
||||||
// Compare areas
|
|
||||||
let areaA = regionA.size[0] * regionA.size[1];
|
|
||||||
let areaB = regionB.size[0] * regionB.size[1];
|
|
||||||
if (areaA <= areaB) {
|
|
||||||
removed.add(j);
|
|
||||||
} else {
|
|
||||||
removed.add(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i < regions.length; ++i) {
|
|
||||||
if (!removed.has(i)) keep.push(regions[i]);
|
|
||||||
}
|
|
||||||
return keep;
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterWindowRegionsByLayers(windowRegions, layerRegions) {
|
|
||||||
return windowRegions.filter(windowRegion => {
|
|
||||||
for (let i = 0; i < layerRegions.length; ++i) {
|
|
||||||
if (intersectionOverUnion(windowRegion, layerRegions[i]) > 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterImageRegions(regions, windowRegions, threshold = 0.1) {
|
|
||||||
// Remove image regions that overlap too much with any window region
|
|
||||||
let filtered = regions.filter(region => {
|
|
||||||
for (let i = 0; i < windowRegions.length; ++i) {
|
|
||||||
if (intersectionOverUnion(region, windowRegions[i]) > threshold)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
// Remove overlapping image regions, keep only the smaller one
|
|
||||||
return filterOverlappingImageRegions(filtered);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateTargetedRegion(x, y) {
|
|
||||||
// Image regions
|
|
||||||
const clickedRegion = panelWindow.imageRegions.find(region => {
|
|
||||||
return region.at[0] <= x && x <= region.at[0] + region.size[0] && region.at[1] <= y && y <= region.at[1] + region.size[1];
|
|
||||||
});
|
|
||||||
if (clickedRegion) {
|
|
||||||
panelWindow.targetedRegionX = clickedRegion.at[0];
|
|
||||||
panelWindow.targetedRegionY = clickedRegion.at[1];
|
|
||||||
panelWindow.targetedRegionWidth = clickedRegion.size[0];
|
|
||||||
panelWindow.targetedRegionHeight = clickedRegion.size[1];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Layer regions
|
|
||||||
const clickedLayer = panelWindow.layerRegions.find(region => {
|
|
||||||
return region.at[0] <= x && x <= region.at[0] + region.size[0] && region.at[1] <= y && y <= region.at[1] + region.size[1];
|
|
||||||
});
|
|
||||||
if (clickedLayer) {
|
|
||||||
panelWindow.targetedRegionX = clickedLayer.at[0];
|
|
||||||
panelWindow.targetedRegionY = clickedLayer.at[1];
|
|
||||||
panelWindow.targetedRegionWidth = clickedLayer.size[0];
|
|
||||||
panelWindow.targetedRegionHeight = clickedLayer.size[1];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Window regions
|
|
||||||
const clickedWindow = panelWindow.windowRegions.find(region => {
|
|
||||||
return region.at[0] <= x && x <= region.at[0] + region.size[0] && region.at[1] <= y && y <= region.at[1] + region.size[1];
|
|
||||||
});
|
|
||||||
if (clickedWindow) {
|
|
||||||
panelWindow.targetedRegionX = clickedWindow.at[0];
|
|
||||||
panelWindow.targetedRegionY = clickedWindow.at[1];
|
|
||||||
panelWindow.targetedRegionWidth = clickedWindow.size[0];
|
|
||||||
panelWindow.targetedRegionHeight = clickedWindow.size[1];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
panelWindow.targetedRegionX = -1;
|
|
||||||
panelWindow.targetedRegionY = -1;
|
|
||||||
panelWindow.targetedRegionWidth = 0;
|
|
||||||
panelWindow.targetedRegionHeight = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
property real regionWidth: Math.abs(draggingX - dragStartX)
|
|
||||||
property real regionHeight: Math.abs(draggingY - dragStartY)
|
|
||||||
property real regionX: Math.min(dragStartX, draggingX)
|
|
||||||
property real regionY: Math.min(dragStartY, draggingY)
|
|
||||||
|
|
||||||
visible: false
|
|
||||||
screen: modelData
|
|
||||||
WlrLayershell.namespace: "quickshell:screenshot"
|
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
|
||||||
exclusionMode: ExclusionMode.Ignore
|
|
||||||
anchors {
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
top: true
|
|
||||||
bottom: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: screenshotProcess
|
|
||||||
running: true
|
|
||||||
command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(modelData.name)}' '${StringUtils.shellSingleQuoteEscape(panelWindow.screenshotPath)}'`]
|
|
||||||
onExited: (exitCode, exitStatus) => {
|
|
||||||
panelWindow.visible = true;
|
|
||||||
imageDetectionProcess.running = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: imageDetectionProcess
|
|
||||||
command: ["bash", "-c", `${Directories.scriptPath}/images/find_regions.py `
|
|
||||||
+ `--hyprctl `
|
|
||||||
+ `--image '${StringUtils.shellSingleQuoteEscape(panelWindow.screenshotPath)}' `
|
|
||||||
+ `--max-width ${Math.round(panelWindow.screen.width * root.falsePositivePreventionRatio)} `
|
|
||||||
+ `--max-height ${Math.round(panelWindow.screen.height * root.falsePositivePreventionRatio)} `]
|
|
||||||
stdout: StdioCollector {
|
|
||||||
id: imageDimensionCollector
|
|
||||||
onStreamFinished: {
|
|
||||||
imageRegions = filterImageRegions(
|
|
||||||
JSON.parse(imageDimensionCollector.text),
|
|
||||||
panelWindow.windowRegions
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: snipProc
|
|
||||||
function snip() {
|
|
||||||
if (panelWindow.regionWidth <= 0 || panelWindow.regionHeight <= 0) {
|
|
||||||
console.warn("Invalid region size, skipping snip.");
|
|
||||||
Qt.quit();
|
|
||||||
}
|
|
||||||
snipProc.startDetached();
|
|
||||||
Qt.quit();
|
|
||||||
}
|
|
||||||
command: ["bash", "-c",
|
|
||||||
`magick ${StringUtils.shellSingleQuoteEscape(panelWindow.screenshotPath)} `
|
|
||||||
+ `-crop ${panelWindow.regionWidth * panelWindow.monitorScale}x${panelWindow.regionHeight * panelWindow.monitorScale}+${panelWindow.regionX * panelWindow.monitorScale}+${panelWindow.regionY * panelWindow.monitorScale} - `
|
|
||||||
+ `| ${panelWindow.mouseButton === Qt.LeftButton ? "wl-copy" : "swappy -f -"}`]
|
|
||||||
}
|
|
||||||
|
|
||||||
ScreencopyView {
|
|
||||||
anchors.fill: parent
|
|
||||||
live: false
|
|
||||||
captureSource: modelData
|
|
||||||
|
|
||||||
focus: panelWindow.visible
|
|
||||||
Keys.onPressed: (event) => { // Esc to close
|
|
||||||
if (event.key === Qt.Key_Escape) {
|
|
||||||
Qt.quit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.CrossCursor
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
// Controls
|
|
||||||
onPressed: mouse => {
|
|
||||||
panelWindow.dragStartX = mouse.x;
|
|
||||||
panelWindow.dragStartY = mouse.y;
|
|
||||||
panelWindow.draggingX = mouse.x;
|
|
||||||
panelWindow.draggingY = mouse.y;
|
|
||||||
panelWindow.dragging = true;
|
|
||||||
panelWindow.mouseButton = mouse.button;
|
|
||||||
}
|
|
||||||
onReleased: mouse => {
|
|
||||||
// Detect if it was a click
|
|
||||||
|
|
||||||
// Image regions
|
|
||||||
if (panelWindow.draggingX === panelWindow.dragStartX && panelWindow.draggingY === panelWindow.dragStartY) {
|
|
||||||
if (panelWindow.targetedRegionX >= 0 && panelWindow.targetedRegionY >= 0) {
|
|
||||||
panelWindow.regionX = panelWindow.targetedRegionX;
|
|
||||||
panelWindow.regionY = panelWindow.targetedRegionY;
|
|
||||||
panelWindow.regionWidth = panelWindow.targetedRegionWidth;
|
|
||||||
panelWindow.regionHeight = panelWindow.targetedRegionHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
snipProc.snip();
|
|
||||||
}
|
|
||||||
onPositionChanged: mouse => {
|
|
||||||
if (panelWindow.dragging) {
|
|
||||||
panelWindow.draggingX = mouse.x;
|
|
||||||
panelWindow.draggingY = mouse.y;
|
|
||||||
panelWindow.dragDiffX = mouse.x - panelWindow.dragStartX;
|
|
||||||
panelWindow.dragDiffY = mouse.y - panelWindow.dragStartY;
|
|
||||||
}
|
|
||||||
panelWindow.updateTargetedRegion(mouse.x, mouse.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overlay to darken screen
|
|
||||||
Rectangle { // Base
|
|
||||||
id: overlayRect
|
|
||||||
z: 0
|
|
||||||
anchors.fill: parent
|
|
||||||
color: root.overlayColor
|
|
||||||
layer.enabled: true
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
// TODO: Make this mask the base instead of just overlaying a border
|
|
||||||
z: 1
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
top: parent.top
|
|
||||||
leftMargin: panelWindow.regionX
|
|
||||||
topMargin: panelWindow.regionY
|
|
||||||
}
|
|
||||||
width: panelWindow.regionWidth
|
|
||||||
height: panelWindow.regionHeight
|
|
||||||
color: "transparent"
|
|
||||||
border.color: root.selectionBorderColor
|
|
||||||
border.width: 2
|
|
||||||
radius: root.standardRounding
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instructions
|
|
||||||
Rectangle {
|
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
topMargin: (Appearance.sizes.barHeight - implicitHeight) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
opacity: panelWindow.dragging ? 0 : 1
|
|
||||||
visible: opacity > 0
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
color: root.genericContentColor
|
|
||||||
radius: 10
|
|
||||||
border.width: 1
|
|
||||||
border.color: Appearance.m3colors.m3outlineVariant
|
|
||||||
implicitWidth: instructionsRow.implicitWidth + 10 * 2
|
|
||||||
implicitHeight: instructionsRow.implicitHeight + 5 * 2
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: instructionsRow
|
|
||||||
anchors.centerIn: parent
|
|
||||||
Item {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
implicitWidth: screenshotRegionIcon.implicitWidth
|
|
||||||
MaterialSymbol {
|
|
||||||
id: screenshotRegionIcon
|
|
||||||
anchors.centerIn: parent
|
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
|
||||||
text: "screenshot_region"
|
|
||||||
color: root.genericContentForeground
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
text: Translation.tr("Drag or click a region • LMB: Copy • RMB: Edit")
|
|
||||||
color: root.genericContentForeground
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Window regions
|
|
||||||
Repeater {
|
|
||||||
model: ScriptModel {
|
|
||||||
values: panelWindow.windowRegions
|
|
||||||
}
|
|
||||||
delegate: TargetRegion {
|
|
||||||
z: 2
|
|
||||||
required property var modelData
|
|
||||||
showIcon: true
|
|
||||||
targeted: !panelWindow.draggedAway &&
|
|
||||||
(panelWindow.targetedRegionX === modelData.at[0]
|
|
||||||
&& panelWindow.targetedRegionY === modelData.at[1]
|
|
||||||
&& panelWindow.targetedRegionWidth === modelData.size[0]
|
|
||||||
&& panelWindow.targetedRegionHeight === modelData.size[1])
|
|
||||||
|
|
||||||
opacity: panelWindow.draggedAway ? 0 : 1
|
|
||||||
visible: opacity > 0
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
x: modelData.at[0]
|
|
||||||
y: modelData.at[1]
|
|
||||||
width: modelData.size[0]
|
|
||||||
height: modelData.size[1]
|
|
||||||
borderColor: root.windowBorderColor
|
|
||||||
fillColor: targeted ? root.windowFillColor : "transparent"
|
|
||||||
border.width: targeted ? 4 : 2
|
|
||||||
text: `${modelData.class}`
|
|
||||||
radius: Appearance.rounding.windowRounding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Layer regions
|
|
||||||
Repeater {
|
|
||||||
model: ScriptModel {
|
|
||||||
values: panelWindow.layerRegions
|
|
||||||
}
|
|
||||||
delegate: TargetRegion {
|
|
||||||
z: 3
|
|
||||||
required property var modelData
|
|
||||||
targeted: !panelWindow.draggedAway &&
|
|
||||||
(panelWindow.targetedRegionX === modelData.at[0]
|
|
||||||
&& panelWindow.targetedRegionY === modelData.at[1]
|
|
||||||
&& panelWindow.targetedRegionWidth === modelData.size[0]
|
|
||||||
&& panelWindow.targetedRegionHeight === modelData.size[1])
|
|
||||||
|
|
||||||
opacity: panelWindow.draggedAway ? 0 : 1
|
|
||||||
visible: opacity > 0
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
x: modelData.at[0]
|
|
||||||
y: modelData.at[1]
|
|
||||||
width: modelData.size[0]
|
|
||||||
height: modelData.size[1]
|
|
||||||
borderColor: root.windowBorderColor
|
|
||||||
fillColor: targeted ? root.windowFillColor : "transparent"
|
|
||||||
border.width: targeted ? 4 : 2
|
|
||||||
text: `${modelData.namespace}`
|
|
||||||
radius: Appearance.rounding.windowRounding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image regions
|
|
||||||
Repeater {
|
|
||||||
model: ScriptModel {
|
|
||||||
values: Config.options.screenshotTool.showContentRegions ? panelWindow.imageRegions : []
|
|
||||||
}
|
|
||||||
delegate: TargetRegion {
|
|
||||||
z: 4
|
|
||||||
required property var modelData
|
|
||||||
targeted: !panelWindow.draggedAway &&
|
|
||||||
(panelWindow.targetedRegionX === modelData.at[0]
|
|
||||||
&& panelWindow.targetedRegionY === modelData.at[1]
|
|
||||||
&& panelWindow.targetedRegionWidth === modelData.size[0]
|
|
||||||
&& panelWindow.targetedRegionHeight === modelData.size[1])
|
|
||||||
|
|
||||||
opacity: panelWindow.draggedAway ? 0 : 1
|
|
||||||
visible: opacity > 0
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
x: modelData.at[0]
|
|
||||||
y: modelData.at[1]
|
|
||||||
width: modelData.size[0]
|
|
||||||
height: modelData.size[1]
|
|
||||||
borderColor: root.imageBorderColor
|
|
||||||
fillColor: targeted ? root.imageFillColor : "transparent"
|
|
||||||
border.width: targeted ? 4 : 2
|
|
||||||
text: "Content region"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
#!/usr/bin/env -S\_/bin/sh\_-xc\_"source\_\$(eval\_echo\_\$ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate&&exec\_python\_-E\_"\$0"\_"\$@""
|
|
||||||
|
|
||||||
# From https://github.com/stwa/wayland-idle-inhibitor
|
|
||||||
# License: WTFPL Version 2
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from signal import SIGINT, SIGTERM, signal
|
|
||||||
from threading import Event
|
|
||||||
import setproctitle
|
|
||||||
|
|
||||||
from pywayland.client.display import Display
|
|
||||||
from pywayland.protocol.idle_inhibit_unstable_v1.zwp_idle_inhibit_manager_v1 import (
|
|
||||||
ZwpIdleInhibitManagerV1,
|
|
||||||
)
|
|
||||||
from pywayland.protocol.wayland.wl_compositor import WlCompositor
|
|
||||||
from pywayland.protocol.wayland.wl_registry import WlRegistryProxy
|
|
||||||
from pywayland.protocol.wayland.wl_surface import WlSurface
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class GlobalRegistry:
|
|
||||||
surface: WlSurface | None = None
|
|
||||||
inhibit_manager: ZwpIdleInhibitManagerV1 | None = None
|
|
||||||
|
|
||||||
|
|
||||||
def handle_registry_global(
|
|
||||||
wl_registry: WlRegistryProxy, id_num: int, iface_name: str, version: int
|
|
||||||
) -> None:
|
|
||||||
global_registry: GlobalRegistry = wl_registry.user_data or GlobalRegistry()
|
|
||||||
|
|
||||||
if iface_name == "wl_compositor":
|
|
||||||
compositor = wl_registry.bind(id_num, WlCompositor, version)
|
|
||||||
global_registry.surface = compositor.create_surface() # type: ignore
|
|
||||||
elif iface_name == "zwp_idle_inhibit_manager_v1":
|
|
||||||
global_registry.inhibit_manager = wl_registry.bind(
|
|
||||||
id_num, ZwpIdleInhibitManagerV1, version
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
done = Event()
|
|
||||||
signal(SIGINT, lambda _, __: done.set())
|
|
||||||
signal(SIGTERM, lambda _, __: done.set())
|
|
||||||
|
|
||||||
global_registry = GlobalRegistry()
|
|
||||||
|
|
||||||
display = Display()
|
|
||||||
display.connect()
|
|
||||||
|
|
||||||
registry = display.get_registry() # type: ignore
|
|
||||||
registry.user_data = global_registry
|
|
||||||
registry.dispatcher["global"] = handle_registry_global
|
|
||||||
|
|
||||||
def shutdown() -> None:
|
|
||||||
display.dispatch()
|
|
||||||
display.roundtrip()
|
|
||||||
display.disconnect()
|
|
||||||
|
|
||||||
display.dispatch()
|
|
||||||
display.roundtrip()
|
|
||||||
|
|
||||||
if global_registry.surface is None or global_registry.inhibit_manager is None:
|
|
||||||
print("Wayland seems not to support idle_inhibit_unstable_v1 protocol.")
|
|
||||||
shutdown()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
inhibitor = global_registry.inhibit_manager.create_inhibitor( # type: ignore
|
|
||||||
global_registry.surface
|
|
||||||
)
|
|
||||||
|
|
||||||
display.dispatch()
|
|
||||||
display.roundtrip()
|
|
||||||
|
|
||||||
print("Inhibiting idle...")
|
|
||||||
done.wait()
|
|
||||||
print("Shutting down...")
|
|
||||||
|
|
||||||
inhibitor.destroy()
|
|
||||||
|
|
||||||
shutdown()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
setproctitle.setproctitle("wayland-idle-inhibitor.py")
|
|
||||||
main()
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import qs.modules.common
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Services.Pipewire
|
|
||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A nice wrapper for default Pipewire audio sink and source.
|
|
||||||
*/
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property bool ready: Pipewire.defaultAudioSink?.ready ?? false
|
|
||||||
property PwNode sink: Pipewire.defaultAudioSink
|
|
||||||
property PwNode source: Pipewire.defaultAudioSource
|
|
||||||
|
|
||||||
signal sinkProtectionTriggered(string reason);
|
|
||||||
|
|
||||||
PwObjectTracker {
|
|
||||||
objects: [sink, source]
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections { // Protection against sudden volume changes
|
|
||||||
target: sink?.audio ?? null
|
|
||||||
property bool lastReady: false
|
|
||||||
property real lastVolume: 0
|
|
||||||
function onVolumeChanged() {
|
|
||||||
if (!Config.options.audio.protection.enable) return;
|
|
||||||
if (!lastReady) {
|
|
||||||
lastVolume = sink.audio.volume;
|
|
||||||
lastReady = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newVolume = sink.audio.volume;
|
|
||||||
const maxAllowedIncrease = Config.options.audio.protection.maxAllowedIncrease / 100;
|
|
||||||
const maxAllowed = Config.options.audio.protection.maxAllowed / 100;
|
|
||||||
|
|
||||||
if (newVolume - lastVolume > maxAllowedIncrease) {
|
|
||||||
sink.audio.volume = lastVolume;
|
|
||||||
root.sinkProtectionTriggered("Illegal increment");
|
|
||||||
} else if (newVolume > maxAllowed) {
|
|
||||||
root.sinkProtectionTriggered("Exceeded max allowed");
|
|
||||||
sink.audio.volume = Math.min(lastVolume, maxAllowed);
|
|
||||||
}
|
|
||||||
if (sink.ready && (isNaN(sink.audio.volume) || sink.audio.volume === undefined || sink.audio.volume === null)) {
|
|
||||||
sink.audio.volume = 0;
|
|
||||||
}
|
|
||||||
lastVolume = sink.audio.volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import qs
|
|
||||||
import qs.modules.common
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Services.UPower
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
property bool available: UPower.displayDevice.isLaptopBattery
|
|
||||||
property var chargeState: UPower.displayDevice.state
|
|
||||||
property bool isCharging: chargeState == UPowerDeviceState.Charging
|
|
||||||
property bool isPluggedIn: isCharging || chargeState == UPowerDeviceState.PendingCharge
|
|
||||||
property real percentage: UPower.displayDevice.percentage
|
|
||||||
readonly property bool allowAutomaticSuspend: Config.options.battery.automaticSuspend
|
|
||||||
|
|
||||||
property bool isLow: percentage <= Config.options.battery.low / 100
|
|
||||||
property bool isCritical: percentage <= Config.options.battery.critical / 100
|
|
||||||
property bool isSuspending: percentage <= Config.options.battery.suspend / 100
|
|
||||||
|
|
||||||
property bool isLowAndNotCharging: isLow && !isCharging
|
|
||||||
property bool isCriticalAndNotCharging: isCritical && !isCharging
|
|
||||||
property bool isSuspendingAndNotCharging: allowAutomaticSuspend && isSuspending && !isCharging
|
|
||||||
|
|
||||||
onIsLowAndNotChargingChanged: {
|
|
||||||
if (available && isLowAndNotCharging) Quickshell.execDetached([
|
|
||||||
"notify-send",
|
|
||||||
Translation.tr("Low battery"),
|
|
||||||
Translation.tr("Consider plugging in your device"),
|
|
||||||
"-u", "critical",
|
|
||||||
"-a", "Shell"
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
onIsCriticalAndNotChargingChanged: {
|
|
||||||
if (available && isCriticalAndNotCharging) Quickshell.execDetached([
|
|
||||||
"notify-send",
|
|
||||||
Translation.tr("Critically low battery"),
|
|
||||||
Translation.tr("Please charge!\nAutomatic suspend triggers at %1").arg(Config.options.battery.suspend),
|
|
||||||
"-u", "critical",
|
|
||||||
"-a", "Shell"
|
|
||||||
]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
onIsSuspendingAndNotChargingChanged: {
|
|
||||||
if (available && isSuspendingAndNotCharging) {
|
|
||||||
Quickshell.execDetached(["bash", "-c", `systemctl suspend || loginctl suspend`]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import Quickshell;
|
|
||||||
import Quickshell.Io;
|
|
||||||
import QtQuick;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic polled Bluetooth state.
|
|
||||||
*/
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property int updateInterval: 1000
|
|
||||||
property string bluetoothDeviceName: ""
|
|
||||||
property string bluetoothDeviceAddress: ""
|
|
||||||
property bool bluetoothEnabled: false
|
|
||||||
property bool bluetoothConnected: false
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
updateBluetoothDevice.running = true
|
|
||||||
updateBluetoothStatus.running = true
|
|
||||||
updateBluetoothEnabled.running = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
interval: 10
|
|
||||||
running: true
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
update()
|
|
||||||
interval = root.updateInterval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if Bluetooth is enabled (controller powered on)
|
|
||||||
Process {
|
|
||||||
id: updateBluetoothEnabled
|
|
||||||
command: ["sh", "-c", "bluetoothctl show | grep -q 'Powered: yes' && echo 1 || echo 0"]
|
|
||||||
running: true
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
root.bluetoothEnabled = (parseInt(data) === 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the name and address of the first connected Bluetooth device
|
|
||||||
Process {
|
|
||||||
id: updateBluetoothDevice
|
|
||||||
command: ["sh", "-c", "bluetoothctl info | awk -F': ' '/Name: /{name=$2} /Device /{addr=$2} END{print name \":\" addr}'"]
|
|
||||||
running: true
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
let parts = data.split(":")
|
|
||||||
root.bluetoothDeviceName = parts[0] || ""
|
|
||||||
root.bluetoothDeviceAddress = parts[1] || ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if any device is connected
|
|
||||||
Process {
|
|
||||||
id: updateBluetoothStatus
|
|
||||||
command: ["sh", "-c", "bluetoothctl info | grep -q 'Connected: yes' && echo 1 || echo 0"]
|
|
||||||
running: true
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
root.bluetoothConnected = (parseInt(data) === 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
// From https://github.com/caelestia-dots/shell with modifications.
|
|
||||||
// License: GPLv3
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For managing brightness of monitors. Supports both brightnessctl and ddcutil.
|
|
||||||
*/
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
signal brightnessChanged()
|
|
||||||
|
|
||||||
property var ddcMonitors: []
|
|
||||||
readonly property list<BrightnessMonitor> monitors: Quickshell.screens.map(screen => monitorComp.createObject(root, {
|
|
||||||
screen
|
|
||||||
}))
|
|
||||||
|
|
||||||
function getMonitorForScreen(screen: ShellScreen): var {
|
|
||||||
return monitors.find(m => m.screen === screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
function increaseBrightness(): void {
|
|
||||||
const focusedName = Hyprland.focusedMonitor.name;
|
|
||||||
const monitor = monitors.find(m => focusedName === m.screen.name);
|
|
||||||
if (monitor)
|
|
||||||
monitor.setBrightness(monitor.brightness + 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
function decreaseBrightness(): void {
|
|
||||||
const focusedName = Hyprland.focusedMonitor.name;
|
|
||||||
const monitor = monitors.find(m => focusedName === m.screen.name);
|
|
||||||
if (monitor)
|
|
||||||
monitor.setBrightness(monitor.brightness - 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadableId: "brightness"
|
|
||||||
|
|
||||||
onMonitorsChanged: {
|
|
||||||
ddcMonitors = [];
|
|
||||||
ddcProc.running = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: ddcProc
|
|
||||||
|
|
||||||
command: ["ddcutil", "detect", "--brief"]
|
|
||||||
stdout: SplitParser {
|
|
||||||
splitMarker: "\n\n"
|
|
||||||
onRead: data => {
|
|
||||||
if (data.startsWith("Display ")) {
|
|
||||||
const lines = data.split("\n").map(l => l.trim());
|
|
||||||
root.ddcMonitors.push({
|
|
||||||
model: lines.find(l => l.startsWith("Monitor:")).split(":")[2],
|
|
||||||
busNum: lines.find(l => l.startsWith("I2C bus:")).split("/dev/i2c-")[1]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onExited: root.ddcMonitorsChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: setProc
|
|
||||||
}
|
|
||||||
|
|
||||||
component BrightnessMonitor: QtObject {
|
|
||||||
id: monitor
|
|
||||||
|
|
||||||
required property ShellScreen screen
|
|
||||||
readonly property bool isDdc: root.ddcMonitors.some(m => m.model === screen.model)
|
|
||||||
readonly property string busNum: root.ddcMonitors.find(m => m.model === screen.model)?.busNum ?? ""
|
|
||||||
property real brightness
|
|
||||||
property bool ready: false
|
|
||||||
|
|
||||||
onBrightnessChanged: {
|
|
||||||
if (monitor.ready) {
|
|
||||||
root.brightnessChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initialize() {
|
|
||||||
monitor.ready = false;
|
|
||||||
initProc.command = isDdc ? ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"] : ["sh", "-c", `echo "a b c $(brightnessctl g) $(brightnessctl m)"`];
|
|
||||||
initProc.running = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property Process initProc: Process {
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
const [, , , current, max] = data.split(" ");
|
|
||||||
monitor.brightness = parseInt(current) / parseInt(max);
|
|
||||||
monitor.ready = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBrightness(value: real): void {
|
|
||||||
value = Math.max(0.01, Math.min(1, value));
|
|
||||||
const rounded = Math.round(value * 100);
|
|
||||||
if (Math.round(brightness * 100) === rounded)
|
|
||||||
return;
|
|
||||||
brightness = value;
|
|
||||||
setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rounded] : ["brightnessctl", "s", `${rounded}%`, "--quiet"];
|
|
||||||
setProc.startDetached();
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
onBusNumChanged: {
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: monitorComp
|
|
||||||
|
|
||||||
BrightnessMonitor {}
|
|
||||||
}
|
|
||||||
|
|
||||||
IpcHandler {
|
|
||||||
target: "brightness"
|
|
||||||
|
|
||||||
function increment() {
|
|
||||||
onPressed: root.increaseBrightness()
|
|
||||||
}
|
|
||||||
|
|
||||||
function decrement() {
|
|
||||||
onPressed: root.decreaseBrightness()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "brightnessIncrease"
|
|
||||||
description: "Increase brightness"
|
|
||||||
onPressed: root.increaseBrightness()
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "brightnessDecrease"
|
|
||||||
description: "Decrease brightness"
|
|
||||||
onPressed: root.decreaseBrightness()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple polled network state service.
|
|
||||||
*/
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property bool wifi: true
|
|
||||||
property bool ethernet: false
|
|
||||||
property int updateInterval: 1000
|
|
||||||
property string networkName: ""
|
|
||||||
property int networkStrength
|
|
||||||
property string materialSymbol: ethernet ? "lan" :
|
|
||||||
(Network.networkName.length > 0 && Network.networkName != "lo") ? (
|
|
||||||
Network.networkStrength > 80 ? "signal_wifi_4_bar" :
|
|
||||||
Network.networkStrength > 60 ? "network_wifi_3_bar" :
|
|
||||||
Network.networkStrength > 40 ? "network_wifi_2_bar" :
|
|
||||||
Network.networkStrength > 20 ? "network_wifi_1_bar" :
|
|
||||||
"signal_wifi_0_bar"
|
|
||||||
) : "signal_wifi_off"
|
|
||||||
function update() {
|
|
||||||
updateConnectionType.startCheck();
|
|
||||||
updateNetworkName.running = true;
|
|
||||||
updateNetworkStrength.running = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
interval: 10
|
|
||||||
running: true
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
root.update();
|
|
||||||
interval = root.updateInterval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: updateConnectionType
|
|
||||||
property string buffer
|
|
||||||
command: ["sh", "-c", "nmcli -t -f NAME,TYPE,DEVICE c show --active"]
|
|
||||||
running: true
|
|
||||||
function startCheck() {
|
|
||||||
buffer = "";
|
|
||||||
updateConnectionType.running = true;
|
|
||||||
}
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
updateConnectionType.buffer += data + "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onExited: (exitCode, exitStatus) => {
|
|
||||||
const lines = updateConnectionType.buffer.trim().split('\n');
|
|
||||||
let hasEthernet = false;
|
|
||||||
let hasWifi = false;
|
|
||||||
lines.forEach(line => {
|
|
||||||
if (line.includes("ethernet"))
|
|
||||||
hasEthernet = true;
|
|
||||||
else if (line.includes("wireless"))
|
|
||||||
hasWifi = true;
|
|
||||||
});
|
|
||||||
root.ethernet = hasEthernet;
|
|
||||||
root.wifi = hasWifi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: updateNetworkName
|
|
||||||
command: ["sh", "-c", "nmcli -t -f NAME c show --active | head -1"]
|
|
||||||
running: true
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
root.networkName = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: updateNetworkStrength
|
|
||||||
running: true
|
|
||||||
command: ["sh", "-c", "nmcli -f IN-USE,SIGNAL,SSID device wifi | awk '/^\*/{if (NR!=1) {print $2}}'"]
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
root.networkStrength = parseInt(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import qs.modules.common
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple polled resource usage service with RAM, Swap, and CPU usage.
|
|
||||||
*/
|
|
||||||
Singleton {
|
|
||||||
property double memoryTotal: 1
|
|
||||||
property double memoryFree: 1
|
|
||||||
property double memoryUsed: memoryTotal - memoryFree
|
|
||||||
property double memoryUsedPercentage: memoryUsed / memoryTotal
|
|
||||||
property double swapTotal: 1
|
|
||||||
property double swapFree: 1
|
|
||||||
property double swapUsed: swapTotal - swapFree
|
|
||||||
property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
|
|
||||||
property double cpuUsage: 0
|
|
||||||
property var previousCpuStats
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
interval: 1
|
|
||||||
running: true
|
|
||||||
repeat: true
|
|
||||||
onTriggered: {
|
|
||||||
// Reload files
|
|
||||||
fileMeminfo.reload()
|
|
||||||
fileStat.reload()
|
|
||||||
|
|
||||||
// Parse memory and swap usage
|
|
||||||
const textMeminfo = fileMeminfo.text()
|
|
||||||
memoryTotal = Number(textMeminfo.match(/MemTotal: *(\d+)/)?.[1] ?? 1)
|
|
||||||
memoryFree = Number(textMeminfo.match(/MemAvailable: *(\d+)/)?.[1] ?? 0)
|
|
||||||
swapTotal = Number(textMeminfo.match(/SwapTotal: *(\d+)/)?.[1] ?? 1)
|
|
||||||
swapFree = Number(textMeminfo.match(/SwapFree: *(\d+)/)?.[1] ?? 0)
|
|
||||||
|
|
||||||
// Parse CPU usage
|
|
||||||
const textStat = fileStat.text()
|
|
||||||
const cpuLine = textStat.match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/)
|
|
||||||
if (cpuLine) {
|
|
||||||
const stats = cpuLine.slice(1).map(Number)
|
|
||||||
const total = stats.reduce((a, b) => a + b, 0)
|
|
||||||
const idle = stats[3]
|
|
||||||
|
|
||||||
if (previousCpuStats) {
|
|
||||||
const totalDiff = total - previousCpuStats.total
|
|
||||||
const idleDiff = idle - previousCpuStats.idle
|
|
||||||
cpuUsage = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0
|
|
||||||
}
|
|
||||||
|
|
||||||
previousCpuStats = { total, idle }
|
|
||||||
}
|
|
||||||
interval = Config.options?.resources?.updateInterval ?? 3000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView { id: fileMeminfo; path: "/proc/meminfo" }
|
|
||||||
FileView { id: fileStat; path: "/proc/stat" }
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
|
|
||||||
ApiStrategy {
|
|
||||||
property string buffer: ""
|
|
||||||
|
|
||||||
function buildEndpoint(model: AiModel): string {
|
|
||||||
const result = model.endpoint + `?key=\$\{${root.apiKeyEnvVarName}\}`
|
|
||||||
// console.log("[AI] Endpoint: " + result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildRequestData(model: AiModel, messages, systemPrompt: string, temperature: real, tools: list<var>) {
|
|
||||||
let baseData = {
|
|
||||||
"contents": messages.map(message => {
|
|
||||||
const geminiApiRoleName = (message.role === "assistant") ? "model" : message.role;
|
|
||||||
const usingSearch = tools[0]?.google_search !== undefined
|
|
||||||
if (!usingSearch && message.functionCall != undefined && message.functionName.length > 0) {
|
|
||||||
return {
|
|
||||||
"role": geminiApiRoleName,
|
|
||||||
"parts": [{
|
|
||||||
functionCall: {
|
|
||||||
"name": message.functionName,
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!usingSearch && message.functionResponse != undefined && message.functionName.length > 0) {
|
|
||||||
return {
|
|
||||||
"role": geminiApiRoleName,
|
|
||||||
"parts": [{
|
|
||||||
functionResponse: {
|
|
||||||
"name": message.functionName,
|
|
||||||
"response": { "content": message.functionResponse }
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
"role": geminiApiRoleName,
|
|
||||||
"parts": [{
|
|
||||||
text: message.rawContent,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"tools": tools,
|
|
||||||
"system_instruction": {
|
|
||||||
"parts": [{ text: systemPrompt }]
|
|
||||||
},
|
|
||||||
"generationConfig": {
|
|
||||||
"temperature": temperature,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return model.extraParams ? Object.assign({}, baseData, model.extraParams) : baseData;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildAuthorizationHeader(apiKeyEnvVarName: string): string {
|
|
||||||
// Gemini doesn't use Authorization header, key is in URL
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseResponseLine(line, message) {
|
|
||||||
if (line.startsWith("[")) {
|
|
||||||
buffer += line.slice(1).trim();
|
|
||||||
} else if (line === "]") {
|
|
||||||
buffer += line.slice(0, -1).trim();
|
|
||||||
return parseBuffer(message);
|
|
||||||
} else if (line.startsWith(",")) {
|
|
||||||
return parseBuffer(message);
|
|
||||||
} else {
|
|
||||||
buffer += line.trim();
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseBuffer(message) {
|
|
||||||
// console.log("[Ai] Gemini buffer: ", buffer);
|
|
||||||
let finished = false;
|
|
||||||
try {
|
|
||||||
if (buffer.length === 0) return {};
|
|
||||||
const dataJson = JSON.parse(buffer);
|
|
||||||
if (!dataJson.candidates) return {};
|
|
||||||
|
|
||||||
if (dataJson.candidates[0]?.finishReason) {
|
|
||||||
finished = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function call handling
|
|
||||||
if (dataJson.candidates[0]?.content?.parts[0]?.functionCall) {
|
|
||||||
const functionCall = dataJson.candidates[0]?.content?.parts[0]?.functionCall;
|
|
||||||
message.functionName = functionCall.name;
|
|
||||||
message.functionCall = functionCall.name;
|
|
||||||
const newContent = `\n\n[[ Function: ${functionCall.name}(${JSON.stringify(functionCall.args, null, 2)}) ]]\n`
|
|
||||||
message.rawContent += newContent;
|
|
||||||
message.content += newContent;
|
|
||||||
return { functionCall: { name: functionCall.name, args: functionCall.args }, finished: finished };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normal text response
|
|
||||||
const responseContent = dataJson.candidates[0]?.content?.parts[0]?.text
|
|
||||||
message.rawContent += responseContent;
|
|
||||||
message.content += responseContent;
|
|
||||||
|
|
||||||
// Handle annotations and metadata
|
|
||||||
const annotationSources = dataJson.candidates[0]?.groundingMetadata?.groundingChunks?.map(chunk => {
|
|
||||||
return {
|
|
||||||
"type": "url_citation",
|
|
||||||
"text": chunk?.web?.title,
|
|
||||||
"url": chunk?.web?.uri,
|
|
||||||
}
|
|
||||||
}) ?? [];
|
|
||||||
|
|
||||||
const annotations = dataJson.candidates[0]?.groundingMetadata?.groundingSupports?.map(citation => {
|
|
||||||
return {
|
|
||||||
"type": "url_citation",
|
|
||||||
"start_index": citation.segment?.startIndex,
|
|
||||||
"end_index": citation.segment?.endIndex,
|
|
||||||
"text": citation?.segment.text,
|
|
||||||
"url": annotationSources[citation.groundingChunkIndices[0]]?.url,
|
|
||||||
"sources": citation.groundingChunkIndices
|
|
||||||
}
|
|
||||||
});
|
|
||||||
message.annotationSources = annotationSources;
|
|
||||||
message.annotations = annotations;
|
|
||||||
message.searchQueries = dataJson.candidates[0]?.groundingMetadata?.webSearchQueries ?? [];
|
|
||||||
|
|
||||||
// Usage metadata
|
|
||||||
if (dataJson.usageMetadata) {
|
|
||||||
return {
|
|
||||||
tokenUsage: {
|
|
||||||
input: dataJson.usageMetadata.promptTokenCount ?? -1,
|
|
||||||
output: dataJson.usageMetadata.candidatesTokenCount ?? -1,
|
|
||||||
total: dataJson.usageMetadata.totalTokenCount ?? -1
|
|
||||||
},
|
|
||||||
finished: finished
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
console.log("[AI] Gemini: Could not parse buffer: ", e);
|
|
||||||
message.rawContent += buffer;
|
|
||||||
message.content += buffer;
|
|
||||||
} finally {
|
|
||||||
buffer = "";
|
|
||||||
}
|
|
||||||
return { finished: finished };
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRequestFinished(message) {
|
|
||||||
return parseBuffer(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
buffer = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
//@ pragma UseQApplication
|
|
||||||
//@ pragma Env QS_NO_RELOAD_POPUP=1
|
|
||||||
//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic
|
|
||||||
//@ pragma Env QT_QUICK_FLICKABLE_WHEEL_DECELERATION=10000
|
|
||||||
|
|
||||||
// Adjust this to make the shell smaller or larger
|
|
||||||
//@ pragma Env QT_SCALE_FACTOR=1
|
|
||||||
|
|
||||||
|
|
||||||
import "./modules/common/"
|
|
||||||
import "./modules/background/"
|
|
||||||
import "./modules/bar/"
|
|
||||||
import "./modules/cheatsheet/"
|
|
||||||
import "./modules/dock/"
|
|
||||||
import "./modules/lock/"
|
|
||||||
import "./modules/mediaControls/"
|
|
||||||
import "./modules/notificationPopup/"
|
|
||||||
import "./modules/onScreenDisplay/"
|
|
||||||
import "./modules/onScreenKeyboard/"
|
|
||||||
import "./modules/overview/"
|
|
||||||
import "./modules/screenCorners/"
|
|
||||||
import "./modules/session/"
|
|
||||||
import "./modules/sidebarLeft/"
|
|
||||||
import "./modules/sidebarRight/"
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Window
|
|
||||||
import Quickshell
|
|
||||||
import "./services/"
|
|
||||||
|
|
||||||
ShellRoot {
|
|
||||||
// Enable/disable modules here. False = not loaded at all, so rest assured
|
|
||||||
// no unnecessary stuff will take up memory if you decide to only use, say, the overview.
|
|
||||||
property bool enableBar: true
|
|
||||||
property bool enableBackground: true
|
|
||||||
property bool enableCheatsheet: true
|
|
||||||
property bool enableDock: true
|
|
||||||
property bool enableLock: true
|
|
||||||
property bool enableMediaControls: true
|
|
||||||
property bool enableNotificationPopup: true
|
|
||||||
property bool enableOnScreenDisplayBrightness: true
|
|
||||||
property bool enableOnScreenDisplayVolume: true
|
|
||||||
property bool enableOnScreenKeyboard: true
|
|
||||||
property bool enableOverview: true
|
|
||||||
property bool enableReloadPopup: true
|
|
||||||
property bool enableScreenCorners: true
|
|
||||||
property bool enableSession: true
|
|
||||||
property bool enableSidebarLeft: true
|
|
||||||
property bool enableSidebarRight: true
|
|
||||||
|
|
||||||
// Force initialization of some singletons
|
|
||||||
Component.onCompleted: {
|
|
||||||
Cliphist.refresh()
|
|
||||||
FirstRunExperience.load()
|
|
||||||
Hyprsunset.load()
|
|
||||||
MaterialThemeLoader.reapplyTheme()
|
|
||||||
}
|
|
||||||
|
|
||||||
LazyLoader { active: enableBar; component: Bar {} }
|
|
||||||
LazyLoader { active: enableBackground; component: Background {} }
|
|
||||||
LazyLoader { active: enableCheatsheet; component: Cheatsheet {} }
|
|
||||||
LazyLoader { active: enableDock && Config.options.dock.enable; component: Dock {} }
|
|
||||||
LazyLoader { active: enableLock; component: Lock {} }
|
|
||||||
LazyLoader { active: enableMediaControls; component: MediaControls {} }
|
|
||||||
LazyLoader { active: enableNotificationPopup; component: NotificationPopup {} }
|
|
||||||
LazyLoader { active: enableOnScreenDisplayBrightness; component: OnScreenDisplayBrightness {} }
|
|
||||||
LazyLoader { active: enableOnScreenDisplayVolume; component: OnScreenDisplayVolume {} }
|
|
||||||
LazyLoader { active: enableOnScreenKeyboard; component: OnScreenKeyboard {} }
|
|
||||||
LazyLoader { active: enableOverview; component: Overview {} }
|
|
||||||
LazyLoader { active: enableReloadPopup; component: ReloadPopup {} }
|
|
||||||
LazyLoader { active: enableScreenCorners; component: ScreenCorners {} }
|
|
||||||
LazyLoader { active: enableSession; component: Session {} }
|
|
||||||
LazyLoader { active: enableSidebarLeft; component: SidebarLeft {} }
|
|
||||||
LazyLoader { active: enableSidebarRight; component: SidebarRight {} }
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,314 +0,0 @@
|
|||||||
{
|
|
||||||
"Mo": "Mo/*keep*/",
|
|
||||||
"Tu": "Tu/*keep*/",
|
|
||||||
"We": "We/*keep*/",
|
|
||||||
"Th": "Th/*keep*/",
|
|
||||||
"Fr": "Fr/*keep*/",
|
|
||||||
"Sa": "Sa/*keep*/",
|
|
||||||
"Su": "Su/*keep*/",
|
|
||||||
"%1 characters": "%1 characters",
|
|
||||||
"**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key",
|
|
||||||
"**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key",
|
|
||||||
". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!",
|
|
||||||
"<i>No further instruction provided</i>": "<i>No further instruction provided</i>",
|
|
||||||
"Action": "Action",
|
|
||||||
"Add": "Add",
|
|
||||||
"Add task": "Add task",
|
|
||||||
"All-rounder | Good quality, decent quantity": "All-rounder | Good quality, decent quantity",
|
|
||||||
"Allow NSFW": "Allow NSFW",
|
|
||||||
"Allow NSFW content": "Allow NSFW content",
|
|
||||||
"Anime": "Anime",
|
|
||||||
"Anime boorus": "Anime boorus",
|
|
||||||
"App": "App",
|
|
||||||
"Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel",
|
|
||||||
"Bluetooth": "Bluetooth",
|
|
||||||
"Brightness": "Brightness",
|
|
||||||
"Cancel": "Cancel",
|
|
||||||
"Chain of Thought": "Chain of Thought",
|
|
||||||
"Cheat sheet": "Cheat sheet",
|
|
||||||
"Choose model": "Choose model",
|
|
||||||
"Clean stuff | Excellent quality, no NSFW": "Clean stuff | Excellent quality, no NSFW",
|
|
||||||
"Clear": "Clear",
|
|
||||||
"Clear chat history": "Clear chat history",
|
|
||||||
"Clear the current list of images": "Clear the current list of images",
|
|
||||||
"Close": "Close",
|
|
||||||
"Copy": "Copy",
|
|
||||||
"Copy code": "Copy code",
|
|
||||||
"Delete": "Delete",
|
|
||||||
"Desktop": "Desktop",
|
|
||||||
"Disable NSFW content": "Disable NSFW content",
|
|
||||||
"Done": "Done",
|
|
||||||
"Download": "Download",
|
|
||||||
"Edit": "Edit",
|
|
||||||
"Enter text to translate...": "Enter text to translate...",
|
|
||||||
"Finished tasks will go here": "Finished tasks will go here",
|
|
||||||
"For desktop wallpapers | Good quality": "For desktop wallpapers | Good quality",
|
|
||||||
"For storing API keys and other sensitive information": "For storing API keys and other sensitive information",
|
|
||||||
"Game mode": "Game mode",
|
|
||||||
"Get the next page of results": "Get the next page of results",
|
|
||||||
"Hibernate": "Hibernate",
|
|
||||||
"Input": "Input",
|
|
||||||
"Intelligence": "Intelligence",
|
|
||||||
"Interface": "Interface",
|
|
||||||
"Invalid arguments. Must provide `key` and `value`.": "Invalid arguments. Must provide `key` and `value`.",
|
|
||||||
"Jump to current month": "Jump to current month",
|
|
||||||
"Keep system awake": "Keep system awake",
|
|
||||||
"Large images | God tier quality, no NSFW.": "Large images | God tier quality, no NSFW.",
|
|
||||||
"Large language models": "Large language models",
|
|
||||||
"Launch": "Launch",
|
|
||||||
"Lock": "Lock",
|
|
||||||
"Logout": "Logout",
|
|
||||||
"Markdown test": "Markdown test",
|
|
||||||
"Math result": "Math result",
|
|
||||||
"Night Light": "Night Light",
|
|
||||||
"No audio source": "No audio source",
|
|
||||||
"No media": "No media",
|
|
||||||
"No notifications": "No notifications",
|
|
||||||
"Not visible to model": "Not visible to model",
|
|
||||||
"Nothing here!": "Nothing here!",
|
|
||||||
"Notifications": "Notifications",
|
|
||||||
"OK": "OK",
|
|
||||||
"Open file link": "Open file link",
|
|
||||||
"Output": "Output",
|
|
||||||
"Reboot": "Reboot",
|
|
||||||
"Reboot to firmware settings": "Reboot to firmware settings",
|
|
||||||
"Reload Hyprland & Quickshell": "Reload Hyprland & Quickshell",
|
|
||||||
"Run": "Run",
|
|
||||||
"Run command": "Run command",
|
|
||||||
"Save": "Save",
|
|
||||||
"Save to Downloads": "Save to Downloads",
|
|
||||||
"Search": "Search",
|
|
||||||
"Search the web": "Search the web",
|
|
||||||
"Search, calculate or run": "Search, calculate or run",
|
|
||||||
"Select Language": "Select Language",
|
|
||||||
"Session": "Session",
|
|
||||||
"Set API key": "Set API key",
|
|
||||||
"Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.",
|
|
||||||
"Set the current API provider": "Set the current API provider",
|
|
||||||
"Shutdown": "Shutdown",
|
|
||||||
"Silent": "Silent",
|
|
||||||
"Sleep": "Sleep",
|
|
||||||
"System": "System",
|
|
||||||
"Task Manager": "Task Manager",
|
|
||||||
"Task description": "Task description",
|
|
||||||
"Temperature must be between 0 and 2": "Temperature must be between 0 and 2",
|
|
||||||
"The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "The hentai one | Great quantity, a lot of NSFW, quality varies wildly",
|
|
||||||
"The popular one | Best quantity, but quality can vary wildly": "The popular one | Best quantity, but quality can vary wildly",
|
|
||||||
"Thinking": "Thinking",
|
|
||||||
"Translation goes here...": "Translation goes here...",
|
|
||||||
"Translator": "Translator",
|
|
||||||
"Unfinished": "Unfinished",
|
|
||||||
"Unknown": "Unknown",
|
|
||||||
"Unknown Album": "Unknown Album",
|
|
||||||
"Unknown Artist": "Unknown Artist",
|
|
||||||
"Unknown Title": "Unknown Title",
|
|
||||||
"View Markdown source": "View Markdown source",
|
|
||||||
"Volume": "Volume",
|
|
||||||
"Volume mixer": "Volume mixer",
|
|
||||||
"Waifus only | Excellent quality, limited quantity": "Waifus only | Excellent quality, limited quantity",
|
|
||||||
"Waiting for response...": "Waiting for response...",
|
|
||||||
"Workspace": "Workspace",
|
|
||||||
"Set with /mode PROVIDER": "Set with /mode PROVIDER",
|
|
||||||
"Invalid API provider. Supported: \n-": "Invalid API provider. Supported: \n-",
|
|
||||||
"Unknown command:": "Unknown command:",
|
|
||||||
"Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window",
|
|
||||||
"The current API used. Endpoint:": "The current API used. Endpoint:",
|
|
||||||
"Provider set to": "Provider set to",
|
|
||||||
"Invalid model. Supported: \n```": "Invalid model. Supported: \n```",
|
|
||||||
"That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number",
|
|
||||||
"Online | Google's model\nGives up-to-date information with search.": "Online | Google's model\nGives up-to-date information with search.",
|
|
||||||
"Switched to search mode. Continue with the user's request.": "Switched to search mode. Continue with the user's request.",
|
|
||||||
"Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly",
|
|
||||||
"Settings": "Settings",
|
|
||||||
"Save chat": "Save chat",
|
|
||||||
"Load chat": "Load chat",
|
|
||||||
"or": "or",
|
|
||||||
"Set the system prompt for the model.": "Set the system prompt for the model.",
|
|
||||||
"To Do": "To Do",
|
|
||||||
"Calendar": "Calendar",
|
|
||||||
"Advanced": "Advanced",
|
|
||||||
"About": "About",
|
|
||||||
"Services": "Services",
|
|
||||||
"Style": "Style",
|
|
||||||
"Edit config": "Edit config",
|
|
||||||
"Colors & Wallpaper": "Colors & Wallpaper",
|
|
||||||
"Light": "Light",
|
|
||||||
"Dark": "Dark",
|
|
||||||
"Material palette": "Material palette",
|
|
||||||
"Fidelity": "Fidelity",
|
|
||||||
"Fruit Salad": "Fruit Salad",
|
|
||||||
"Alternatively use /dark, /light, /img in the launcher": "Alternatively use /dark, /light, /img in the launcher",
|
|
||||||
"Fake screen rounding": "Fake screen rounding",
|
|
||||||
"When not fullscreen": "When not fullscreen",
|
|
||||||
"Choose file": "Choose file",
|
|
||||||
"Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers",
|
|
||||||
"Be patient...": "Be patient...",
|
|
||||||
"Decorations & Effects": "Decorations & Effects",
|
|
||||||
"Tonal Spot": "Tonal Spot",
|
|
||||||
"Shell windows": "Shell windows",
|
|
||||||
"Auto": "Auto",
|
|
||||||
"Wallpaper": "Wallpaper",
|
|
||||||
"Content": "Content",
|
|
||||||
"Title bar": "Title bar",
|
|
||||||
"Transparency": "Transparency",
|
|
||||||
"Expressive": "Expressive",
|
|
||||||
"Yes": "Yes",
|
|
||||||
"Enable": "Enable",
|
|
||||||
"Rainbow": "Rainbow",
|
|
||||||
"Might look ass. Unsupported.": "Might look ass. Unsupported.",
|
|
||||||
"Monochrome": "Monochrome",
|
|
||||||
"Random: Konachan": "Random: Konachan",
|
|
||||||
"Center title": "Center title",
|
|
||||||
"Neutral": "Neutral",
|
|
||||||
"Pick wallpaper image on your system": "Pick wallpaper image on your system",
|
|
||||||
"No": "No",
|
|
||||||
"AI": "AI",
|
|
||||||
"Local only": "Local only",
|
|
||||||
"Policies": "Policies",
|
|
||||||
"Weeb": "Weeb",
|
|
||||||
"Closet": "Closet",
|
|
||||||
"Bar style": "Bar style",
|
|
||||||
"Show next time": "Show next time",
|
|
||||||
"Usage": "Usage",
|
|
||||||
"Plain rectangle": "Plain rectangle",
|
|
||||||
"Useless buttons": "Useless buttons",
|
|
||||||
"GitHub": "GitHub",
|
|
||||||
"Style & wallpaper": "Style & wallpaper",
|
|
||||||
"Configuration": "Configuration",
|
|
||||||
"Change any time later with /dark, /light, /img in the launcher": "Change any time later with /dark, /light, /img in the launcher",
|
|
||||||
"Keybinds": "Keybinds",
|
|
||||||
"Float": "Float",
|
|
||||||
"Hug": "Hug",
|
|
||||||
"Yooooo hi there": "Yooooo hi there",
|
|
||||||
"illogical-impulse Welcome": "illogical-impulse Welcome",
|
|
||||||
"Info": "Info",
|
|
||||||
"Volume limit": "Volume limit",
|
|
||||||
"Prevents abrupt increments and restricts volume limit": "Prevents abrupt increments and restricts volume limit",
|
|
||||||
"Resources": "Resources",
|
|
||||||
"12h am/pm": "12h am/pm",
|
|
||||||
"Base URL": "Base URL",
|
|
||||||
"Audio": "Audio",
|
|
||||||
"Networking": "Networking",
|
|
||||||
"Format": "Format",
|
|
||||||
"Time": "Time",
|
|
||||||
"Battery": "Battery",
|
|
||||||
"Prefixes": "Prefixes",
|
|
||||||
"Emojis": "Emojis",
|
|
||||||
"Earbang protection": "Earbang protection",
|
|
||||||
"Automatically suspends the system when battery is low": "Automatically suspends the system when battery is low",
|
|
||||||
"Automatic suspend": "Automatic suspend",
|
|
||||||
"Suspend at": "Suspend at",
|
|
||||||
"Max allowed increase": "Max allowed increase",
|
|
||||||
"Web search": "Web search",
|
|
||||||
"Polling interval (ms)": "Polling interval (ms)",
|
|
||||||
"Clipboard": "Clipboard",
|
|
||||||
"Low warning": "Low warning",
|
|
||||||
"24h": "24h",
|
|
||||||
"Use Levenshtein distance-based algorithm instead of fuzzy": "Use Levenshtein distance-based algorithm instead of fuzzy",
|
|
||||||
"System prompt": "System prompt",
|
|
||||||
"12h AM/PM": "12h AM/PM",
|
|
||||||
"Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)",
|
|
||||||
"Critical warning": "Critical warning",
|
|
||||||
"User agent (for services that require it)": "User agent (for services that require it)",
|
|
||||||
"Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.",
|
|
||||||
"Note: turning off can hurt readability": "Note: turning off can hurt readability",
|
|
||||||
"Workspaces shown": "Workspaces shown",
|
|
||||||
"Dark/Light toggle": "Dark/Light toggle",
|
|
||||||
"Dock": "Dock",
|
|
||||||
"Weather": "Weather",
|
|
||||||
"Pinned on startup": "Pinned on startup",
|
|
||||||
"Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience": "Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience",
|
|
||||||
"Appearance": "Appearance",
|
|
||||||
"Always show numbers": "Always show numbers",
|
|
||||||
"Buttons": "Buttons",
|
|
||||||
"Keyboard toggle": "Keyboard toggle",
|
|
||||||
"Scale (%)": "Scale (%)",
|
|
||||||
"Overview": "Overview",
|
|
||||||
"Rows": "Rows",
|
|
||||||
"Borderless": "Borderless",
|
|
||||||
"Screenshot tool": "Screenshot tool",
|
|
||||||
"Number show delay when pressing Super (ms)": "Number show delay when pressing Super (ms)",
|
|
||||||
"Timeout (ms)": "Timeout (ms)",
|
|
||||||
"Show app icons": "Show app icons",
|
|
||||||
"Workspaces": "Workspaces",
|
|
||||||
"Columns": "Columns",
|
|
||||||
"On-screen display": "On-screen display",
|
|
||||||
"Screen snip": "Screen snip",
|
|
||||||
"Mic toggle": "Mic toggle",
|
|
||||||
"Hover to reveal": "Hover to reveal",
|
|
||||||
"Bar": "Bar",
|
|
||||||
"Show background": "Show background",
|
|
||||||
"Show regions of potential interest": "Show regions of potential interest",
|
|
||||||
"Color picker": "Color picker",
|
|
||||||
"Help & Support": "Help & Support",
|
|
||||||
"Discussions": "Discussions",
|
|
||||||
"Color generation": "Color generation",
|
|
||||||
"Dotfiles": "Dotfiles",
|
|
||||||
"Distro": "Distro",
|
|
||||||
"Privacy Policy": "Privacy Policy",
|
|
||||||
"Documentation": "Documentation",
|
|
||||||
"Shell & utilities theming must also be enabled": "Shell & utilities theming must also be enabled",
|
|
||||||
"illogical-impulse": "illogical-impulse",
|
|
||||||
"Donate": "Donate",
|
|
||||||
"Terminal": "Terminal",
|
|
||||||
"Shell & utilities": "Shell & utilities",
|
|
||||||
"Qt apps": "Qt apps",
|
|
||||||
"Report a Bug": "Report a Bug",
|
|
||||||
"Issues": "Issues",
|
|
||||||
"Drag or click a region • LMB: Copy • RMB: Edit": "Drag or click a region • LMB: Copy • RMB: Edit",
|
|
||||||
"Current model: %1\nSet it with %2model MODEL": "Current model: %1\nSet it with %2model MODEL",
|
|
||||||
"Message the model... \"%1\" for commands": "Message the model... \"%1\" for commands",
|
|
||||||
"No API key set for %1": "No API key set for %1",
|
|
||||||
"Loaded the following system prompt\n\n---\n\n%1": "Loaded the following system prompt\n\n---\n\n%1",
|
|
||||||
"%1 | Right-click to configure": "%1 | Right-click to configure",
|
|
||||||
"API key set for %1": "API key set for %1",
|
|
||||||
"Online via %1 | %2's model": "Online via %1 | %2's model",
|
|
||||||
"Current API endpoint: %1\nSet it with %2mode PROVIDER": "Current API endpoint: %1\nSet it with %2mode PROVIDER",
|
|
||||||
"Go to source (%1)": "Go to source (%1)",
|
|
||||||
"Temperature set to %1": "Temperature set to %1",
|
|
||||||
"To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For %1:\n\n**Link**: %2\n\n%3": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For %1:\n\n**Link**: %2\n\n%3",
|
|
||||||
"Enter tags, or \"%1\" for commands": "Enter tags, or \"%1\" for commands",
|
|
||||||
"%1 queries pending": "%1 queries pending",
|
|
||||||
"API key:\n\n```txt\n%1\n```": "API key:\n\n```txt\n%1\n```",
|
|
||||||
"Uptime: %1": "Uptime: %1",
|
|
||||||
"%1 Safe Storage": "%1 Safe Storage",
|
|
||||||
"%1 does not require an API key": "%1 does not require an API key",
|
|
||||||
"Temperature: %1": "Temperature: %1",
|
|
||||||
"Model set to %1": "Model set to %1",
|
|
||||||
"Page %1": "Page %1",
|
|
||||||
"Local Ollama model | %1": "Local Ollama model | %1",
|
|
||||||
"The current system prompt is\n\n---\n\n%1": "The current system prompt is\n\n---\n\n%1",
|
|
||||||
"Unknown function call: %1": "Unknown function call: %1",
|
|
||||||
"%1 notifications": "%1 notifications",
|
|
||||||
"Load chat from %1": "Load chat from %1",
|
|
||||||
"Load prompt from %1": "Load prompt from %1",
|
|
||||||
"Save chat to %1": "Save chat to %1",
|
|
||||||
"Weather Service": "Weather Service",
|
|
||||||
"Cannot find a GPS service. Using the fallback method instead.": "Cannot find a GPS service. Using the fallback method instead.",
|
|
||||||
"Critically low battery": "Critically low battery",
|
|
||||||
"Select output device": "Select output device",
|
|
||||||
"Code saved to file": "Code saved to file",
|
|
||||||
"Online models disallowed\n\nControlled by `policies.ai` config option": "Online models disallowed\n\nControlled by `policies.ai` config option",
|
|
||||||
"Scroll to change volume": "Scroll to change volume",
|
|
||||||
"Elements": "Elements",
|
|
||||||
"%1 • %2 tasks": "%1 • %2 tasks",
|
|
||||||
"Download complete": "Download complete",
|
|
||||||
"Please charge!\nAutomatic suspend triggers at %1": "Please charge!\nAutomatic suspend triggers at %1",
|
|
||||||
"Cloudflare WARP": "Cloudflare WARP",
|
|
||||||
"Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)",
|
|
||||||
"Scroll to change brightness": "Scroll to change brightness",
|
|
||||||
"Connection failed. Please inspect manually with the <tt>warp-cli</tt> command": "Connection failed. Please inspect manually with the <tt>warp-cli</tt> command",
|
|
||||||
"Select input device": "Select input device",
|
|
||||||
"Registration failed. Please inspect manually with the <tt>warp-cli</tt> command": "Registration failed. Please inspect manually with the <tt>warp-cli</tt> command",
|
|
||||||
"Consider plugging in your device": "Consider plugging in your device",
|
|
||||||
"Low battery": "Low battery",
|
|
||||||
"Saved to %1": "Saved to %1",
|
|
||||||
"Sunset": "Sunset",
|
|
||||||
"UV Index": "UV Index",
|
|
||||||
"Humidity": "Humidity",
|
|
||||||
"Wind": "Wind",
|
|
||||||
"Sunrise": "Sunrise",
|
|
||||||
"Pressure": "Pressure",
|
|
||||||
"Visibility": "Visibility",
|
|
||||||
"Precipitation": "Precipitation"
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
|
||||||
* text=auto
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
- Please, please, please, make multiple PRs if you have many features/fixes, and don't shove your personal changes along with the PR, including changed defaults
|
||||||
|
- We can accept features that we do not personally want, but in that case we will ask you to make it configurable/optionally loaded.
|
||||||
|
- If you want to start working on something big to contribute, it might be a good idea to ask first to not waste your effort (but if you've already done it for yourself, it doesn't hurt to submit).
|
||||||
|
|
||||||
|
# Code details
|
||||||
|
|
||||||
|
## Contributing to i18n
|
||||||
|
|
||||||
|
For contributing in translation (i18n) for Quickshell, see also `dots/.config/quickshell/ii/translations/tools`.
|
||||||
|
|
||||||
|
## Dynamic loading
|
||||||
|
|
||||||
|
- If something's not always necessary, especially when guarded by a config option to enable/disable, put it in a `Loader`. One tip with `Loader`s is sometimes you will need to declare positioning properties (like `anchors`) in the `Loader`, not the `sourceComponent`.
|
||||||
|
|
||||||
|
## Practical concerns
|
||||||
|
|
||||||
|
- Make sure what you add does not require significant resources for a minor purpose or harm usability just for the sake of looking nice. The dotfiles must remain practical for daily driving.
|
||||||
|
- If there is something really fancy and impractical anyway, add a config option for it and make sure it's disabled by default.
|
||||||
|
|
||||||
|
# Setting up
|
||||||
|
|
||||||
|
The following instruction assumes that you have an Arch(-based) Linux system.
|
||||||
|
|
||||||
|
## Complete
|
||||||
|
|
||||||
|
_Might not be necessary depending on what you change, but this is recommended._
|
||||||
|
|
||||||
|
- [Install](https://ii.clsty.link/en/ii-qs/01setup/) the dotfiles (if you don't wanna replace your stuff completely, do it on a new user).
|
||||||
|
- Make changes, copy changes to a fork, create PR.
|
||||||
|
|
||||||
|
## Partially working shell
|
||||||
|
|
||||||
|
_Most stuff in the shell will work but not everything._
|
||||||
|
|
||||||
|
- Install Hyprland and the development version of Quickshell (`yay -S hyprland quickshell-git`).
|
||||||
|
- Copy `dots/.config/quickshell` folder to your home directory.
|
||||||
|
|
||||||
|
## Extra setup for Quickshell
|
||||||
|
- Quickshell-specific LSP setup: Run `touch ~/.config/quickshell/ii/.qmlls.ini` for proper LSP support.
|
||||||
|
- Hint for VSCode: Get the official "Qt Qml" extension, go to its settings and change custom exe path to `/usr/bin/qmlls6`.
|
||||||
|
|
||||||
|
## Python
|
||||||
|
If your changes involves using python package or script, please use the virtual environment created by uv as described in `sdata/uv/README.md`.
|
||||||
|
|
||||||
|
# Running
|
||||||
|
|
||||||
|
- Launch Hyprland (not the "uwsm-managed" one)
|
||||||
|
- For the shell:
|
||||||
|
- Open `~/.config/quickshell/ii` in your code editor.
|
||||||
|
- In a terminal run `pkill qs; qs -c ii` to start the shell in the terminal (for logs).
|
||||||
|
- Make edits in the opened folder. Changes are reloaded live.
|
||||||
@@ -4,21 +4,21 @@ labels: ["ISSUE"]
|
|||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "**Welcome to submit a new issue!**\n- It takes only 3 steps, so please be patient :)\n- Tip: If your issue is not a feature request, and it does not fit into the following form, for example \"how can I edit some widget\", please use [Discussions](https://github.com/end-4/dots-hyprland/discussions) instead."
|
value: "**Welcome to submit a new issue!**\n- Please search in [existing issues](https://github.com/end-4/dots-hyprland/issues?q=is%3Aissue) before continue.\n- It takes only 3 steps, so please be patient :)\n- NOTE 1: If your issue is not a feature request, and it does not fit into the following form, for example \"how can I edit some widget\", please use [Discussions](https://github.com/end-4/dots-hyprland/discussions) instead.\n- NOTE 2: If your problem is distro specific and you do not use Arch(-based) distros, plesae submit [Discussion at Extra Distros](https://github.com/end-4/dots-hyprland/discussions/new?category=extra-distros) instead."
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: "Step 1. Before you submit"
|
label: "Step 1. Before you submit"
|
||||||
description: "Hint: The 2nd and 3rd checkbox is **not** forcely required as you may have failed to do so."
|
description: "Hint: The 2nd and 3rd checkbox is **not** forcely required as you may have failed to do so."
|
||||||
options:
|
options:
|
||||||
- label: I have read the [Troubleshooting](https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/04troubleshooting/) and [Usage](https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/02usage/) pages.
|
- label: I have read the [Troubleshooting](https://ii.clsty.link/en/ii-qs/04troubleshooting/) and [Usage](https://ii.clsty.link/en/ii-qs/02usage/) pages.
|
||||||
required: true
|
required: true
|
||||||
- label: I've successfully updated to the latest version following the [guidance](https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/01setup/#updating).
|
- label: I've successfully updated to the latest version following the [guidance](https://ii.clsty.link/en/ii-qs/01setup/#updating).
|
||||||
required: false # Not required cuz user may have failed to do so
|
required: false # Not required cuz user may have failed to do so
|
||||||
- label: I've successfully updated the system packages to the latest.
|
- label: I've successfully updated the system packages to the latest.
|
||||||
required: false # Not required cuz user may have failed to do so
|
required: false # Not required cuz user may have failed to do so
|
||||||
- label: I've ticked the checkboxes without reading their contents
|
- label: I've ticked the checkboxes without reading their contents
|
||||||
required: false # Obviously
|
required: false # Obviously
|
||||||
|
# TODO: Use GitHub Action to auto add folding tag if the log contains more than 15 lines, instead of tell user to "paste here" cuz many users actually does not know its meaning (It's also not convenient anyway).
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "Step 2. Quick diagnose info"
|
label: "Step 2. Quick diagnose info"
|
||||||
@@ -33,7 +33,7 @@ body:
|
|||||||
**Tips for the following Step 3**
|
**Tips for the following Step 3**
|
||||||
1. Use `LANG=C LC_ALL=C` to get the output of a command in English, eg. `LANG=C LC_ALL=C date` displays time in English.
|
1. Use `LANG=C LC_ALL=C` to get the output of a command in English, eg. `LANG=C LC_ALL=C date` displays time in English.
|
||||||
2. If it throws errors, **PLEASE**, attach logs and describe in detail if possible.
|
2. If it throws errors, **PLEASE**, attach logs and describe in detail if possible.
|
||||||
- Bar and widgets not showing? run `pkill agsv1; agsv1` for logs.
|
- Bar and widgets not showing? run `pkill qs; qs -c ii` for logs.
|
||||||
- Installation failed? Run installation again for logs.
|
- Installation failed? Run installation again for logs.
|
||||||
- You may use more code blocks when needed.
|
- You may use more code blocks when needed.
|
||||||
3. In case you are confused, the `<details>`, `<summary>`, `</summary>`, `</details>` are HTML tags for folding the logs (typically very long) inside. Please do not touch them (unless you know what you are doing).
|
3. In case you are confused, the `<details>`, `<summary>`, `</summary>`, `</details>` are HTML tags for folding the logs (typically very long) inside. Please do not touch them (unless you know what you are doing).
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ labels: ["FEATURE"]
|
|||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "NOTE: Please write in **English**."
|
value: "NOTE:\n- Please search in [existing issues](https://github.com/end-4/dots-hyprland/issues?q=is%3Aissue) before continue.\n- Please write in **English**."
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
<a href="https://discord.gg/GtdRBXgMwq"> <img alt="Dynamic JSON Badge" src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscordapp.com%2Fapi%2Finvites%2FGtdRBXgMwq%3Fwith_counts%3Dtrue&query=approximate_member_count&style=for-the-badge&logo=discord&logoColor=D9E0EE&label=discord&labelColor=%231E202B&color=86dbc0&link=https%3A%2F%2Fdiscord.gg%2FGtdRBXgMwq"> </a>
|
<a href="https://discord.gg/GtdRBXgMwq"> <img alt="Dynamic JSON Badge" src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscordapp.com%2Fapi%2Finvites%2FGtdRBXgMwq%3Fwith_counts%3Dtrue&query=approximate_member_count&style=for-the-badge&logo=discord&logoColor=D9E0EE&label=discord&labelColor=%231E202B&color=86dbc0&link=https%3A%2F%2Fdiscord.gg%2FGtdRBXgMwq"> </a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -26,10 +26,13 @@
|
|||||||
- **Transparent installation**: Every command is shown before it's run
|
- **Transparent installation**: Every command is shown before it's run
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
<summary>Installation instructions</summary>
|
<summary>Installation (illogical-impulse Quickshell)</summary>
|
||||||
|
|
||||||
- See the [Wiki](https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/01setup/)
|
- _If you're new to Linux and decide to use Hyprland, you're in for a tough ride._
|
||||||
- **Default keybinds**: Should be somewhat familiar to Windows or GNOME users.
|
- Just run `bash <(curl -s https://ii.clsty.link/get)`
|
||||||
|
- Or, clone this repo and run `./setup install`
|
||||||
|
- See [document](https://ii.clsty.link/en/ii-qs/01setup/) for details.
|
||||||
|
- **Default keybinds**: Should be somewhat familiar to Windows or GNOME users. Important ones:
|
||||||
- `Super`+`/` = keybind list
|
- `Super`+`/` = keybind list
|
||||||
- `Super`+`Enter` = terminal
|
- `Super`+`Enter` = terminal
|
||||||
- If for whatever reason the keybind list widget does not work, here's an image:
|
- If for whatever reason the keybind list widget does not work, here's an image:
|
||||||
@@ -44,12 +47,11 @@
|
|||||||
|
|
||||||
| Software | Purpose |
|
| Software | Purpose |
|
||||||
| ------------- | ------------- |
|
| ------------- | ------------- |
|
||||||
| [Hyprland](https://github.com/hyprwm/hyprland) | The compositor (for noobs, you can just call it a window manager) |
|
| [Hyprland](https://github.com/hyprwm/hyprland) | The compositor (manages and renders windows) |
|
||||||
| [Quickshell](https://quickshell.outfoxxed.me/) | A QtQuick-based widget system, responsible for the status bar, sidebars, etc. |
|
| [Quickshell](https://quickshell.outfoxxed.me/) | A QtQuick-based widget system, used for the status bar, sidebars, etc. |
|
||||||
|
|
||||||
|
- For the full list of dependencies, see the [sdata/dist-arch folder](https://github.com/end-4/dots-hyprland/tree/main/sdata/dist-arch)
|
||||||
|
- THERE IS NO WAYBAR STOP FUCKING CALLING EVERY BAR WAYBAR
|
||||||
- For a more comprehensive list of dependencies, see [scriptdata/dependencies.conf](https://github.com/end-4/dots-hyprland/blob/main/scriptdata/dependencies.conf)
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -58,41 +60,78 @@
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
_Note: **THERE IS NO FUCKING WAYBAR**_
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h2>• screenshots •</h2>
|
<h2>• screenshots •</h2>
|
||||||
<h3></h3>
|
<h3></h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table style="border-collapse: collapse;">
|
<div align="center">
|
||||||
<tr>
|
<img src="assets/illogical-impulse.svg" alt="illogical-impulse logo" style="float:left; width:400;">
|
||||||
<td width="25%">
|
</div>
|
||||||
<img src=".github/assets/illogical-impulse.svg" alt="illogical-impulse logo" style="float:left; width:100%;">
|
|
||||||
</td>
|
|
||||||
<td width="75%">
|
|
||||||
<i>latest and only style that I actively use. Other past styles are still there for your viewing pleasure and not actual use, but code is still available, see below.</i>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
### illogical-impulse<sup>Quickshell</sup>
|
### illogical-impulse<sup>Quickshell</sup>
|
||||||
|
|
||||||
|
This is the latest and only supported style. Other stuff are still there mostly for viewing pleasure and not actual use, but code is still available, see below.
|
||||||
|
|
||||||
Widget system: Quickshell | Support: Yes
|
Widget system: Quickshell | Support: Yes
|
||||||
|
|
||||||
[Showcase video](https://www.youtube.com/watch?v=RPwovTInagE)
|
[Showcase video](https://www.youtube.com/watch?v=RPwovTInagE)
|
||||||
|
|
||||||
| AI, settings app | Some widgets |
|
| AI, settings app | Some widgets |
|
||||||
|:---|:---------------|
|
|:---|:---------------|
|
||||||
|  |  |
|
| <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/ea0154a1-e984-4bb6-a424-23247cefe3c6" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/6eba0d57-2606-4cea-8993-e6f169e82e70" /> |
|
||||||
| Window management | Weeb power |
|
| Window management | Weeb power |
|
||||||
|  |  |
|
| <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/e77a7c96-1905-4126-a2a0-434f818825a2" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/c8544e99-8881-477f-b83a-d6e35c0184a1" /> |
|
||||||
|
|
||||||
### illogical-impulse<sup>AGS</sup> <sub>(Deprecated)</sub>
|
### Other styles: Available at the end of the readme.
|
||||||
|
|
||||||
Widget system: AGS | Support: Limited, no new features
|
<div align="center">
|
||||||
|
<h2>• thank you •</h2>
|
||||||
|
<h3></h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- [@clsty](https://github.com/clsty) for making the dotfiles accessible by taking care of the install script and many other things
|
||||||
|
- [@midn8hustlr](https://github.com/midn8hustlr) for greatly improving the color generation system
|
||||||
|
- [@outfoxxed](https://github.com/outfoxxed/) for being extremely supportive in my Quickshell journey
|
||||||
|
- Quickshell: [Soramane](https://github.com/caelestia-dots/shell/), [FridayFaerie](https://github.com/FridayFaerie/quickshell), [nydragon](https://github.com/nydragon/nysh)
|
||||||
|
- AGS: [Aylur](https://github.com/Aylur/dotfiles/tree/ags-pre-ts), [kotontrion](https://github.com/kotontrion/dotfiles)
|
||||||
|
- EWW: [fufexan](https://github.com/fufexan/dotfiles)
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<h2>• stonks •</h2>
|
||||||
|
<h3></h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- If you would like to give me some milk😳 (i dont drink coffee): https://github.com/sponsors/end-4
|
||||||
|
- Virtual tentacle cat website points chart
|
||||||
|
|
||||||
|
[](https://starchart.cc/end-4/dots-hyprland)
|
||||||
|
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<h2>• inspirations/copying •</h2>
|
||||||
|
<h3></h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- Inspiration: osu!lazer, Windows 11, Material Design 3, AvdanOS (concept)
|
||||||
|
- Copying: The license allows you to. Personally I have absolutely no problem with others redistributing/recreating my work. There's no "stealing" (maybe unless you loudly do weird stuff and violate the license) <sub>(some people actually had to ask smh)</sub>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<h2>• old, UNSUPPORTED stuff •</h2>
|
||||||
|
<h3></h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- Source for illogical-impulse AGS available in the `ii-ags` branch, others in the `archive` branch.
|
||||||
|
- The list goes from newest to the oldest, and the code quality is worse the older a style is
|
||||||
|
- No bug fix or official support will be provided.
|
||||||
|
|
||||||
|
### illogical-impulse<sup>AGS</sup>
|
||||||
|
|
||||||
|
Widget system: AGS | Support: No
|
||||||
|
|
||||||
| AI | Common widgets |
|
| AI | Common widgets |
|
||||||
|:---|:---------------|
|
|:---|:---------------|
|
||||||
@@ -100,11 +139,6 @@ Widget system: AGS | Support: Limited, no new features
|
|||||||
| Window management | Weeb power |
|
| Window management | Weeb power |
|
||||||
|  |  |
|
|  |  |
|
||||||
|
|
||||||
### Very old stuff
|
|
||||||
|
|
||||||
- Source code not likely to work but still available in the [`archive`](https://github.com/end-4/dots-hyprland/tree/archive) branch. Extremely spaghetti.
|
|
||||||
- Click image for a presentation video
|
|
||||||
|
|
||||||
#### m3ww
|
#### m3ww
|
||||||
|
|
||||||
Widget system: EWW | Support: No, dead
|
Widget system: EWW | Support: No, dead
|
||||||
@@ -115,7 +149,7 @@ Widget system: AGS | Support: Limited, no new features
|
|||||||
|
|
||||||
#### NovelKnock
|
#### NovelKnock
|
||||||
|
|
||||||
Widget system: EWW | Support: No, dead
|
Widget system: EWW | Support: No
|
||||||
|
|
||||||
<a href="https://streamable.com/7vo61k">
|
<a href="https://streamable.com/7vo61k">
|
||||||
<img src="https://github.com/end-4/dots-hyprland/assets/97237370/42903d03-bf6f-49d4-be7f-dd77e6cb389d" alt="Desktop Preview">
|
<img src="https://github.com/end-4/dots-hyprland/assets/97237370/42903d03-bf6f-49d4-be7f-dd77e6cb389d" alt="Desktop Preview">
|
||||||
@@ -123,7 +157,7 @@ Widget system: AGS | Support: Limited, no new features
|
|||||||
|
|
||||||
#### Hybrid
|
#### Hybrid
|
||||||
|
|
||||||
Widget system: EWW | Support: No, dead
|
Widget system: EWW | Support: No
|
||||||
|
|
||||||
<a href="https://streamable.com/4oogot">
|
<a href="https://streamable.com/4oogot">
|
||||||
<img src="https://github.com/end-4/dots-hyprland/assets/97237370/190deb1e-f6f5-46ce-8cf0-9b39944c079d" alt="click the circles!">
|
<img src="https://github.com/end-4/dots-hyprland/assets/97237370/190deb1e-f6f5-46ce-8cf0-9b39944c079d" alt="click the circles!">
|
||||||
@@ -131,44 +165,9 @@ Widget system: AGS | Support: Limited, no new features
|
|||||||
|
|
||||||
#### Windoes
|
#### Windoes
|
||||||
|
|
||||||
Widget system: EWW | Support: No, dead
|
Widget system: EWW | Support: No
|
||||||
|
|
||||||
<a href="https://streamable.com/5qx614">
|
<a href="https://streamable.com/5qx614">
|
||||||
<img src="https://github.com/end-4/dots-hyprland/assets/97237370/b15317b1-f295-49f5-b90c-fb6328b8d886" alt="Desktop Preview">
|
<img src="https://github.com/end-4/dots-hyprland/assets/97237370/b15317b1-f295-49f5-b90c-fb6328b8d886" alt="Desktop Preview">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<h2>• thank you •</h2>
|
|
||||||
<h3></h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
- [@clsty](https://github.com/clsty) for making my work accessible by taking care of the install script and many other things
|
|
||||||
- [@midn8hustlr](https://github.com/midn8hustlr) for greatly improving the color generation system
|
|
||||||
- [@outfoxxed](https://github.com/outfoxxed/) for being extremely supportive in my Quickshell journey
|
|
||||||
- Quickshell: [Soramane](https://github.com/caelestia-dots/shell/), [FridayFaerie](https://github.com/FridayFaerie/quickshell), [nydragon](https://github.com/nydragon/nysh)
|
|
||||||
- AGS: [Aylur's config](https://github.com/Aylur/dotfiles/tree/ags-pre-ts), [kotontrion's config](https://github.com/kotontrion/dotfiles)
|
|
||||||
- EWW: [fufexan's config](https://github.com/fufexan/dotfiles) (he thanks more people there btw)
|
|
||||||
- AI bots for providing useful examples
|
|
||||||
|
|
||||||
<sup><sup><sup><sup><sup>[@tokyobot](https://github.com/tokyob0t) for saying hi</sup></sup></sup></sup></sup>
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<h2>• stonks •</h2>
|
|
||||||
<h3></h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
- Funny useful number: https://github.com/sponsors/end-4
|
|
||||||
- Funny useless number:
|
|
||||||
|
|
||||||
[](https://starchart.cc/end-4/dots-hyprland)
|
|
||||||
|
|
||||||
- *Jokes aside i do appreciate both*
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<h2>• inspirations/copying •</h2>
|
|
||||||
<h3></h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
- Inspiration: osu!lazer, Windows 11, Material Design 3, AvdanOS (concept)
|
|
||||||
- Copying: The license allows you to. Personally I have absolutely no problem with others redistributing/recreating my work. There's no "stealing" (unless you do weird stuff and violate the license). (this note is here because some people actually asked)
|
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
name: Close issues when the "ticked without reading" checkbox is checked
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
detect-and-close:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Detect checked "ticked without reading" checkbox, comment, close and lock
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
OWNER: ${{ github.repository_owner }}
|
||||||
|
REPO: ${{ github.event.repository.name }}
|
||||||
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||||
|
ISSUE_BODY: ${{ toJson(github.event.issue.body) }}
|
||||||
|
ISSUE_USER: ${{ github.event.issue.user.login }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Normalize the JSON-encoded body into plain text
|
||||||
|
BODY=$(printf '%s' "$ISSUE_BODY" | sed -E 's/^"(.*)"$/\1/' | sed 's/\\"/"/g' | sed 's/\\n/\n/g')
|
||||||
|
|
||||||
|
echo "Checking issue #${ISSUE_NUMBER} for the target checked checkbox..."
|
||||||
|
# Use -- to stop option parsing so the leading - in the pattern isn't treated as an option
|
||||||
|
if printf '%s' "$BODY" | grep -Fiq -- "- [x] I've ticked the checkboxes without reading their contents"; then
|
||||||
|
echo "Target checkbox is checked. Proceeding to comment, close and lock the issue."
|
||||||
|
|
||||||
|
# --- Get issue node id via GraphQL ---
|
||||||
|
QUERY='query($owner: String!, $name: String!, $number: Int!) { repository(owner: $owner, name: $name) { issue(number: $number) { id } } }'
|
||||||
|
GET_ID_PAYLOAD=$(jq -n --arg q "$QUERY" --arg owner "$OWNER" --arg name "$REPO" --argjson number "$ISSUE_NUMBER" '{query:$q, variables:{owner:$owner, name:$name, number:$number}}')
|
||||||
|
|
||||||
|
echo "GraphQL: fetching issue node id..."
|
||||||
|
RES=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" -H "Content-Type: application/json" -d "$GET_ID_PAYLOAD" https://api.github.com/graphql)
|
||||||
|
echo "GraphQL response (get id):"
|
||||||
|
printf '%s\n' "$RES"
|
||||||
|
|
||||||
|
ISSUE_ID=$(printf '%s' "$RES" | jq -r '.data.repository.issue.id // empty')
|
||||||
|
|
||||||
|
if [ -z "$ISSUE_ID" ]; then
|
||||||
|
echo "Failed to get issue id from GraphQL response. Aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Issue node id: $ISSUE_ID"
|
||||||
|
|
||||||
|
# --- Post a comment to the issue ---
|
||||||
|
COMMENT_BODY="Hi @${ISSUE_USER} — I noticed you checked \"I've ticked the checkboxes without reading their contents\" in the issue template. To help others assist you effectively, please read the template and provide the requested diagnostic information (Step 2 & Step 3). I will close this issue now. If you create a new issue with the required information, we can re-evaluate. Thank you!"
|
||||||
|
MUT_ADD_COMMENT='mutation($id: ID!, $body: String!) { addComment(input: {subjectId: $id, body: $body}) { clientMutationId } }'
|
||||||
|
ADD_COMMENT_PAYLOAD=$(jq -n --arg q "$MUT_ADD_COMMENT" --arg id "$ISSUE_ID" --arg body "$COMMENT_BODY" '{query:$q, variables:{id:$id, body:$body}}')
|
||||||
|
|
||||||
|
echo "GraphQL: adding comment..."
|
||||||
|
RES_COMMENT=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" -H "Content-Type: application/json" -d "$ADD_COMMENT_PAYLOAD" https://api.github.com/graphql)
|
||||||
|
echo "GraphQL response (add comment):"
|
||||||
|
printf '%s\n' "$RES_COMMENT"
|
||||||
|
|
||||||
|
ERR_COMMENT=$(printf '%s' "$RES_COMMENT" | jq -r '.errors[]?.message // empty')
|
||||||
|
if [ -n "$ERR_COMMENT" ]; then
|
||||||
|
echo "addComment error: $ERR_COMMENT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Comment posted."
|
||||||
|
|
||||||
|
# --- Attempt to close via GraphQL updateIssue ---
|
||||||
|
MUT_UPDATE_ISSUE='mutation($id: ID!) { updateIssue(input: {id: $id, state: CLOSED, stateReason: NOT_PLANNED}) { issue { number, state, stateReason } } }'
|
||||||
|
UPDATE_PAYLOAD=$(jq -n --arg q "$MUT_UPDATE_ISSUE" --arg id "$ISSUE_ID" '{query:$q, variables:{id:$id}}')
|
||||||
|
|
||||||
|
echo "GraphQL: updating issue (close with NOT_PLANNED)..."
|
||||||
|
RES_UPDATE=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" -H "Content-Type: application/json" -d "$UPDATE_PAYLOAD" https://api.github.com/graphql)
|
||||||
|
echo "GraphQL response (update issue):"
|
||||||
|
printf '%s\n' "$RES_UPDATE"
|
||||||
|
|
||||||
|
ERR_UPDATE=$(printf '%s' "$RES_UPDATE" | jq -r '.errors[]?.message // empty')
|
||||||
|
UPDATED_STATE=$(printf '%s' "$RES_UPDATE" | jq -r '.data.updateIssue.issue.state // empty')
|
||||||
|
UPDATED_REASON=$(printf '%s' "$RES_UPDATE" | jq -r '.data.updateIssue.issue.stateReason // empty')
|
||||||
|
|
||||||
|
CLOSED_OK=false
|
||||||
|
|
||||||
|
if [ -n "$ERR_UPDATE" ]; then
|
||||||
|
echo "GraphQL updateIssue returned errors: $ERR_UPDATE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$UPDATED_STATE" = "CLOSED" ]; then
|
||||||
|
echo "Issue closed via GraphQL: state=$UPDATED_STATE, stateReason=$UPDATED_REASON"
|
||||||
|
CLOSED_OK=true
|
||||||
|
else
|
||||||
|
echo "GraphQL update did not confirm the issue is closed. Falling back to REST API PATCH to ensure the issue is closed."
|
||||||
|
|
||||||
|
# REST fallback to close the issue with state_reason "not_planned"
|
||||||
|
REST_PAYLOAD=$(jq -n --arg state "closed" --arg sr "not_planned" '{state:$state, state_reason:$sr}')
|
||||||
|
echo "REST: PATCH /repos/$OWNER/$REPO/issues/$ISSUE_NUMBER payload: $REST_PAYLOAD"
|
||||||
|
RES_REST=$(curl -s -w "\n%{http_code}" -X PATCH \
|
||||||
|
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$REST_PAYLOAD" \
|
||||||
|
"https://api.github.com/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER")
|
||||||
|
|
||||||
|
HTTP_STATUS=$(printf '%s' "$RES_REST" | tail -n1)
|
||||||
|
RESP_BODY=$(printf '%s' "$RES_REST" | sed '$d')
|
||||||
|
|
||||||
|
echo "REST response body:"
|
||||||
|
printf '%s\n' "$RESP_BODY"
|
||||||
|
echo "REST HTTP status: $HTTP_STATUS"
|
||||||
|
|
||||||
|
if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ]; then
|
||||||
|
CLOSED_STATE=$(printf '%s' "$RESP_BODY" | jq -r '.state // empty')
|
||||||
|
CLOSED_REASON=$(printf '%s' "$RESP_BODY" | jq -r '.state_reason // empty')
|
||||||
|
echo "Issue closed via REST: state=$CLOSED_STATE, state_reason=$CLOSED_REASON"
|
||||||
|
if [ "$CLOSED_STATE" = "closed" ]; then
|
||||||
|
CLOSED_OK=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "REST fallback failed to close the issue. See REST response above."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Attempt to lock the conversation (GraphQL first, then REST fallback) ---
|
||||||
|
if [ "$CLOSED_OK" = "true" ]; then
|
||||||
|
echo "Attempting to lock the conversation via GraphQL with reason NO_REASON..."
|
||||||
|
|
||||||
|
MUT_LOCK='mutation($id: ID!, $reason: LockReason) { lockLockable(input:{lockableId:$id, lockReason:$reason}) { clientMutationId } }'
|
||||||
|
LOCK_PAYLOAD=$(jq -n --arg q "$MUT_LOCK" --arg id "$ISSUE_ID" --arg reason "NO_REASON" '{query:$q, variables:{id:$id, reason:$reason}}')
|
||||||
|
|
||||||
|
RES_LOCK=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" -H "Content-Type: application/json" -d "$LOCK_PAYLOAD" https://api.github.com/graphql)
|
||||||
|
echo "GraphQL response (lock):"
|
||||||
|
printf '%s\n' "$RES_LOCK"
|
||||||
|
|
||||||
|
LOCK_ERR=$(printf '%s' "$RES_LOCK" | jq -r '.errors[]?.message // empty')
|
||||||
|
|
||||||
|
if [ -n "$LOCK_ERR" ]; then
|
||||||
|
echo "GraphQL lockLockable returned errors: $LOCK_ERR"
|
||||||
|
echo "Falling back to REST API to lock the conversation (no explicit reason)."
|
||||||
|
|
||||||
|
# REST fallback to lock the issue (no lock_reason to indicate "no reason")
|
||||||
|
RES_REST_LOCK=$(curl -s -w "\n%{http_code}" -X PUT \
|
||||||
|
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
"https://api.github.com/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER/lock" -d '{}')
|
||||||
|
|
||||||
|
HTTP_STATUS_LOCK=$(printf '%s' "$RES_REST_LOCK" | tail -n1)
|
||||||
|
RESP_BODY_LOCK=$(printf '%s' "$RES_REST_LOCK" | sed '$d')
|
||||||
|
|
||||||
|
echo "REST lock response body:"
|
||||||
|
printf '%s\n' "$RESP_BODY_LOCK"
|
||||||
|
echo "REST lock HTTP status: $HTTP_STATUS_LOCK"
|
||||||
|
|
||||||
|
if [ "$HTTP_STATUS_LOCK" -ge 200 ] && [ "$HTTP_STATUS_LOCK" -lt 300 ]; then
|
||||||
|
echo "Issue conversation locked via REST (no explicit reason)."
|
||||||
|
else
|
||||||
|
echo "REST fallback failed to lock the conversation. See REST response above."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Lock via GraphQL succeeded (or returned no errors)."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Issue was not successfully closed; skipping lock."
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Checkbox not present/checked. Nothing to do."
|
||||||
|
fi
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
name: Comment on Discussion When sdata/dist-arch/ Changes
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- "sdata/dist-arch/**"
|
||||||
|
- "!sdata/dist-arch/README.md"
|
||||||
|
# workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
comment_on_discussion:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Create comment on discussion #2140
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
DISCUSSION_NUMBER: 2140
|
||||||
|
# https://docs.github.com/en/graphql/guides/using-the-graphql-api-for-discussions
|
||||||
|
# https://docs.github.com/en/graphql/reference/mutations#adddiscussioncomment
|
||||||
|
run: |
|
||||||
|
MESSAGE="**Auto notification:**\n"
|
||||||
|
MESSAGE+="Directory \`sdata/dist-arch\` has been updated.\n"
|
||||||
|
MESSAGE+="Commit HASH: ${{ github.sha }}\n"
|
||||||
|
MESSAGE+="Commit message: ${{ github.event.head_commit.message }}"
|
||||||
|
REPO_OWNER="${{ github.repository_owner }}"
|
||||||
|
REPO_NAME="${{ github.event.repository.name }}"
|
||||||
|
|
||||||
|
DISCUSSION_NODE_ID=$(gh api graphql -f query='
|
||||||
|
query {
|
||||||
|
repository( owner: "'${REPO_OWNER}'", name: "'${REPO_NAME}'" )
|
||||||
|
{ discussion(number: '${DISCUSSION_NUMBER}') { id } }
|
||||||
|
}' | \
|
||||||
|
jq -r '.data.repository.discussion.id')
|
||||||
|
gh api graphql -f query='
|
||||||
|
mutation {
|
||||||
|
addDiscussionComment(input:{
|
||||||
|
discussionId: "'$DISCUSSION_NODE_ID'",
|
||||||
|
body: "'"$MESSAGE"'",
|
||||||
|
}) {
|
||||||
|
clientMutationId
|
||||||
|
comment {
|
||||||
|
id
|
||||||
|
body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
name: Dump github context
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
dump_github_context:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Dump github context
|
||||||
|
run: echo "$GITHUB_CONTEXT"
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
.config/ags/node_modules
|
|
||||||
.config/ags/scss/_musicmaterial.scss
|
|
||||||
.config/ags/scss/_musicwal.scss
|
|
||||||
.config/ags/scss/_material.scss
|
|
||||||
/diagnose.result
|
/diagnose.result
|
||||||
/ii_ags.log
|
|
||||||
/cache
|
/cache
|
||||||
# Ignore Python cache files
|
# Ignore Python cache files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
.config/quickshell/ii/.qmlls.ini
|
dots/.config/quickshell/ii/.qmlls.ini
|
||||||
|
.update-lock
|
||||||
|
/os-release
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "dots/.config/quickshell/ii/modules/common/widgets/shapes"]
|
||||||
|
path = dots/.config/quickshell/ii/modules/common/widgets/shapes
|
||||||
|
url = https://github.com/end-4/rounded-polygon-qmljs.git
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
- I can accept features I do not personally want, but in that case I will ask you to make it configurable/optionally loaded
|
|
||||||
- If you want to add new stuff, it's a good idea to ask me first to not waste your work
|
|
||||||
- Please make multiple PRs if you have many features/fixes
|
|
||||||
|
|
||||||
# Setting up
|
|
||||||
|
|
||||||
Assumption: you have an Arch(-based) Linux system
|
|
||||||
|
|
||||||
## Complete
|
|
||||||
|
|
||||||
_might not be necessary depending on what you change, but this is recommended_
|
|
||||||
|
|
||||||
- [Install](https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/01setup/) the dotfiles (if you don't wanna replace your stuff completely, do it on a new user)
|
|
||||||
- Make changes, copy changes to a fork, PR
|
|
||||||
|
|
||||||
## Partially working shell
|
|
||||||
|
|
||||||
_most stuff in the shell will work but not everything_
|
|
||||||
|
|
||||||
- Install Hyprland and the development version of Quickshell (`yay -S hyprland quickshell-git`)
|
|
||||||
- Copy `.config/quickshell` folder to your home directory
|
|
||||||
|
|
||||||
## Extra setup for Quickshell
|
|
||||||
- Quickshell-specific LSP setup: Run `touch ~/.config/quickshell/ii/.qmlls.ini` for proper LSP support
|
|
||||||
- Hint for VSCode: Get the official "Qt Qml" extension, go to its settings and change custom exe path to `/usr/bin/qmlls6`
|
|
||||||
|
|
||||||
# Running
|
|
||||||
|
|
||||||
- Launch Hyprland (not the "uwsm-managed" one)
|
|
||||||
- For the shell:
|
|
||||||
- Open `~/.config/quickshell/ii` in your code editor
|
|
||||||
- In a terminal run `pkill qs; qs -c ii` to start the shell in the terminal (for logs)
|
|
||||||
- Make edits in the opened folder. Changes are reloaded live.
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
pkgname=illogical-impulse-basic
|
|
||||||
pkgver=1.0
|
|
||||||
pkgrel=1
|
|
||||||
pkgdesc='Illogical Impulse Basic Dependencies'
|
|
||||||
arch=(any)
|
|
||||||
license=(None)
|
|
||||||
depends=(
|
|
||||||
axel
|
|
||||||
bc
|
|
||||||
coreutils
|
|
||||||
cliphist
|
|
||||||
cmake
|
|
||||||
curl
|
|
||||||
rsync
|
|
||||||
wget
|
|
||||||
ripgrep
|
|
||||||
jq
|
|
||||||
meson
|
|
||||||
xdg-user-dirs
|
|
||||||
)
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
pkgname=illogical-impulse-toolkit
|
|
||||||
pkgver=1.0
|
|
||||||
pkgrel=1
|
|
||||||
pkgdesc='Illogical Impulse GTK/Qt Dependencies'
|
|
||||||
arch=(any)
|
|
||||||
license=(None)
|
|
||||||
depends=(
|
|
||||||
kdialog
|
|
||||||
qt6-5compat
|
|
||||||
qt6-avif-image-plugin
|
|
||||||
qt6-base
|
|
||||||
qt6-declarative
|
|
||||||
qt6-imageformats
|
|
||||||
qt6-multimedia
|
|
||||||
qt6-positioning
|
|
||||||
qt6-quicktimeline
|
|
||||||
qt6-sensors
|
|
||||||
qt6-svg
|
|
||||||
qt6-tools
|
|
||||||
qt6-translations
|
|
||||||
qt6-virtualkeyboard
|
|
||||||
qt6-wayland
|
|
||||||
syntax-highlighting
|
|
||||||
upower
|
|
||||||
wtype
|
|
||||||
ydotool
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
pkgname=illogical-impulse-widgets
|
|
||||||
pkgver=1.0
|
|
||||||
pkgrel=3
|
|
||||||
pkgdesc='Illogical Impulse Widget Dependencies'
|
|
||||||
arch=(any)
|
|
||||||
license=(None)
|
|
||||||
depends=(
|
|
||||||
fuzzel
|
|
||||||
glib2 # for `gsettings` it seems?
|
|
||||||
hypridle
|
|
||||||
hyprutils
|
|
||||||
hyprlock
|
|
||||||
hyprpicker
|
|
||||||
nm-connection-editor
|
|
||||||
quickshell-git
|
|
||||||
translate-shell
|
|
||||||
wlogout
|
|
||||||
)
|
|
||||||
@@ -2,18 +2,24 @@
|
|||||||
#
|
#
|
||||||
# This script is for quickly generate helpful info
|
# This script is for quickly generate helpful info
|
||||||
#
|
#
|
||||||
|
# It should be as independent as possible and should not source other files unless it has to
|
||||||
|
#
|
||||||
|
# TODO: How to get the Qt version which Quickshell was built against?
|
||||||
|
|
||||||
|
STY_RED='\e[31m'
|
||||||
|
STY_RST='\e[00m'
|
||||||
|
|
||||||
cd "$(dirname "$0")";export base="$(pwd)"
|
cd "$(dirname "$0")";export base="$(pwd)"
|
||||||
output_file=diagnose.result;rm $output_file
|
output_file=diagnose.result;rm $output_file
|
||||||
export LANG=C;export LC_ALL=C
|
export LANG=C;export LC_ALL=C
|
||||||
case $(whoami) in
|
case $(whoami) in
|
||||||
root)echo -e "\e[31m[$0]: This script is NOT to be executed with sudo or as root. Aborting...\e[0m";exit 1;;
|
root)echo -e "${STY_RED}[$0]: This script is NOT to be executed with sudo or as root. Aborting...${STY_RST}";exit 1;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
||||||
x() { ii_exec "$@" 2>&1 | tee -a $output_file ; }
|
x() { _exec "$@" 2>&1 | tee -a $output_file ; }
|
||||||
e() { ii_box "$@" | tee -a $output_file ; }
|
e() { _box "$@" | tee -a $output_file ; }
|
||||||
ii_box() {
|
_box() {
|
||||||
length=$(echo "$1" | wc -L);total_width=$((length + 2))
|
length=$(echo "$1" | wc -L);total_width=$((length + 2))
|
||||||
#line=$(printf "═%.0s" $(seq 1 $total_width))
|
#line=$(printf "═%.0s" $(seq 1 $total_width))
|
||||||
#border_up="╔${line}╗";border_down="╚${line}╝"
|
#border_up="╔${line}╗";border_down="╚${line}╝"
|
||||||
@@ -25,11 +31,22 @@ ii_box() {
|
|||||||
echo "$border_vertical $1 $border_vertical"
|
echo "$border_vertical $1 $border_vertical"
|
||||||
echo "$border_down"
|
echo "$border_down"
|
||||||
}
|
}
|
||||||
ii_exec() {
|
_exec() {
|
||||||
printf "\n[===diagnose===] $*\n"
|
printf "\n[===diagnose===] $*\n"
|
||||||
"$@"
|
"$@"
|
||||||
err=$?;if [ ! $err -eq 0 ];then echo "[---EXIT $err---]";else echo "[---SUCCESS---]";fi
|
err=$?;if [ ! $err -eq 0 ];then echo "[---EXIT $err---]";else echo "[---SUCCESS---]";fi
|
||||||
}
|
}
|
||||||
|
ii_check_distro_id() {
|
||||||
|
OS_RELEASE_FILE=/etc/os-release
|
||||||
|
if [[ -f "$OS_RELEASE_FILE" ]]; then
|
||||||
|
OS_DISTRO_ID=$(awk -F'=' '/^ID=/ { gsub("\"","",$2); print tolower($2) }' ${OS_RELEASE_FILE} 2> /dev/null)
|
||||||
|
OS_DISTRO_ID_LIKE=$(awk -F'=' '/^ID_LIKE=/ { gsub("\"","",$2); print tolower($2) }' ${OS_RELEASE_FILE} 2> /dev/null)
|
||||||
|
echo "distro ID: $OS_DISTRO_ID"
|
||||||
|
echo "distro ID_LIKE: $OS_DISTRO_ID_LIKE"
|
||||||
|
else
|
||||||
|
echo "$OS_RELEASE_FILE does not exist."
|
||||||
|
fi
|
||||||
|
}
|
||||||
ii_check_distro() {
|
ii_check_distro() {
|
||||||
lsb_release -a || cat /etc/os-release || cat /etc/lsb-release
|
lsb_release -a || cat /etc/os-release || cat /etc/lsb-release
|
||||||
}
|
}
|
||||||
@@ -38,13 +55,21 @@ ii_check_venv() {
|
|||||||
which python
|
which python
|
||||||
deactivate
|
deactivate
|
||||||
}
|
}
|
||||||
|
ii_check_quickshell_version() {
|
||||||
|
pacman -Q | grep -E 'quickshell|qt6-base'
|
||||||
|
}
|
||||||
|
ii_check_PKGBUILD_version() {
|
||||||
|
pacman -Q | grep '^illogical-impulse-'
|
||||||
|
}
|
||||||
|
|
||||||
e "Checking git repo info"
|
e "Checking git repo info"
|
||||||
x git remote get-url origin
|
x git remote get-url origin
|
||||||
x git rev-parse HEAD
|
x git rev-parse HEAD
|
||||||
|
x git submodule status --recursive
|
||||||
|
|
||||||
e "Checking distro"
|
e "Checking distro"
|
||||||
x ii_check_distro
|
x ii_check_distro_id
|
||||||
|
#x ii_check_distro
|
||||||
|
|
||||||
e "Checking variables"
|
e "Checking variables"
|
||||||
x declare -p XDG_CACHE_HOME # ~/.cache
|
x declare -p XDG_CACHE_HOME # ~/.cache
|
||||||
@@ -55,17 +80,18 @@ x declare -p ILLOGICAL_IMPULSE_VIRTUAL_ENV # $XDG_STATE_HOME/quickshell/.venv
|
|||||||
|
|
||||||
e "Checking directories/files"
|
e "Checking directories/files"
|
||||||
x ls -l ~/.local/state/quickshell/.venv
|
x ls -l ~/.local/state/quickshell/.venv
|
||||||
#x cat ~/.config/ags/
|
|
||||||
|
|
||||||
#e "Checking command existence"
|
#e "Checking command existence"
|
||||||
#commands=(yay pacman zypper apt dnf yum)
|
#commands=(yay pacman zypper apt dnf yum)
|
||||||
commands+=(ags agsv1)
|
#commands+=(ags agsv1)
|
||||||
#commands+=(Hyprland hypr{ctl,idle,lock,picker})
|
#commands+=(Hyprland hypr{ctl,idle,lock,picker})
|
||||||
#commands+=(uv)
|
#commands+=(uv)
|
||||||
#for i in "${commands[@]}";do x command -v $i;done
|
#for i in "${commands[@]}";do x command -v $i;done
|
||||||
|
|
||||||
e "Checking versions"
|
e "Checking versions"
|
||||||
x Hyprland --version
|
x Hyprland --version
|
||||||
|
x ii_check_quickshell_version
|
||||||
|
x ii_check_PKGBUILD_version
|
||||||
|
|
||||||
e "Finished. Output saved as \"$output_file\"."
|
e "Finished. Output saved as \"$output_file\"."
|
||||||
if ! command -v curl 2>&1 >>/dev/null ;then echo "\"curl\" not found, pastebin upload unavailable.";exit;fi
|
if ! command -v curl 2>&1 >>/dev/null ;then echo "\"curl\" not found, pastebin upload unavailable.";exit;fi
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
;; Define function to read SCSS variables
|
;; Define function to read SCSS variables
|
||||||
(defun material-get-color-from-scss (var-name)
|
(defun material-get-color-from-scss (var-name)
|
||||||
"Extract color value for VAR-NAME from material_colors.scss file."
|
"Extract color value for VAR-NAME from material_colors.scss file."
|
||||||
(let* ((scss-file (expand-file-name "~/.cache/ags/user/generated/material_colors.scss"))
|
(let* ((scss-file (expand-file-name "~/.local/state/quickshell/user/generated/material_colors.scss"))
|
||||||
(scss-content (with-temp-buffer
|
(scss-content (with-temp-buffer
|
||||||
(insert-file-contents scss-file)
|
(insert-file-contents scss-file)
|
||||||
(buffer-string)))
|
(buffer-string)))
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Bar, wallpaper
|
||||||
|
exec-once = ~/.config/hypr/hyprland/scripts/start_geoclue_agent.sh
|
||||||
|
exec-once = qs -c $qsConfig &
|
||||||
|
|
||||||
|
# Input method
|
||||||
|
# exec-once = fcitx5
|
||||||
|
|
||||||
|
# Core components (authentication, lock screen, notification daemon)
|
||||||
|
exec-once = gnome-keyring-daemon --start --components=secrets
|
||||||
|
exec-once = hypridle
|
||||||
|
exec-once = dbus-update-activation-environment --all
|
||||||
|
exec-once = sleep 1 && dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP # Some fix idk
|
||||||
|
exec-once = hyprpm reload
|
||||||
|
|
||||||
|
# Audio
|
||||||
|
exec-once = easyeffects --gapplication-service
|
||||||
|
|
||||||
|
# Clipboard: history
|
||||||
|
# exec-once = wl-paste --watch cliphist store &
|
||||||
|
exec-once = wl-paste --type text --watch bash -c 'cliphist store && qs -c $qsConfig ipc call cliphistService update'
|
||||||
|
exec-once = wl-paste --type image --watch bash -c 'cliphist store && qs -c $qsConfig ipc call cliphistService update'
|
||||||
|
|
||||||
|
# Cursor
|
||||||
|
exec-once = hyprctl setcursor Bibata-Modern-Classic 24
|
||||||
|
|
||||||
|
# Fix dock pinned apps not launching properly (https://github.com/end-4/dots-hyprland/issues/2200)
|
||||||
|
exec-once = sleep 3.5 && hyprctl reload && sleep 0.5 && touch ~/.config/quickshell/ii/shell.qml
|
||||||
|
|
||||||
|
# For fedora to setup polkit
|
||||||
|
exec-once = /usr/libexec/kf6/polkit-kde-authentication-agent-1
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
|
||||||
|
<fontconfig>
|
||||||
|
<match target="font">
|
||||||
|
<edit name="rgba" mode="assign">
|
||||||
|
<const>none</const>
|
||||||
|
</edit>
|
||||||
|
</match>
|
||||||
|
|
||||||
|
<!-- Fix for: arabic fonts rendering in Noto Nastaliq Urdu | affects Chromium, Discord (maybe all chromium based apps, but not Spotify somehow) -->
|
||||||
|
<match target="pattern">
|
||||||
|
<test compare="eq" name="family">
|
||||||
|
<string>sans-serif</string>
|
||||||
|
</test>
|
||||||
|
<edit name="family" mode="prepend" binding="strong">
|
||||||
|
<string>Noto Sans Arabic</string>
|
||||||
|
</edit>
|
||||||
|
</match>
|
||||||
|
</fontconfig>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
This folder contains tweakd configs when --via-nix is specified.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# $lock_cmd = hyprctl dispatch global quickshell:lock & pidof qs quickshell hyprlock || hyprlock
|
$lock_cmd = swaylock -c 000000
|
||||||
$lock_cmd = pidof hyprlock || hyprlock
|
# $lock_cmd = pidof hyprlock || hyprlock
|
||||||
$suspend_cmd = systemctl suspend || loginctl suspend
|
$suspend_cmd = systemctl suspend || loginctl suspend
|
||||||
|
|
||||||
general {
|
general {
|
||||||
|
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
@@ -0,0 +1,2 @@
|
|||||||
|
[Style]
|
||||||
|
WidgetDrawShadow=false
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
[DetailsMode]
|
||||||
|
PreviewSize=32
|
||||||
|
|
||||||
|
[General]
|
||||||
|
GlobalViewProps=false
|
||||||
|
ShowFullPath=true
|
||||||
|
ShowStatusBar=FullWidth
|
||||||
|
Version=202
|
||||||
|
|
||||||
|
[KFileDialog Settings]
|
||||||
|
Places Icons Auto-resize=false
|
||||||
|
Places Icons Static Size=22
|
||||||
|
|
||||||
|
[MainWindow]
|
||||||
|
MenuBar=Disabled
|
||||||
|
|
||||||
|
[PreviewSettings]
|
||||||
|
Plugins=ffmpegthumbnailer,appimagethumbnail,audiothumbnail,avif,blenderthumbnail,comicbookthumbnail,cursorthumbnail,djvuthumbnail,ebookthumbnail,exrthumbnail,directorythumbnail,fontthumbnail,heif,imagethumbnail,jpegthumbnail,jxl,kraorathumbnail,windowsexethumbnail,windowsimagethumbnail,mobithumbnail,opendocumentthumbnail,gsthumbnail,rawthumbnail,librsvg,svgthumbnail,ffmpegthumbs,gdk-pixbuf-thumbnailer,gsf-office
|
||||||