forked from Shinonome/dots-hyprland
Compare commits
2519 Commits
main
..
2026.05.11
| Author | SHA1 | Date | |
|---|---|---|---|
| bebf66da89 | |||
| adb36f435e | |||
| c67c8840e4 | |||
| bfad75c93a | |||
| 5c69271c10 | |||
| 84cd4582dd | |||
| e7c283e91e | |||
| f992294ec2 | |||
| c652d2a7f3 | |||
| 242de398c3 | |||
| fe9eb0e84c | |||
| 47235ac440 | |||
| e79c9a8792 | |||
| 762629d2f1 | |||
| 7596509777 | |||
| e6570bbe75 | |||
| a2c1641090 | |||
| 25de796710 | |||
| 96bf8dcbf0 | |||
| a9dcab5e41 | |||
| a57119005d | |||
| 5467ab7f06 | |||
| fdd3cf5ef8 | |||
| dbcde24003 | |||
| e03cbfec91 | |||
| eae36f4d4f | |||
| 09ff686f18 | |||
| 0040588fd2 | |||
| 94fa3f9c40 | |||
| 4b4e1fd53c | |||
| 5e6db59bfc | |||
| 5f9febd4b7 | |||
| 3e0561578c | |||
| fa3bf6f5df | |||
| a5eca98543 | |||
| a8761bc6f2 | |||
| dd26742d52 | |||
| 771cc18be5 | |||
| ada4b856f6 | |||
| d44bbcfefc | |||
| 6589158f51 | |||
| f3ab1366d8 | |||
| d42420f848 | |||
| 9f5be8b0bc | |||
| 747abbde9e | |||
| 0fb7d4dfd8 | |||
| c1c964aa89 | |||
| 099406a276 | |||
| 2d0ac6e7db | |||
| d24e227245 | |||
| fad2a5f8ae | |||
| 1e2a6d23e3 | |||
| aea6216a7f | |||
| 77ce65a53c | |||
| 799bfae184 | |||
| 0ebe70976f | |||
| f88b21e681 | |||
| 64b52f6a90 | |||
| ec3b92a938 | |||
| 59398595a5 | |||
| eef2b691e7 | |||
| 347ada664d | |||
| a06764e111 | |||
| d42c9b63be | |||
| dfe9c876d2 | |||
| 4408b5d9d3 | |||
| e356dbb6f9 | |||
| 7948d01d94 | |||
| 8447588c7f | |||
| 56ad8b80b2 | |||
| fe8d9887fa | |||
| bcb815aac5 | |||
| da99437351 | |||
| ba2da84e7b | |||
| b4a57bbbec | |||
| 687beedc4a | |||
| 2243f8e489 | |||
| 6c0c7beaa6 | |||
| 40705b4635 | |||
| 83701e3038 | |||
| 0493c08759 | |||
| 0c3be42218 | |||
| f6f07d4f65 | |||
| 3a20c0c2bb | |||
| db10d4a45b | |||
| 33f2865967 | |||
| 7eae26474f | |||
| f6f0dedcb8 | |||
| 257abdf80a | |||
| f3caad4d90 | |||
| 7a983b049f | |||
| 69c5ac5cb3 | |||
| 55a33fb7d7 | |||
| 72aa169087 | |||
| 7f0fba51a9 | |||
| 842d1323e9 | |||
| 26adce8fc0 | |||
| bdc6026cc2 | |||
| 9f882d9f3c | |||
| 15b4394865 | |||
| 67fbaf1d27 | |||
| 7206d1a19e | |||
| ae9de61b72 | |||
| 0a8fb89501 | |||
| b1e2f86360 | |||
| 74329eb294 | |||
| 01562ca544 | |||
| dde0988a45 | |||
| c117d9903a | |||
| 29d40ca90a | |||
| 737ccce538 | |||
| 96f5f82656 | |||
| 35fb3d4da8 | |||
| 17c2762b1d | |||
| 27bd759a41 | |||
| 2ed29f5c46 | |||
| 75594fb5cf | |||
| f86d42d8f2 | |||
| b6fc897b39 | |||
| 8f66afe109 | |||
| 72b8b7aecb | |||
| a9721c7cfe | |||
| 774ce228a0 | |||
| abea376e59 | |||
| bf2fec015c | |||
| 78c6e3c16f | |||
| deadf6cc4f | |||
| ae0412b513 | |||
| f974dea8e5 | |||
| ede3cf8858 | |||
| dfe6eee674 | |||
| 854016e82a | |||
| b4956cc05c | |||
| e06a3be726 | |||
| 3e0f480ce0 | |||
| 065b34ccde | |||
| 41b6c190d2 | |||
| 795f4042b0 | |||
| aff4705d6a | |||
| a7353e5033 | |||
| a43228ab28 | |||
| fc7524a30f | |||
| a28ed6023e | |||
| 973b83bc15 | |||
| 9d065f2c8e | |||
| 4caa59dd9e | |||
| 8aa9041106 | |||
| 58a122a3b4 | |||
| 6e76977976 | |||
| 15a56bdce3 | |||
| 33db15f991 | |||
| 9dcf63b54a | |||
| b61ae3abe7 | |||
| 42f14b92d0 | |||
| fc17e29ebf | |||
| b306d308b7 | |||
| 369fadd74b | |||
| fd86b282a6 | |||
| 0362b21f5a | |||
| fd2d69e407 | |||
| 7a01602c5e | |||
| 4a9d6a2665 | |||
| be1838e40d | |||
| 7cf704d450 | |||
| 4eedd030df | |||
| 74ce3378d0 | |||
| a831fa92e8 | |||
| 7e94ee60aa | |||
| 9a11a0d8f9 | |||
| a574baacc5 | |||
| bd923a0f88 | |||
| 735fb7895b | |||
| fffa2b5749 | |||
| 673e5ffe2e | |||
| 8190c3b976 | |||
| 5ac58bdb3c | |||
| 9bbfef19e2 | |||
| bda3834176 | |||
| 3653715029 | |||
| 0974a6069a | |||
| 5710fbb3d6 | |||
| fb4b5e83a9 | |||
| a052a01eee | |||
| 97d4f8e438 | |||
| a245b4681d | |||
| 8c34bcf124 | |||
| f554a0512d | |||
| 370574d8ca | |||
| c3b152b318 | |||
| 26c69e5fd3 | |||
| 1671959186 | |||
| a7f1cddd45 | |||
| b4b422bd9c | |||
| 1dcf90ac57 | |||
| 2e0dea28a5 | |||
| 9873a23794 | |||
| 8c66dc7aa7 | |||
| 9cbeded70d | |||
| 01f2ebb246 | |||
| 1f9a8ea37c | |||
| 5cbf5608be | |||
| 0c843c8d03 | |||
| c85e98d6f9 | |||
| 7013893cbd | |||
| 65eeea7ffa | |||
| 5860462082 | |||
| 33bd04200a | |||
| 4fc56f6c78 | |||
| ba4ac764aa | |||
| 65dab672cf | |||
| ea0dd2c9a4 | |||
| e52d569b9e | |||
| f610332877 | |||
| bbc84b68ef | |||
| 2554fc7d61 | |||
| dc172862b0 | |||
| 0725edd35d | |||
| 77dd839320 | |||
| 1402a19c98 | |||
| 57b78135a6 | |||
| 48fec445ad | |||
| 060b8693ef | |||
| a187d013e4 | |||
| 577fab457f | |||
| 35bb2a1ad9 | |||
| 095d637c70 | |||
| 373a9e5670 | |||
| d11ef2adef | |||
| 9a3bb5e59e | |||
| 8bf279e571 | |||
| e72d39fecb | |||
| c5e0c2d3dc | |||
| c19e625069 | |||
| aea06d42ab | |||
| 40fb416366 | |||
| ab1b720503 | |||
| b6c2fd3f18 | |||
| 3d9f51030c | |||
| 14770e5b75 | |||
| 2e7ea9d9f6 | |||
| 60c197e2f5 | |||
| 47c5ffe5f9 | |||
| 5ab4812a3b | |||
| c063bdfe86 | |||
| cea0acafff | |||
| fb1b674a30 | |||
| 8d52e83a59 | |||
| 7670c135ed | |||
| a7f30838de | |||
| 585a7b0f06 | |||
| 502dffeac7 | |||
| 52f1a7af65 | |||
| 1e7f065add | |||
| f128c48356 | |||
| 44244ed1bf | |||
| e3d89101f1 | |||
| 0435681032 | |||
| 33b4ef42f4 | |||
| 633afb54c9 | |||
| feeb0bba8c | |||
| 92e9aa2976 | |||
| 09d5817f2e | |||
| d23d39df04 | |||
| 9e9c6b70b6 | |||
| 3a14cc644b | |||
| 2132829184 | |||
| 9fbc549da4 | |||
| 4d6f58a914 | |||
| 4601605df9 | |||
| 640a147721 | |||
| 145731d8bb | |||
| c1a6034a13 | |||
| 68f0355940 | |||
| 4fbd238a90 | |||
| b3117ce578 | |||
| f8903da663 | |||
| 15ceda494e | |||
| f1a2784777 | |||
| 52ae3b187f | |||
| 622b6ec424 | |||
| 4ff3435446 | |||
| e24de7f935 | |||
| 6215f54c9f | |||
| a55ebc5f48 | |||
| f3f9d183f2 | |||
| 21e3f253d9 | |||
| 1be524ce15 | |||
| aee7240627 | |||
| 901b4cf1ff | |||
| d92a081b57 | |||
| 6bf455a042 | |||
| d3ccde299e | |||
| 18004b5959 | |||
| 1588a20b46 | |||
| 6075835d85 | |||
| 63dd6516f1 | |||
| 1b4c439c3e | |||
| 76ee7b6bb1 | |||
| c30776e811 | |||
| a217d4c5da | |||
| b236690f2a | |||
| 36051b5970 | |||
| f98e3cd3e9 | |||
| 30269dd052 | |||
| 20ec7717a4 | |||
| bbf1066ceb | |||
| 798e4bde27 | |||
| 224bdbd5cb | |||
| 2cb45c3a78 | |||
| 80db68467f | |||
| 8182dbb17b | |||
| ecc8d42265 | |||
| 6afc4b1aa7 | |||
| 1c3c255ab6 | |||
| a5c61d9ab0 | |||
| 887d54bf07 | |||
| 2d0cff9716 | |||
| 47beca14de | |||
| 9611288d79 | |||
| 2af409b1e5 | |||
| 800c75c4d2 | |||
| e0640a8782 | |||
| 1932dcbab8 | |||
| 94490f371b | |||
| 3fbf9a7056 | |||
| b75ae3817c | |||
| 26abbd29e2 | |||
| 09c09ebfcf | |||
| 36658322c4 | |||
| a9a92e0203 | |||
| eecc1201d8 | |||
| 0f24287a39 | |||
| 07446b5399 | |||
| 803500c4b0 | |||
| d8018e97fe | |||
| 6675fb8abb | |||
| cf2e944742 | |||
| e9b80dbbf4 | |||
| b7b94b4a4e | |||
| 1a9d78fb48 | |||
| 97d22378ff | |||
| 809dac681f | |||
| 46911074f9 | |||
| 528102ba4a | |||
| 755a9b15a5 | |||
| f05358ed28 | |||
| 922c85795f | |||
| 5b27dfa747 | |||
| 57b5e34f27 | |||
| 3b86fe7de7 | |||
| 360f1258f3 | |||
| 672e756fbf | |||
| 60fd1ea030 | |||
| bf06497f9e | |||
| 22970db52f | |||
| 14c930d48c | |||
| 7197f9ddfb | |||
| 58e372c590 | |||
| 8538efe743 | |||
| b268f1d61c | |||
| 711793dd66 | |||
| dc57f940d0 | |||
| da578735e6 | |||
| 31e821250f | |||
| ce6d5969e0 | |||
| d5b599da3d | |||
| c9d0248a6a | |||
| 7238b2b15c | |||
| 5a687c3565 | |||
| 171cf6059f | |||
| e818a202b8 | |||
| 199845bf59 | |||
| 575b26d572 | |||
| af1adef5f1 | |||
| c5c8ad2236 | |||
| 8cb2c7e016 | |||
| d30c8138df | |||
| 0027e8a6e3 | |||
| 652104a358 | |||
| e9d6ed874c | |||
| aaa8448011 | |||
| f02e9ba2a5 | |||
| a05b0bb559 | |||
| 60c1a92cf5 | |||
| 91f6f95460 | |||
| 2434b36098 | |||
| a849fe0883 | |||
| 8ae14725e6 | |||
| e8a05d12e9 | |||
| 76fbe3d04a | |||
| 7763a26e03 | |||
| 281943306e | |||
| 61feb958ec | |||
| 953427692f | |||
| b21699b8a0 | |||
| d13bcddb33 | |||
| b820bdc654 | |||
| d178dafac6 | |||
| d22822e734 | |||
| 5c141e0361 | |||
| e1f30bf85c | |||
| c5aaf721c8 | |||
| 13a827c0f4 | |||
| 64e726d4c5 | |||
| 392ef6b74f | |||
| db60f8775d | |||
| 5a48a22e71 | |||
| 1e2a972747 | |||
| a34ead004f | |||
| a658aced0d | |||
| 6ad8717a47 | |||
| 76ca889eec | |||
| ec7d6fd66b | |||
| cfb8b44d7a | |||
| c580b050e4 | |||
| d7ae6014ed | |||
| 29c8001785 | |||
| 896aa97701 | |||
| 8d7dd0d6ae | |||
| 41f007a771 | |||
| 4041310b4d | |||
| d5dbf7ab7f | |||
| 8842df6340 | |||
| 169b24bea5 | |||
| e499f4f8f1 | |||
| ae8be40c2d | |||
| 3eec36d20d | |||
| ed6a0204b4 | |||
| b01e0c315a | |||
| a38c725d3d | |||
| 17a3874ab1 | |||
| 34991dd32e | |||
| 28d3f6a94a | |||
| 252a1055c2 | |||
| 4ac6784844 | |||
| ea8f0fbc2d | |||
| 61f0f0dc97 | |||
| dd00908026 | |||
| 4b1e02dda6 | |||
| 0cff92d02c | |||
| 125e57c98f | |||
| 2b9e5b1a6f | |||
| f65f805fe2 | |||
| 409fc94d6e | |||
| 36ff18bfe3 | |||
| 60b2225cc6 | |||
| 70363ab886 | |||
| 663eb1896a | |||
| b73cdf0379 | |||
| c92832ff95 | |||
| 607d6056c1 | |||
| 2ef8342187 | |||
| 0e3b3eceb6 | |||
| f1fdb941e1 | |||
| f91ca59fb6 | |||
| 5f51b97b6d | |||
| c02daf5c12 | |||
| e1601b972f | |||
| 8d0d71812a | |||
| e206c4334c | |||
| 3358ebe639 | |||
| abd657ed76 | |||
| 04211411af | |||
| 536c1ab465 | |||
| 29c4a6a15f | |||
| 93bc4d935c | |||
| f0926b6ce3 | |||
| d54ad65b50 | |||
| 91a2a520b0 | |||
| 136f4a3e48 | |||
| f71ed855e5 | |||
| 39a3a0c484 | |||
| 2ead5fa4ab | |||
| d83733bd86 | |||
| 1ea3153886 | |||
| eaae89c904 | |||
| 044221be93 | |||
| e874a6a3e0 | |||
| 70c0adb8e5 | |||
| 3ae0973df5 | |||
| b966e2d539 | |||
| 6da58f5235 | |||
| bcd7fb1c1b | |||
| 80f4a0549c | |||
| 1a3cc9b4d3 | |||
| 0cc521aef5 | |||
| d9b1d0261d | |||
| fdbe39d744 | |||
| 3cb61c4267 | |||
| bb65137415 | |||
| 95c6fcab01 | |||
| fce229cdc3 | |||
| bfe97c1c05 | |||
| 8b8ac44852 | |||
| 34b5892374 | |||
| 0645200807 | |||
| 1c8339df10 | |||
| 8b1f0fc1d4 | |||
| 6c460b209c | |||
| 13968db31c | |||
| 80a7804ade | |||
| ddf1bc6a08 | |||
| fa47c6778c | |||
| 255006172f | |||
| 52d6e8a5d1 | |||
| ead056c207 | |||
| 9a0e4181a5 | |||
| ed89ad882f | |||
| 3b4c721584 | |||
| 7a2e21ac7c | |||
| 9043ae7bf6 | |||
| 758c84feaf | |||
| 1efd2dfa19 | |||
| 1193b7a802 | |||
| f62ab85a19 | |||
| 15481c646a | |||
| 8e704e4009 | |||
| e9b5e7d7d2 | |||
| 8cfb3be4cd | |||
| 85892940b5 | |||
| 7d9a405146 | |||
| 8203115a24 | |||
| 895faae39f | |||
| 71c1fbe1dd | |||
| 4055ad48fa | |||
| 05aae36e82 | |||
| 20d60a11f7 | |||
| b0c396aa21 | |||
| ed9e510c32 | |||
| d3c1ae14b8 | |||
| ffd01741d9 | |||
| 8a9f105e75 | |||
| dd07a62dc0 | |||
| c093265b05 | |||
| 1dd959fad4 | |||
| 4cfb706b6f | |||
| 90f9467871 | |||
| cce06cc2f0 | |||
| 1d40360cf2 | |||
| f08bbc0d67 | |||
| 4ce4645749 | |||
| 4cad401ea6 | |||
| 8d2c8bd38e | |||
| 61b5cf8cb6 | |||
| 9c48fd32ef | |||
| c352ebc2fc | |||
| b45f2dd235 | |||
| 4442200479 | |||
| f5fea85334 | |||
| 8892982874 | |||
| 677fa06b06 | |||
| c11814b1f8 | |||
| 3cf14671ad | |||
| d2c019f8de | |||
| 6b90e37b0f | |||
| 21f2a9c65d | |||
| fd209851d9 | |||
| 533b9c01f5 | |||
| b4038dafa9 | |||
| 04f73e67c8 | |||
| 2503c1f14b | |||
| b214993c16 | |||
| 2a1aaa9b7e | |||
| 2fd25af353 | |||
| b7ad7361d6 | |||
| 9cb5cc1416 | |||
| 0700e024d9 | |||
| d40df98aa5 | |||
| d27fbede2a | |||
| a786f0353e | |||
| 9ba8723a5d | |||
| e4b5718833 | |||
| b4920a7cb6 | |||
| 08739043f6 | |||
| 55152aee4b | |||
| 9053927480 | |||
| 616c1dfe3a | |||
| 5242373db5 | |||
| 1836a2ff1c | |||
| cecb476ed3 | |||
| e6f36114bd | |||
| 449d6fc285 | |||
| 0703429393 | |||
| 0adefcc0d3 | |||
| 996579729d | |||
| 25816662f8 | |||
| 19ba7dac48 | |||
| 07f8a72d6d | |||
| e46c7c0d3d | |||
| c78c363388 | |||
| 12ebcf2d19 | |||
| 52f67431c7 | |||
| f9755694f7 | |||
| a72d5f98af | |||
| fae2309f62 | |||
| a17a909fd9 | |||
| 3087e5da92 | |||
| 6e986fa8b0 | |||
| adfc7a15e8 | |||
| 0f11296ee1 | |||
| 9a113c24ca | |||
| 55961ae079 | |||
| 7613bba393 | |||
| 52af531c9f | |||
| 92beee6c16 | |||
| be5b03dfdc | |||
| 67c7fd09dd | |||
| f0a042246c | |||
| 9cf66f4d83 | |||
| 8943d568f5 | |||
| 4d79cc6764 | |||
| 0b3cc187cb | |||
| e2230a0a35 | |||
| 4b0cb15762 | |||
| da233086c2 | |||
| 65ef0ca666 | |||
| 6376521d14 | |||
| 7b2cfb11f6 | |||
| 6cb1b738c6 | |||
| 4878f7dbb1 | |||
| b350d87ce2 | |||
| 3507aee627 | |||
| 9466013124 | |||
| b869336738 | |||
| 4fc85a926f | |||
| 31998a6c32 | |||
| cd7bf9c1c2 | |||
| 68c159f210 | |||
| 30c845d226 | |||
| d69559e8f6 | |||
| 0c8391534a | |||
| e0584bf50e | |||
| 79762e4193 | |||
| fbfb81c83b | |||
| 35a1f7906a | |||
| a10b8b50d4 | |||
| 53399549fc | |||
| 8660d68420 | |||
| e8326b96d7 | |||
| b93213ddb6 | |||
| 9885dd7a1e | |||
| 7016b59933 | |||
| 527cb9e9b0 | |||
| 23fcd183bf | |||
| 9001cae848 | |||
| 6e5ba69430 | |||
| 5c8d824749 | |||
| 2fbfbb80ef | |||
| bca177eed2 | |||
| b052df4d72 | |||
| 255232135b | |||
| 03890d0099 | |||
| bfc07aa4e4 | |||
| 1a844e512b | |||
| 148384200c | |||
| 64c1b5be0b | |||
| 488b3c06d6 | |||
| 4cbb0f23c6 | |||
| b650120fd4 | |||
| f7cb85632e | |||
| f3bfe8a374 | |||
| dfe11810fc | |||
| aeb106ed21 | |||
| c0de7f1d37 | |||
| 625b4da3b7 | |||
| e6e86b4258 | |||
| f21e282780 | |||
| ae28d0bd6d | |||
| d8921a6608 | |||
| 0ec4b3f54a | |||
| b79fb33134 | |||
| 99171c9512 | |||
| 189530507f | |||
| 781404749e | |||
| 9fd83a2812 | |||
| e830dc93cc | |||
| 268b072a16 | |||
| fcee7ce6f9 | |||
| 9228165428 | |||
| 8f6e2bc7fb | |||
| c7bd1019e7 | |||
| 0e3cf4c908 | |||
| e8bae0e529 | |||
| 2d65af70ad | |||
| 71e0538cf5 | |||
| 28bf94904f | |||
| 986461f590 | |||
| 869f9529f6 | |||
| 83b54f5fc4 | |||
| d2692cc95e | |||
| 11dd8c9efb | |||
| ec76180f4c | |||
| 2ce060484a | |||
| 9adf310738 | |||
| a23ee83aad | |||
| 5aa384e906 | |||
| 8574dbcb02 | |||
| f33bc7663e | |||
| 1271f147ed | |||
| 5b69995945 | |||
| c71a2498d0 | |||
| 803f2ecb42 | |||
| be1ce37014 | |||
| 2a34831f87 | |||
| 5c746f34b7 | |||
| 3dfd043645 | |||
| d15960d930 | |||
| 885a9eb0b3 | |||
| 4abfcd0162 | |||
| 68a9c8729f | |||
| 4e07aa52ba | |||
| 7ff3a212ab | |||
| fdc38d69f7 | |||
| 6ee7212bdc | |||
| 839718cc2b | |||
| bbe0329df4 | |||
| 5d6d9234ac | |||
| d990d68472 | |||
| 24df9ce2e4 | |||
| 4393c05e46 | |||
| 6d221f558b | |||
| f3ad9f27f6 | |||
| d0de047db0 | |||
| 4e86ec9fe9 | |||
| 6f7b501430 | |||
| bd1c9bfb2a | |||
| 443f86d347 | |||
| 6dd0387833 | |||
| b6dfbf6c97 | |||
| 47cd7be87e | |||
| 3c25d18f88 | |||
| cc519e9f60 | |||
| aa85e2168e | |||
| bd284a5ef4 | |||
| 541c701d5a | |||
| 179815b73c | |||
| 11064d04f0 | |||
| 767d2beb6c | |||
| c19dd725b8 | |||
| 42f29d47b4 | |||
| 2ccdf3b751 | |||
| 02afa37da1 | |||
| 4ea48d60f8 | |||
| c16cff52b8 | |||
| 5bec659486 | |||
| 8404817e51 | |||
| eb8f1379f2 | |||
| a3cb292fe9 | |||
| f8ffe5e63f | |||
| a5b941360c | |||
| c8c4642c61 | |||
| 237fa85f0f | |||
| f46835c9a1 | |||
| 67b02627ae | |||
| 98ef819fee | |||
| 42147b2117 | |||
| 61a9d406f2 | |||
| 4b8f294a91 | |||
| 9e3e14ca83 | |||
| bf225d6de2 | |||
| 2e5be90237 | |||
| c369db75eb | |||
| 347cbd9803 | |||
| b5ccddf34d | |||
| f0643c9c5c | |||
| 63c3433b6f | |||
| f8af8093f7 | |||
| 45a78c383b | |||
| 499b0628ea | |||
| 18c264a85e | |||
| 323b0bc257 | |||
| 1495669a60 | |||
| b97c2d1d4d | |||
| a135b09ec7 | |||
| 666cf09d5f | |||
| 6c7ac470bd | |||
| 7049dda7de | |||
| 3e55442654 | |||
| 21c5111961 | |||
| 2362c2ab3f | |||
| bda5422ccc | |||
| 86aab5b1fd | |||
| 6f61782c8f | |||
| 6f19e1cdd6 | |||
| c64f3a0122 | |||
| 37c1d9cf61 | |||
| 5ad3508d3e | |||
| 6194ef912b | |||
| e07ddaa0b3 | |||
| 2ea3904b56 | |||
| 8d1fe864ad | |||
| 3e4674d651 | |||
| 5c92ad64f9 | |||
| b828a1dbf0 | |||
| df0c7bbbd6 | |||
| 7bfbf011d2 | |||
| 945c6a0782 | |||
| cfdbb873aa | |||
| 4cc4ad749b | |||
| 3c390d95be | |||
| 0917ee65f2 | |||
| 6889185c3e | |||
| 1844d12bc5 | |||
| 55b5d7145d | |||
| 480ef19b01 | |||
| 31c9fe1f5a | |||
| 5696a4348e | |||
| 9997fdce0c | |||
| 318d80de38 | |||
| ca261d931d | |||
| 56c07a514f | |||
| 9162c24ccd | |||
| 155a11734f | |||
| 7c590bd5d8 | |||
| a250675b5c | |||
| 3f8c62a81b | |||
| f2cd533ae9 | |||
| 20cae142d7 | |||
| 20e1f0e0bb | |||
| a412688af2 | |||
| efcb826f5c | |||
| 1ad99b43a0 | |||
| 2dfbb84b23 | |||
| de7d504160 | |||
| 8225a3981a | |||
| c91191f89b | |||
| ac55a66dd4 | |||
| febe3d53e3 | |||
| 60ef4a873d | |||
| 462ddefb7f | |||
| dc958436d8 | |||
| e00e703af2 | |||
| 6fedb70f69 | |||
| dec65aea17 | |||
| cbcb8cf8e1 | |||
| fdcb95b8a4 | |||
| 378af7ac2a | |||
| 694eaccfbf | |||
| 42919c59ec | |||
| 58980959aa | |||
| a065829eee | |||
| f1479626f3 | |||
| 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 | |||
| 60eed56ea7 | |||
| 11fa846ae1 | |||
| 3502e46d19 | |||
| 53768a6885 | |||
| eccfec4671 | |||
| 819735169b | |||
| ee94828d97 | |||
| 935a8ee411 | |||
| 8a5852f61a | |||
| 9c1c97a7ec | |||
| 77e6264568 | |||
| 5db16e6245 | |||
| 631d496001 | |||
| dda0a228cf | |||
| 5820106e31 | |||
| f678a55e6a | |||
| cd452c256e | |||
| c035427e6a | |||
| b322af7051 | |||
| 7236d2f50b | |||
| 87f5bc5870 | |||
| 3187239175 | |||
| 09fd61c71d | |||
| c1f84c77dd | |||
| 3f242fa298 | |||
| c7ac7b5b43 | |||
| 688a36af58 | |||
| 10e1509d5d | |||
| fc2e51ebbc | |||
| 1433fb5412 | |||
| b383635fa8 | |||
| 788c01c242 | |||
| d8bcf2ed59 | |||
| b965b50009 | |||
| eafe9f7217 | |||
| 96e4ca1095 | |||
| fa6a4e8543 | |||
| a2ae9497df | |||
| 225e03b0a3 | |||
| 4c5bc19748 | |||
| 9209ca8216 | |||
| 241f33fb2f | |||
| f9bc4b1608 | |||
| 0091fce2c1 | |||
| 424510065d | |||
| cf43479530 | |||
| c7c38bf0b1 | |||
| 8b5a783c1f | |||
| d74e385f84 | |||
| 3f1b5bba95 | |||
| 9cf2aa83a1 | |||
| 6fa417a4c1 | |||
| 6b0572975f | |||
| d292a85a5e | |||
| 3b1d8fd262 | |||
| 549a43ac7f | |||
| 769ed3bf71 | |||
| d10ac9cc74 | |||
| 4bf31544e7 | |||
| 03a149c10e | |||
| 5ec8cca5d5 | |||
| 44c8de82c7 | |||
| b08a545ece | |||
| 5504bd3f09 | |||
| fb1b5db279 | |||
| 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,2 +0,0 @@
|
||||
# You can put extra environment variables here
|
||||
# https://wiki.hyprland.org/Configuring/Environment-variables/
|
||||
@@ -1,6 +0,0 @@
|
||||
# Put general config stuff here
|
||||
# Here's a list of every variable: https://wiki.hyprland.org/Configuring/Variables/
|
||||
# monitor=DP-2, highres@180,0x1080,1,bitdepth,10,cm,hdr,sdrbrightness,1.4,sdrsaturation,0.98
|
||||
monitor=DP-1,3440x1440@180,0x1080,1,bitdepth,10,cm,hdr,sdrbrightness,1.4,sdrsaturation,0.98,vrr,0
|
||||
monitor=DP-2, highres@highrr,760x0,1
|
||||
monitor=HDMI-A-1, 1920x1080@120, 3440x1440, 1
|
||||
@@ -1,42 +0,0 @@
|
||||
# See https://wiki.hyprland.org/Configuring/Binds/
|
||||
#!
|
||||
##! User
|
||||
bind = Ctrl+Super, Slash, exec, xdg-open ~/.config/illogical-impulse/config.json # Edit shell config
|
||||
bind = Ctrl+Super+Alt, Slash, exec, xdg-open ~/.config/hypr/custom/keybinds.conf # Edit extra keybinds
|
||||
|
||||
# Add stuff here
|
||||
# Use #! to add an extra column on the cheatsheet
|
||||
# Use ##! to add a section in that column
|
||||
# Add a comment after a bind to add a description, like above
|
||||
|
||||
bind = Super, H, movefocus, l # [hidden]
|
||||
bind = Super, L, movefocus, r # [hidden]
|
||||
bind = Super, K, movefocus, u # [hidden]
|
||||
bind = Super, J, movefocus, d # [hidden]
|
||||
|
||||
bind = Super+Shift, H, movewindow, l # [hidden]
|
||||
bind = Super+Shift, L, movewindow, r # [hidden]
|
||||
bind = Super+Shift, K, movewindow, u # [hidden]
|
||||
bind = Super+Shift, J, movewindow, d # [hidden]
|
||||
|
||||
|
||||
bind = Super+Shift, 1, movetoworkspace, 1 # [hidden]
|
||||
bind = Super+Shift, 2, movetoworkspace, 2 # [hidden]
|
||||
bind = Super+Shift, 3, movetoworkspace, 3 # [hidden]
|
||||
bind = Super+Shift, 4, movetoworkspace, 4 # [hidden]
|
||||
bind = Super+Shift, 5, movetoworkspace, 5 # [hidden]
|
||||
bind = Super+Shift, 6, movetoworkspace, 6 # [hidden]
|
||||
bind = Super+Shift, 7, movetoworkspace, 7 # [hidden]
|
||||
bind = Super+Shift, 8, movetoworkspace, 8 # [hidden]
|
||||
bind = Super+Shift, 9, movetoworkspace, 9 # [hidden]
|
||||
bind = Super+Shift, 0, movetoworkspace, 0 # [hidden]
|
||||
|
||||
bindd = Super+Ctrl, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard
|
||||
bindd = Super+Ctrl, J, Toggle bar, global, quickshell:barToggle # Toggle bar
|
||||
|
||||
# gaming
|
||||
bind = Super, G, togglespecialworkspace, gaming
|
||||
bind = Super+Shift, G, movetoworkspace, special:gaming
|
||||
bind = Super, T, togglespecialworkspace, steam
|
||||
bind = Super+Shift, T, movetoworkspace, special:steam
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# You can put custom rules here
|
||||
# Window/layer rules: https://wiki.hyprland.org/Configuring/Window-Rules/
|
||||
# Workspace rules: https://wiki.hyprland.org/Configuring/Workspace-Rules/
|
||||
windowrule = workspace special:steam, class:steam
|
||||
windowrule = workspace special:gaming, class:^(steam_app_).*
|
||||
|
||||
workspace = special:gaming, monitor:DP-1, persistent:true
|
||||
workspace = special:steam, monitor:DP-1, persistent:true, on-created-empty:steam
|
||||
|
||||
@@ -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,27 +0,0 @@
|
||||
# ######### Input method ##########
|
||||
# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland
|
||||
env = QT_IM_MODULE, fcitx
|
||||
env = XMODIFIERS, @im=fcitx
|
||||
env = SDL_IM_MODULE, fcitx
|
||||
env = GLFW_IM_MODULE, ibus
|
||||
env = INPUT_METHOD, fcitx
|
||||
|
||||
# ############ Wayland #############
|
||||
env = ELECTRON_OZONE_PLATFORM_HINT,auto
|
||||
|
||||
# ############ Themes #############
|
||||
env = QT_QPA_PLATFORM, wayland
|
||||
env = QT_QPA_PLATFORMTHEME, kde
|
||||
env = XDG_MENU_PREFIX, plasma-
|
||||
|
||||
# ######## Wayland #########
|
||||
# Tearing
|
||||
# env = WLR_DRM_NO_ATOMIC, 1
|
||||
# ?
|
||||
# env = WLR_NO_HARDWARE_CURSORS, 1
|
||||
|
||||
# ######## Virtual envrionment #########
|
||||
env = ILLOGICAL_IMPULSE_VIRTUAL_ENV, ~/.local/state/quickshell/.venv
|
||||
|
||||
# ######## Terminal application #########
|
||||
env = TERMINAL,kitty -1
|
||||
@@ -1,218 +0,0 @@
|
||||
# Lines ending with `# [hidden]` won't be shown on cheatsheet
|
||||
# Lines starting with #! are section headings
|
||||
|
||||
#!
|
||||
##! Shell
|
||||
# These absolutely need to be on top, or they won't work consistently
|
||||
bindid = Super, Super_L, Toggle overview, global, quickshell:overviewToggleRelease # Toggle overview/launcher
|
||||
bind = Super, Super_L, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || fuzzel # [hidden] Launcher (fallback)
|
||||
binditn = Super, catchall, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Ctrl, Super_L, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:272, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:273, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:274, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:275, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:276, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse:277, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse_up, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
bind = Super, mouse_down,global, quickshell:overviewToggleReleaseInterrupt # [hidden]
|
||||
|
||||
bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden]
|
||||
bindd = Super, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard
|
||||
bindd = Super, Period, Emoji >> clipboard, global, quickshell:overviewEmojiToggle # Emoji >> clipboard
|
||||
bindd = Super, Tab, Toggle overview, global, quickshell:overviewToggle # [hidden] Toggle overview/launcher (alt)
|
||||
bindd = Super, A, Toggle left sidebar, global, quickshell:sidebarLeftToggle # Toggle left sidebar
|
||||
bind = Super+Alt, A, global, quickshell:sidebarLeftToggleDetach # [hidden]
|
||||
bind = Super, B, global, quickshell:sidebarLeftToggle # [hidden]
|
||||
bind = Super, O, global, quickshell:sidebarLeftToggle # [hidden]
|
||||
bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle # Toggle right sidebar
|
||||
bindd = Super, Slash, Toggle cheatsheet, global, quickshell:ch+CtrleatsheetToggle # Toggle cheatsheet
|
||||
# bindd = Super, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard
|
||||
bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls
|
||||
bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
|
||||
# bindd = Super, J, Toggle bar, global, quickshell:barToggle # Toggle bar
|
||||
bind = Ctrl+Alt, Delete, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback)
|
||||
bind = Shift+Super+Alt, Slash, exec, qs -p ~/.config/quickshell/$qsConfig/welcome.qml # [hidden] Launch welcome app
|
||||
|
||||
bindle=, XF86MonBrightnessUp, exec, qs -c $qsConfig ipc call brightness increment || brightnessctl s 5%+ # [hidden]
|
||||
bindle=, XF86MonBrightnessDown, exec, qs -c $qsConfig ipc call brightness decrement || brightnessctl s 5%- # [hidden]
|
||||
bindle=, XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 2%+ # [hidden]
|
||||
bindle=, XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 2%- # [hidden]
|
||||
|
||||
bindl = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SINK@ toggle # [hidden]
|
||||
bindld = Super+Shift,M, Toggle mute, exec, wpctl set-mute @DEFAULT_SINK@ toggle # [hidden]
|
||||
bindl = Alt ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||
bindl = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||
bindld = Super+Alt,M, Toggle mic, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden]
|
||||
bindd = Ctrl+Super, T, Change wallpaper, exec, ~/.config/quickshell/$qsConfig/scripts/colors/switchwall.sh # Change wallpaper
|
||||
bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs -c $qsConfig & # Restart widgets
|
||||
|
||||
##! Utilities
|
||||
# Screenshot, Record, OCR, Color picker, Clipboard history
|
||||
bindd = Super, V, Copy clipboard history entry, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # [hidden] Clipboard history >> clipboard (fallback)
|
||||
bindd = Super, Period, Copy an emoji, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || ~/.config/hypr/hyprland/scripts/fuzzel-emoji.sh copy # [hidden] Emoji >> clipboard (fallback)
|
||||
bindd = Super+Shift, S, Screen snip, exec, hyprshot --clipboard-only --mode region --silent || qs -p ~/.config/quickshell/$qsConfig/screenshot.qml || pidof slurp # Screen snip
|
||||
# OCR
|
||||
bindd = Super+Shift, E, Character recognition,exec,grim -g "$(slurp $SLURP_ARGS)" "tmp.png" && tesseract "tmp.png" - | wl-copy && rm "tmp.png" # [hidden]
|
||||
# Color picker
|
||||
bindd = Super+Shift, C, Color picker, exec, hyprpicker -a # Pick color (Hex) >> clipboard
|
||||
# Fullscreen screenshot
|
||||
bindld = ,Print, Screenshot >> clipboard ,exec,grim - | wl-copy # Screenshot >> clipboard
|
||||
bindld = Ctrl,Print, Screenshot >> clipboard & save, exec, mkdir -p $(xdg-user-dir PICTURES)/Screenshots && grim $(xdg-user-dir PICTURES)/Screenshots/Screenshot_"$(date '+%Y-%m-%d_%H.%M.%S')".png # Screenshot >> clipboard & file
|
||||
# Recording stuff
|
||||
bindd = Super+Alt, R, Record region (no sound), exec, ~/.config/hypr/hyprland/scripts/record.sh # Record region (no sound)
|
||||
bindd = Ctrl+Alt, R, Record screen (no sound), exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen # [hidden] Record screen (no sound)
|
||||
bindd = Super+Shift+Alt, R, Record screen (with sound), exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen-sound # Record screen (with sound)
|
||||
# AI
|
||||
bindd = Super+Shift+Alt, mouse:273, Generate AI summary for selected text, exec, ~/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh # AI summary for selected text
|
||||
|
||||
#!
|
||||
##! Window
|
||||
# Focusing
|
||||
bindm = Super, mouse:272, movewindow # Move
|
||||
bindm = Super, mouse:274, movewindow # [hidden]
|
||||
bindm = Super, mouse:273, resizewindow # Resize
|
||||
#/# bind = Super, ←/↑/→/↓,, # Focus in direction
|
||||
bind = Super, Left, movefocus, l # [hidden]
|
||||
bind = Super, Right, movefocus, r # [hidden]
|
||||
bind = Super, Up, movefocus, u # [hidden]
|
||||
bind = Super, Down, movefocus, d # [hidden]
|
||||
bind = Super, BracketLeft, movefocus, l # [hidden]
|
||||
bind = Super, BracketRight, movefocus, r # [hidden]
|
||||
#/# bind = Super+Shift, ←/↑/→/↓,, # Move in direction
|
||||
bind = Super+Shift, Left, movewindow, l # [hidden]
|
||||
bind = Super+Shift, Right, movewindow, r # [hidden]
|
||||
bind = Super+Shift, Up, movewindow, u # [hidden]
|
||||
bind = Super+Shift, Down, movewindow, d # [hidden]
|
||||
bind = Alt, F4, killactive, # [hidden] Close (Windows)
|
||||
bind = Super, Q, killactive, # Close
|
||||
bind = Super+Shift+Alt, Q, exec, hyprctl kill # Forcefully zap a window
|
||||
|
||||
|
||||
# Window split ratio
|
||||
#/# binde = Super, ;/',, # Adjust split ratio
|
||||
binde = Super, Semicolon, splitratio, -0.1 # [hidden]
|
||||
binde = Super, Apostrophe, splitratio, +0.1 # [hidden]
|
||||
# Positioning mode
|
||||
bind = Super+Alt, Space, togglefloating, # Float/Tile
|
||||
bind = Super, D, fullscreen, 1 # Maximize
|
||||
bind = Super, F, fullscreen, 0 # Fullscreen
|
||||
bind = Super+Alt, F, fullscreenstate, 0 3 # Fullscreen spoof
|
||||
bind = Super, P, pin # Pin
|
||||
|
||||
#/# bind = Super+Alt, Hash,, # Send to workspace # (1, 2, 3,...)
|
||||
bind = Super+Alt, 1, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 1 # [hidden]
|
||||
bind = Super+Alt, 2, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 2 # [hidden]
|
||||
bind = Super+Alt, 3, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 3 # [hidden]
|
||||
bind = Super+Alt, 4, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 4 # [hidden]
|
||||
bind = Super+Alt, 5, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 5 # [hidden]
|
||||
bind = Super+Alt, 6, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 6 # [hidden]
|
||||
bind = Super+Alt, 7, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 7 # [hidden]
|
||||
bind = Super+Alt, 8, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 8 # [hidden]
|
||||
bind = Super+Alt, 9, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 9 # [hidden]
|
||||
bind = Super+Alt, 0, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh movetoworkspacesilent 10 # [hidden]
|
||||
|
||||
# #/# bind = Super+Shift, Scroll ↑/↓,, # Send to workspace left/right
|
||||
bind = Super+Shift, mouse_down, movetoworkspace, r-1 # [hidden]
|
||||
bind = Super+Shift, mouse_up, movetoworkspace, r+1 # [hidden]
|
||||
bind = Super+Alt, mouse_down, movetoworkspace, -1 # [hidden]
|
||||
bind = Super+Alt, mouse_up, movetoworkspace, +1 # [hidden]
|
||||
|
||||
#/# bind = Super+Shift, Page_↑/↓,, # Send to workspace left/right
|
||||
bind = Super+Alt, Page_Down, movetoworkspace, +1 # [hidden]
|
||||
bind = Super+Alt, Page_Up, movetoworkspace, -1 # [hidden]
|
||||
bind = Super+Shift, Page_Down, movetoworkspace, r+1 # [hidden]
|
||||
bind = Super+Shift, Page_Up, movetoworkspace, r-1 # [hidden]
|
||||
bind = Ctrl+Super+Shift, Right, movetoworkspace, r+1 # [hidden]
|
||||
bind = Ctrl+Super+Shift, Left, movetoworkspace, r-1 # [hidden]
|
||||
|
||||
bind = Super+Alt, S, movetoworkspacesilent, special # Send to scratchpad
|
||||
|
||||
bind = Ctrl+Super, S, togglespecialworkspace, # [hidden]
|
||||
bind = Alt, Tab, cyclenext # [hidden] sus keybind
|
||||
bind = Alt, Tab, bringactivetotop, # [hidden] bring it to the top
|
||||
|
||||
##! Workspace
|
||||
# Switching
|
||||
#/# bind = Super, Hash,, # Focus workspace # (1, 2, 3,...)
|
||||
bind = Super, 1, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 1 # [hidden]
|
||||
bind = Super, 2, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 2 # [hidden]
|
||||
bind = Super, 3, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 3 # [hidden]
|
||||
bind = Super, 4, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 4 # [hidden]
|
||||
bind = Super, 5, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 5 # [hidden]
|
||||
bind = Super, 6, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 6 # [hidden]
|
||||
bind = Super, 7, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 7 # [hidden]
|
||||
bind = Super, 8, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 8 # [hidden]
|
||||
bind = Super, 9, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 9 # [hidden]
|
||||
bind = Super, 0, exec, ~/.config/hypr/hyprland/scripts/workspace_action.sh workspace 10 # [hidden]
|
||||
|
||||
#/# bind = Ctrl+Super, ←/→,, # Focus left/right
|
||||
bind = Ctrl+Super, Right, workspace, r+1 # [hidden]
|
||||
bind = Ctrl+Super, Left, workspace, r-1 # [hidden]
|
||||
#/# bind = Ctrl+Super+Alt, ←/→,, # [hidden] Focus busy left/right
|
||||
bind = Ctrl+Super+Alt, Right, workspace, m+1 # [hidden]
|
||||
bind = Ctrl+Super+Alt, Left, workspace, m-1 # [hidden]
|
||||
#/# bind = Super, Page_↑/↓,, # Focus left/right
|
||||
bind = Super, Page_Down, workspace, +1 # [hidden]
|
||||
bind = Super, Page_Up, workspace, -1 # [hidden]
|
||||
bind = Ctrl+Super, Page_Down, workspace, r+1 # [hidden]
|
||||
bind = Ctrl+Super, Page_Up, workspace, r-1 # [hidden]
|
||||
#/# bind = Super, Scroll ↑/↓,, # Focus left/right
|
||||
bind = Super, mouse_up, workspace, +1 # [hidden]
|
||||
bind = Super, mouse_down, workspace, -1 # [hidden]
|
||||
bind = Ctrl+Super, mouse_up, workspace, r+1 # [hidden]
|
||||
bind = Ctrl+Super, mouse_down, workspace, r-1 # [hidden]
|
||||
## Special
|
||||
bind = Super, S, togglespecialworkspace, # Toggle scratchpad
|
||||
bind = Super, mouse:275, togglespecialworkspace, # [hidden]
|
||||
bind = Ctrl+Super, BracketLeft, workspace, -1 # [hidden]
|
||||
bind = Ctrl+Super, BracketRight, workspace, +1 # [hidden]
|
||||
bind = Ctrl+Super, Up, workspace, r-5 # [hidden]
|
||||
bind = Ctrl+Super, Down, workspace, r+5 # [hidden]
|
||||
|
||||
#!
|
||||
# Testing
|
||||
bind = Super+Alt, f11, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification with body image" "This notification should contain your user account <b>image</b> and <a href=\"https://discord.com/app\">Discord</a> <b>icon</b>. Oh and here is a random image in your Pictures folder: <img src=\"$RANDOM_IMAGE\" alt=\"Testing image\"/>" -a "Hyprland keybind" -p -h "string:image-path:/var/lib/AccountsService/icons/$USER" -t 6000 -i "discord" -A "openImage=Open profile image" -A "action2=Open the random image" -A "action3=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"; [[ $ACTION == *action2 ]] && xdg-open \"$RANDOM_IMAGE\"' # [hidden]
|
||||
bind = Super+Alt, f12, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification" "This notification should contain a random image in your <b>Pictures</b> folder and <a href=\"https://discord.com/app\">Discord</a> <b>icon</b>.\n<i>Flick right to dismiss!</i>" -a "Discord (fake)" -p -h "string:image-path:$RANDOM_IMAGE" -t 6000 -i "discord" -A "openImage=Open profile image" -A "action2=Useless button" -A "action3=Cry more"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"' # [hidden]
|
||||
bind = Super+Alt, Equal, exec, notify-send "Urgent notification" "Ah hell no" -u critical -a 'Hyprland keybind' # [hidden]
|
||||
|
||||
##! Session
|
||||
# bindd = Super, L, Lock, exec, loginctl lock-session # Lock
|
||||
# bind = Super+Shift, L, exec, loginctl lock-session # [hidden]
|
||||
# bindld = Super+Shift, L, Suspend system, exec, sleep 0.1 && systemctl suspend || loginctl suspend # Sleep
|
||||
bindd = Ctrl+Shift+Alt+Super, Delete, Shutdown, exec, systemctl poweroff || loginctl poweroff # [hidden] Power off
|
||||
|
||||
##! Screen
|
||||
# Zoom
|
||||
binde = Super, Minus, exec, qs -c $qsConfig ipc call zoom zoomOut # Zoom out
|
||||
binde = Super, Equal, exec, qs -c $qsConfig ipc call zoom zoomIn # Zoom in
|
||||
binde = Super, Minus, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/hypr/hyprland/scripts/zoom.sh decrease 0.1 # [hidden] Zoom out
|
||||
binde = Super, Equal, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/hypr/hyprland/scripts/zoom.sh increase 0.1 # [hidden] Zoom in
|
||||
|
||||
##! Media
|
||||
bindl= Super+Shift, N, exec, playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` # Next track
|
||||
bindl= ,XF86AudioNext, exec, playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` # [hidden]
|
||||
bindl= ,XF86AudioPrev, exec, playerctl previous # [hidden]
|
||||
bind = Super+Shift+Alt, mouse:275, exec, playerctl previous # [hidden]
|
||||
bind = Super+Shift+Alt, mouse:276, exec, playerctl next || playerctl position `bc <<< "100 * $(playerctl metadata mpris:length) / 1000000 / 100"` # [hidden]
|
||||
bindl= Super+Shift, B, exec, playerctl previous # Previous track
|
||||
bindl= Super+Shift, P, exec, playerctl play-pause # Play/pause media
|
||||
bindl= ,XF86AudioPlay, exec, playerctl play-pause # [hidden]
|
||||
bindl= ,XF86AudioPause, exec, playerctl play-pause # [hidden]
|
||||
|
||||
##! Apps
|
||||
bind = Super, Return, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "$TERMINAL" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # Terminal
|
||||
# bind = Super, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "$TERMINAL" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] (terminal) (alt)
|
||||
bind = Ctrl+Alt, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "$TERMINAL" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] (terminal) (for Ubuntu people)
|
||||
bind = Super, E, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "dolphin" "nautilus" "nemo" "thunar" "$TERMINAL" "kitty -1 fish -c yazi" # File manager
|
||||
bind = Super, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "google-chrome-stable" "zen-browser" "firefox" "brave" "chromium" "microsoft-edge-stable" "opera" "librewolf" # Browser
|
||||
bind = Super, C, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "code" "codium" "cursor" "zed" "zedit" "zeditor" "kate" "gnome-text-editor" "emacs" "command -v nvim && kitty -1 nvim" # Code editor
|
||||
bind = Super+Shift, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "wps" "onlyoffice-desktopeditors" # Office software
|
||||
bind = Super, X, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kate" "gnome-text-editor" "emacs" # Text editor
|
||||
bind = Ctrl+Super, V, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "pavucontrol-qt" "pavucontrol" # Volume mixer
|
||||
bind = Super, I, exec, XDG_CURRENT_DESKTOP=gnome ~/.config/hypr/hyprland/scripts/launch_first_available.sh "qs -p ~/.config/quickshell/$qsConfig/settings.qml" "systemsettings" "gnome-control-center" "better-control" # Settings app
|
||||
bind = Ctrl+Shift, Escape, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "gnome-system-monitor" "plasma-systemmonitor --page-name Processes" "command -v btop && kitty -1 fish -c btop" # Task manager
|
||||
|
||||
# Cursed stuff
|
||||
## Make window not amogus large
|
||||
bind = Ctrl+Super, Backslash, resizeactive, exact 640 480 # [hidden]
|
||||
@@ -1,159 +0,0 @@
|
||||
# ######## Window rules ########
|
||||
|
||||
# Uncomment to apply global transparency to all windows:
|
||||
# windowrulev2 = opacity 0.89 override 0.89 override, class:.*
|
||||
|
||||
# Disable blur for xwayland context menus
|
||||
windowrulev2 = noblur,class:^()$,title:^()$
|
||||
# windowrulev2 = noblur, xwayland:1
|
||||
|
||||
|
||||
# Floating
|
||||
windowrulev2 = float, class:^(blueberry\.py)$
|
||||
windowrulev2 = float, class:^(guifetch)$ # FlafyDev/guifetch
|
||||
windowrulev2 = float, class:^(pavucontrol)$
|
||||
windowrulev2 = size 45%, class:^(pavucontrol)$
|
||||
windowrulev2 = center, class:^(pavucontrol)$
|
||||
windowrulev2 = float, class:^(org.pulseaudio.pavucontrol)$
|
||||
windowrulev2 = size 45%, class:^(org.pulseaudio.pavucontrol)$
|
||||
windowrulev2 = center, class:^(org.pulseaudio.pavucontrol)$
|
||||
windowrulev2 = float, class:^(nm-connection-editor)$
|
||||
windowrulev2 = size 45%, class:^(nm-connection-editor)$
|
||||
windowrulev2 = center, class:^(nm-connection-editor)$
|
||||
windowrulev2 = float, class:.*plasmawindowed.*
|
||||
windowrulev2 = float, class:kcm_.*
|
||||
windowrulev2 = float, class:.*bluedevilwizard
|
||||
windowrulev2 = float, title:.*Welcome
|
||||
windowrulev2 = float, title:^(illogical-impulse Settings)$
|
||||
windowrulev2 = float, class:org.freedesktop.impl.portal.desktop.kde
|
||||
windowrulev2 = float, class:^(Zotero)$
|
||||
windowrulev2 = size 45%, class:^(Zotero)$
|
||||
|
||||
|
||||
# Move
|
||||
# kde-material-you-colors spawns a window when changing dark/light theme. This is to make sure it doesn't interfere at all.
|
||||
windowrulev2 = float, class:^(plasma-changeicons)$
|
||||
windowrulev2 = noinitialfocus, class:^(plasma-changeicons)$
|
||||
windowrulev2 = move 999999 999999, class:^(plasma-changeicons)$
|
||||
# stupid dolphin copy
|
||||
windowrulev2 = move 40 80, title:^(Copying — Dolphin)$
|
||||
|
||||
# Tiling
|
||||
windowrulev2 = tile, class:^dev\.warp\.Warp$
|
||||
|
||||
# Picture-in-Picture
|
||||
windowrulev2 = float, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
windowrulev2 = keepaspectratio, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
windowrulev2 = move 73% 72%, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
windowrulev2 = size 25%, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
windowrulev2 = float, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
windowrulev2 = pin, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$
|
||||
|
||||
# Dialog windows – float+center these windows.
|
||||
windowrulev2 = center, title:^(Open File)(.*)$
|
||||
windowrulev2 = center, title:^(Select a File)(.*)$
|
||||
windowrulev2 = center, title:^(Choose wallpaper)(.*)$
|
||||
windowrulev2 = center, title:^(Open Folder)(.*)$
|
||||
windowrulev2 = center, title:^(Save As)(.*)$
|
||||
windowrulev2 = center, title:^(Library)(.*)$
|
||||
windowrulev2 = center, title:^(File Upload)(.*)$
|
||||
windowrulev2 = center, title:^(.*)(wants to save)$
|
||||
windowrulev2 = center, title:^(.*)(wants to open)$
|
||||
windowrulev2 = float, title:^(Open File)(.*)$
|
||||
windowrulev2 = float, title:^(Select a File)(.*)$
|
||||
windowrulev2 = float, title:^(Choose wallpaper)(.*)$
|
||||
windowrulev2 = float, title:^(Open Folder)(.*)$
|
||||
windowrulev2 = float, title:^(Save As)(.*)$
|
||||
windowrulev2 = float, title:^(Library)(.*)$
|
||||
windowrulev2 = float, title:^(File Upload)(.*)$
|
||||
windowrulev2 = float, title:^(.*)(wants to save)$
|
||||
windowrulev2 = float, title:^(.*)(wants to open)$
|
||||
|
||||
|
||||
# --- Tearing ---
|
||||
windowrulev2 = immediate, title:.*\.exe
|
||||
windowrulev2 = immediate, title:.*minecraft.*
|
||||
windowrulev2 = immediate, class:^(steam_app).*
|
||||
|
||||
# No shadow for tiled windows (matches windows that are not floating).
|
||||
windowrulev2 = noshadow, floating:0
|
||||
|
||||
# ######## Workspace rules ########
|
||||
workspace = special:special, gapsout:30
|
||||
|
||||
# ######## Layer rules ########
|
||||
layerrule = xray 1, .*
|
||||
# layerrule = noanim, .*
|
||||
layerrule = noanim, walker
|
||||
layerrule = noanim, selection
|
||||
layerrule = noanim, overview
|
||||
layerrule = noanim, anyrun
|
||||
layerrule = noanim, indicator.*
|
||||
layerrule = noanim, osk
|
||||
layerrule = noanim, hyprpicker
|
||||
|
||||
layerrule = noanim, noanim
|
||||
layerrule = blur, gtk-layer-shell
|
||||
layerrule = ignorezero, gtk-layer-shell
|
||||
layerrule = blur, launcher
|
||||
layerrule = ignorealpha 0.5, launcher
|
||||
layerrule = blur, notifications
|
||||
layerrule = ignorealpha 0.69, notifications
|
||||
layerrule = blur, logout_dialog # wlogout
|
||||
|
||||
# ags
|
||||
layerrule = animation slide left, sideleft.*
|
||||
layerrule = animation slide right, sideright.*
|
||||
layerrule = blur, session[0-9]*
|
||||
layerrule = blur, bar[0-9]*
|
||||
layerrule = ignorealpha 0.6, bar[0-9]*
|
||||
layerrule = blur, barcorner.*
|
||||
layerrule = ignorealpha 0.6, barcorner.*
|
||||
layerrule = blur, dock[0-9]*
|
||||
layerrule = ignorealpha 0.6, dock[0-9]*
|
||||
layerrule = blur, indicator.*
|
||||
layerrule = ignorealpha 0.6, indicator.*
|
||||
layerrule = blur, overview[0-9]*
|
||||
layerrule = ignorealpha 0.6, overview[0-9]*
|
||||
layerrule = blur, cheatsheet[0-9]*
|
||||
layerrule = ignorealpha 0.6, cheatsheet[0-9]*
|
||||
layerrule = blur, sideright[0-9]*
|
||||
layerrule = ignorealpha 0.6, sideright[0-9]*
|
||||
layerrule = blur, sideleft[0-9]*
|
||||
layerrule = ignorealpha 0.6, sideleft[0-9]*
|
||||
layerrule = blur, indicator.*
|
||||
layerrule = ignorealpha 0.6, indicator.*
|
||||
layerrule = blur, osk[0-9]*
|
||||
layerrule = ignorealpha 0.6, osk[0-9]*
|
||||
|
||||
# Quickshell
|
||||
layerrule = blurpopups, quickshell:.*
|
||||
layerrule = blur, quickshell:.*
|
||||
layerrule = ignorealpha 0.79, quickshell:.*
|
||||
layerrule = animation slide top, quickshell:bar
|
||||
layerrule = animation fade, quickshell:screenCorners
|
||||
layerrule = animation slide right, quickshell:sidebarRight
|
||||
layerrule = animation slide left, quickshell:sidebarLeft
|
||||
layerrule = animation slide bottom, quickshell:osk
|
||||
layerrule = animation slide bottom, quickshell:dock
|
||||
layerrule = blur, quickshell:session
|
||||
layerrule = noanim, quickshell:session
|
||||
layerrule = ignorealpha 0, quickshell:session
|
||||
layerrule = animation fade, quickshell:notificationPopup
|
||||
layerrule = blur, quickshell:backgroundWidgets
|
||||
layerrule = ignorealpha 0.05, quickshell:backgroundWidgets
|
||||
layerrule = noanim, quickshell:screenshot
|
||||
layerrule = animation popin 120%, quickshell:screenCorners
|
||||
layerrule = noanim, quickshell:lockWindowPusher
|
||||
|
||||
|
||||
# Launchers need to be FAST
|
||||
layerrule = noanim, quickshell:overview
|
||||
layerrule = noanim, gtk4-layer-shell
|
||||
## outfoxxed's stuff
|
||||
layerrule = blur, shell:bar
|
||||
layerrule = ignorezero, shell:bar
|
||||
layerrule = blur, shell:notifications
|
||||
layerrule = ignorealpha 0.1, shell:notifications
|
||||
|
||||
|
||||
@@ -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,22 +0,0 @@
|
||||
/*
|
||||
* GTK Colors
|
||||
* Generated with Matugen
|
||||
*/
|
||||
|
||||
@define-color accent_color {{colors.primary.default.hex}};
|
||||
@define-color accent_fg_color {{colors.on_primary.default.hex}};
|
||||
@define-color accent_bg_color {{colors.primary.default.hex}};
|
||||
@define-color window_bg_color {{colors.background.default.hex}};
|
||||
@define-color window_fg_color {{colors.on_background.default.hex}};
|
||||
@define-color headerbar_bg_color {{colors.surface_dim.default.hex}};
|
||||
@define-color headerbar_fg_color {{colors.on_surface.default.hex}};
|
||||
@define-color popover_bg_color {{colors.surface_dim.default.hex}};
|
||||
@define-color popover_fg_color {{colors.on_surface.default.hex}};
|
||||
@define-color view_bg_color {{colors.surface.default.hex}};
|
||||
@define-color view_fg_color {{colors.on_surface.default.hex}};
|
||||
@define-color card_bg_color {{colors.surface.default.hex}};
|
||||
@define-color card_fg_color {{colors.on_surface.default.hex}};
|
||||
@define-color sidebar_bg_color @window_bg_color;
|
||||
@define-color sidebar_fg_color @window_fg_color;
|
||||
@define-color sidebar_border_color @window_bg_color;
|
||||
@define-color sidebar_backdrop_color @window_bg_color;
|
||||
@@ -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,32 +0,0 @@
|
||||
[Appearance]
|
||||
color_scheme_path=/usr/share/qt5ct/colors/darker.conf
|
||||
custom_palette=true
|
||||
icon_theme=OneUI-dark
|
||||
standard_dialogs=default
|
||||
style=kvantum-dark
|
||||
|
||||
[Fonts]
|
||||
fixed="JetBrainsMono Nerd Font,11,-1,5,50,0,0,0,0,0,Regular"
|
||||
general="Rubik,11,-1,5,50,0,0,0,0,0"
|
||||
|
||||
[Interface]
|
||||
activate_item_on_single_click=1
|
||||
buttonbox_layout=0
|
||||
cursor_flash_time=1000
|
||||
dialog_buttons_have_icons=1
|
||||
double_click_interval=400
|
||||
gui_effects=General
|
||||
keyboard_scheme=2
|
||||
menus_have_icons=true
|
||||
show_shortcuts_in_context_menus=true
|
||||
stylesheets=@Invalid()
|
||||
toolbutton_style=4
|
||||
underline_shortcut=1
|
||||
wheel_scroll_lines=3
|
||||
|
||||
[SettingsWindow]
|
||||
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\0\0\0\x4\x98\0\0\x3\x99\0\0\0\0\0\0\0\0\0\0\x2\xde\0\0\x3\x1\0\0\0\0\x2\0\0\0\a\x80\0\0\0\0\0\0\0\0\0\0\x4\x98\0\0\x3\x99)
|
||||
|
||||
[Troubleshooting]
|
||||
force_raster_widgets=1
|
||||
ignored_applications=@Invalid()
|
||||
@@ -1,32 +0,0 @@
|
||||
[Appearance]
|
||||
color_scheme_path=~/.config/qt6ct/style-colors.conf
|
||||
custom_palette=true
|
||||
icon_theme=OneUI
|
||||
standard_dialogs=default
|
||||
style=kvantum
|
||||
|
||||
[Fonts]
|
||||
fixed="JetBrainsMono Nerd Font,11,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular"
|
||||
general="Rubik,11,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular"
|
||||
|
||||
[Interface]
|
||||
activate_item_on_single_click=1
|
||||
buttonbox_layout=0
|
||||
cursor_flash_time=1000
|
||||
dialog_buttons_have_icons=1
|
||||
double_click_interval=400
|
||||
gui_effects=@Invalid()
|
||||
keyboard_scheme=2
|
||||
menus_have_icons=true
|
||||
show_shortcuts_in_context_menus=true
|
||||
stylesheets=@Invalid()
|
||||
toolbutton_style=4
|
||||
underline_shortcut=1
|
||||
wheel_scroll_lines=3
|
||||
|
||||
[SettingsWindow]
|
||||
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\0\0\0\as\0\0\x4\x3\0\0\0\0\0\0\0\0\0\0\as\0\0\x4\x3\0\0\0\0\x2\0\0\0\a\x80\0\0\0\0\0\0\0\0\0\0\as\0\0\x4\x3)
|
||||
|
||||
[Troubleshooting]
|
||||
force_raster_widgets=1
|
||||
ignored_applications=@Invalid()
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
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,315 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.modules.common.functions
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property QtObject m3colors
|
||||
property QtObject animation
|
||||
property QtObject animationCurves
|
||||
property QtObject colors
|
||||
property QtObject rounding
|
||||
property QtObject font
|
||||
property QtObject sizes
|
||||
property string syntaxHighlightingTheme
|
||||
|
||||
// Extremely conservative transparency values for consistency and readability
|
||||
property real transparency: Config.options?.appearance.transparency ? (m3colors.darkmode ? 0.1 : 0.07) : 0
|
||||
property real contentTransparency: Config.options?.appearance.transparency ? (m3colors.darkmode ? 0.55 : 0.55) : 0
|
||||
|
||||
m3colors: QtObject {
|
||||
property bool darkmode: false
|
||||
property bool transparent: false
|
||||
property color m3primary_paletteKeyColor: "#91689E"
|
||||
property color m3secondary_paletteKeyColor: "#837186"
|
||||
property color m3tertiary_paletteKeyColor: "#9D6A67"
|
||||
property color m3neutral_paletteKeyColor: "#7C757B"
|
||||
property color m3neutral_variant_paletteKeyColor: "#7D747D"
|
||||
property color m3background: "#161217"
|
||||
property color m3onBackground: "#EAE0E7"
|
||||
property color m3surface: "#161217"
|
||||
property color m3surfaceDim: "#161217"
|
||||
property color m3surfaceBright: "#3D373D"
|
||||
property color m3surfaceContainerLowest: "#110D12"
|
||||
property color m3surfaceContainerLow: "#1F1A1F"
|
||||
property color m3surfaceContainer: "#231E23"
|
||||
property color m3surfaceContainerHigh: "#2D282E"
|
||||
property color m3surfaceContainerHighest: "#383339"
|
||||
property color m3onSurface: "#EAE0E7"
|
||||
property color m3surfaceVariant: "#4C444D"
|
||||
property color m3onSurfaceVariant: "#CFC3CD"
|
||||
property color m3inverseSurface: "#EAE0E7"
|
||||
property color m3inverseOnSurface: "#342F34"
|
||||
property color m3outline: "#988E97"
|
||||
property color m3outlineVariant: "#4C444D"
|
||||
property color m3shadow: "#000000"
|
||||
property color m3scrim: "#000000"
|
||||
property color m3surfaceTint: "#E5B6F2"
|
||||
property color m3primary: "#E5B6F2"
|
||||
property color m3onPrimary: "#452152"
|
||||
property color m3primaryContainer: "#5D386A"
|
||||
property color m3onPrimaryContainer: "#F9D8FF"
|
||||
property color m3inversePrimary: "#775084"
|
||||
property color m3secondary: "#D5C0D7"
|
||||
property color m3onSecondary: "#392C3D"
|
||||
property color m3secondaryContainer: "#534457"
|
||||
property color m3onSecondaryContainer: "#F2DCF3"
|
||||
property color m3tertiary: "#F5B7B3"
|
||||
property color m3onTertiary: "#4C2523"
|
||||
property color m3tertiaryContainer: "#BA837F"
|
||||
property color m3onTertiaryContainer: "#000000"
|
||||
property color m3error: "#FFB4AB"
|
||||
property color m3onError: "#690005"
|
||||
property color m3errorContainer: "#93000A"
|
||||
property color m3onErrorContainer: "#FFDAD6"
|
||||
property color m3primaryFixed: "#F9D8FF"
|
||||
property color m3primaryFixedDim: "#E5B6F2"
|
||||
property color m3onPrimaryFixed: "#2E0A3C"
|
||||
property color m3onPrimaryFixedVariant: "#5D386A"
|
||||
property color m3secondaryFixed: "#F2DCF3"
|
||||
property color m3secondaryFixedDim: "#D5C0D7"
|
||||
property color m3onSecondaryFixed: "#241727"
|
||||
property color m3onSecondaryFixedVariant: "#514254"
|
||||
property color m3tertiaryFixed: "#FFDAD7"
|
||||
property color m3tertiaryFixedDim: "#F5B7B3"
|
||||
property color m3onTertiaryFixed: "#331110"
|
||||
property color m3onTertiaryFixedVariant: "#663B39"
|
||||
property color m3success: "#B5CCBA"
|
||||
property color m3onSuccess: "#213528"
|
||||
property color m3successContainer: "#374B3E"
|
||||
property color m3onSuccessContainer: "#D1E9D6"
|
||||
property color term0: "#EDE4E4"
|
||||
property color term1: "#B52755"
|
||||
property color term2: "#A97363"
|
||||
property color term3: "#AF535D"
|
||||
property color term4: "#A67F7C"
|
||||
property color term5: "#B2416B"
|
||||
property color term6: "#8D76AD"
|
||||
property color term7: "#272022"
|
||||
property color term8: "#0E0D0D"
|
||||
property color term9: "#B52755"
|
||||
property color term10: "#A97363"
|
||||
property color term11: "#AF535D"
|
||||
property color term12: "#A67F7C"
|
||||
property color term13: "#B2416B"
|
||||
property color term14: "#8D76AD"
|
||||
property color term15: "#221A1A"
|
||||
}
|
||||
|
||||
colors: QtObject {
|
||||
property color colSubtext: m3colors.m3outline
|
||||
property color colLayer0: ColorUtils.mix(ColorUtils.transparentize(m3colors.m3background, root.transparency), m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1)
|
||||
property color colOnLayer0: m3colors.m3onBackground
|
||||
property color colLayer0Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.9, root.contentTransparency))
|
||||
property color colLayer0Active: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.8, root.contentTransparency))
|
||||
property color colLayer0Border: ColorUtils.mix(root.m3colors.m3outlineVariant, colLayer0, 0.4)
|
||||
property color colLayer1: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerLow, m3colors.m3background, 0.8), root.contentTransparency);
|
||||
property color colOnLayer1: m3colors.m3onSurfaceVariant;
|
||||
property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45);
|
||||
property color colLayer2: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.1), root.contentTransparency)
|
||||
property color colOnLayer2: m3colors.m3onSurface;
|
||||
property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4);
|
||||
property color colLayer3: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96), root.contentTransparency)
|
||||
property color colOnLayer3: m3colors.m3onSurface;
|
||||
property color colLayer1Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.92), root.contentTransparency)
|
||||
property color colLayer1Active: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.85), root.contentTransparency);
|
||||
property color colLayer2Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.90), root.contentTransparency)
|
||||
property color colLayer2Active: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.80), root.contentTransparency);
|
||||
property color colLayer2Disabled: ColorUtils.transparentize(ColorUtils.mix(colLayer2, m3colors.m3background, 0.8), root.contentTransparency);
|
||||
property color colLayer3Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.90), root.contentTransparency)
|
||||
property color colLayer3Active: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.80), root.contentTransparency);
|
||||
property color colPrimary: m3colors.m3primary
|
||||
property color colOnPrimary: m3colors.m3onPrimary
|
||||
property color colPrimaryHover: ColorUtils.mix(colors.colPrimary, colLayer1Hover, 0.87)
|
||||
property color colPrimaryActive: ColorUtils.mix(colors.colPrimary, colLayer1Active, 0.7)
|
||||
property color colPrimaryContainer: m3colors.m3primaryContainer
|
||||
property color colPrimaryContainerHover: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Hover, 0.7)
|
||||
property color colPrimaryContainerActive: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Active, 0.6)
|
||||
property color colOnPrimaryContainer: m3colors.m3onPrimaryContainer
|
||||
property color colSecondary: m3colors.m3secondary
|
||||
property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85)
|
||||
property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4)
|
||||
property color colSecondaryContainer: m3colors.m3secondaryContainer
|
||||
property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.90)
|
||||
property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54)
|
||||
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
|
||||
property color colSurfaceContainerLow: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency)
|
||||
property color colSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency)
|
||||
property color colSurfaceContainerHigh: ColorUtils.transparentize(m3colors.m3surfaceContainerHigh, root.contentTransparency)
|
||||
property color colSurfaceContainerHighest: ColorUtils.transparentize(m3colors.m3surfaceContainerHighest, root.contentTransparency)
|
||||
property color colSurfaceContainerHighestHover: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95)
|
||||
property color colSurfaceContainerHighestActive: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85)
|
||||
property color colTooltip: m3colors.m3inverseSurface
|
||||
property color colOnTooltip: m3colors.m3inverseOnSurface
|
||||
property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5)
|
||||
property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.7)
|
||||
property color colOutlineVariant: m3colors.m3outlineVariant
|
||||
}
|
||||
|
||||
rounding: QtObject {
|
||||
property int unsharpen: 2
|
||||
property int unsharpenmore: 6
|
||||
property int verysmall: 8
|
||||
property int small: 12
|
||||
property int normal: 17
|
||||
property int large: 23
|
||||
property int verylarge: 30
|
||||
property int full: 9999
|
||||
property int screenRounding: large
|
||||
property int windowRounding: 18
|
||||
}
|
||||
|
||||
font: QtObject {
|
||||
property QtObject family: QtObject {
|
||||
property string main: "Rubik"
|
||||
property string title: "Gabarito"
|
||||
property string iconMaterial: "Material Symbols Rounded"
|
||||
property string iconNerd: "SpaceMono NF"
|
||||
property string monospace: "JetBrains Mono NF"
|
||||
property string reading: "Readex Pro"
|
||||
property string expressive: "Space Grotesk"
|
||||
}
|
||||
property QtObject pixelSize: QtObject {
|
||||
property int smallest: 10
|
||||
property int smaller: 12
|
||||
property int small: 15
|
||||
property int normal: 16
|
||||
property int large: 17
|
||||
property int larger: 19
|
||||
property int huge: 22
|
||||
property int hugeass: 23
|
||||
property int title: huge
|
||||
}
|
||||
}
|
||||
|
||||
animationCurves: QtObject {
|
||||
readonly property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.90, 1, 1] // Default, 350ms
|
||||
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1.00, 1, 1] // Default, 500ms
|
||||
readonly property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1] // Default, 650ms
|
||||
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1] // Default, 200ms
|
||||
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
|
||||
readonly property list<real> emphasizedFirstHalf: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82]
|
||||
readonly property list<real> emphasizedLastHalf: [5 / 24, 0.82, 0.25, 1, 1, 1]
|
||||
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
|
||||
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
|
||||
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
|
||||
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
|
||||
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
|
||||
readonly property real expressiveFastSpatialDuration: 350
|
||||
readonly property real expressiveDefaultSpatialDuration: 500
|
||||
readonly property real expressiveSlowSpatialDuration: 650
|
||||
readonly property real expressiveEffectsDuration: 200
|
||||
}
|
||||
|
||||
animation: QtObject {
|
||||
property QtObject elementMove: QtObject {
|
||||
property int duration: animationCurves.expressiveDefaultSpatialDuration
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.expressiveDefaultSpatial
|
||||
property int velocity: 650
|
||||
property Component numberAnimation: Component {
|
||||
NumberAnimation {
|
||||
duration: root.animation.elementMove.duration
|
||||
easing.type: root.animation.elementMove.type
|
||||
easing.bezierCurve: root.animation.elementMove.bezierCurve
|
||||
}
|
||||
}
|
||||
property Component colorAnimation: Component {
|
||||
ColorAnimation {
|
||||
duration: root.animation.elementMove.duration
|
||||
easing.type: root.animation.elementMove.type
|
||||
easing.bezierCurve: root.animation.elementMove.bezierCurve
|
||||
}
|
||||
}
|
||||
}
|
||||
property QtObject elementMoveEnter: QtObject {
|
||||
property int duration: 400
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.emphasizedDecel
|
||||
property int velocity: 650
|
||||
property Component numberAnimation: Component {
|
||||
NumberAnimation {
|
||||
duration: root.animation.elementMoveEnter.duration
|
||||
easing.type: root.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: root.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
}
|
||||
}
|
||||
property QtObject elementMoveExit: QtObject {
|
||||
property int duration: 200
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.emphasizedAccel
|
||||
property int velocity: 650
|
||||
property Component numberAnimation: Component {
|
||||
NumberAnimation {
|
||||
duration: root.animation.elementMoveExit.duration
|
||||
easing.type: root.animation.elementMoveExit.type
|
||||
easing.bezierCurve: root.animation.elementMoveExit.bezierCurve
|
||||
}
|
||||
}
|
||||
}
|
||||
property QtObject elementMoveFast: QtObject {
|
||||
property int duration: animationCurves.expressiveEffectsDuration
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.expressiveEffects
|
||||
property int velocity: 850
|
||||
property Component colorAnimation: Component { ColorAnimation {
|
||||
duration: root.animation.elementMoveFast.duration
|
||||
easing.type: root.animation.elementMoveFast.type
|
||||
easing.bezierCurve: root.animation.elementMoveFast.bezierCurve
|
||||
}}
|
||||
property Component numberAnimation: Component { NumberAnimation {
|
||||
duration: root.animation.elementMoveFast.duration
|
||||
easing.type: root.animation.elementMoveFast.type
|
||||
easing.bezierCurve: root.animation.elementMoveFast.bezierCurve
|
||||
}}
|
||||
}
|
||||
property QtObject clickBounce: QtObject {
|
||||
property int duration: 200
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.expressiveFastSpatial
|
||||
property int velocity: 850
|
||||
property Component numberAnimation: Component { NumberAnimation {
|
||||
duration: root.animation.clickBounce.duration
|
||||
easing.type: root.animation.clickBounce.type
|
||||
easing.bezierCurve: root.animation.clickBounce.bezierCurve
|
||||
}}
|
||||
}
|
||||
property QtObject scroll: QtObject {
|
||||
property int duration: 200
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.standardDecel
|
||||
}
|
||||
property QtObject menuDecel: QtObject {
|
||||
property int duration: 350
|
||||
property int type: Easing.OutExpo
|
||||
}
|
||||
}
|
||||
|
||||
sizes: QtObject {
|
||||
property real baseBarHeight: 40
|
||||
property real barHeight: Config.options.bar.cornerStyle === 1 ?
|
||||
(baseBarHeight + Appearance.sizes.hyprlandGapsOut * 2) : baseBarHeight
|
||||
property real barCenterSideModuleWidth: Config.options?.bar.verbose ? 360 : 140
|
||||
property real barCenterSideModuleWidthShortened: 280
|
||||
property real barCenterSideModuleWidthHellaShortened: 190
|
||||
property real barShortenScreenWidthThreshold: 1200 // Shorten if screen width is at most this value
|
||||
property real barHellaShortenScreenWidthThreshold: 1000 // Shorten even more...
|
||||
property real sidebarWidth: 460
|
||||
property real sidebarWidthExtended: 750
|
||||
property real osdWidth: 200
|
||||
property real mediaControlsWidth: 440
|
||||
property real mediaControlsHeight: 160
|
||||
property real notificationPopupWidth: 410
|
||||
property real searchWidthCollapsed: 260
|
||||
property real searchWidth: 450
|
||||
property real hyprlandGapsOut: 5
|
||||
property real elevationMargin: 10
|
||||
property real fabShadowRadius: 5
|
||||
property real fabHoveredShadowRadius: 7
|
||||
}
|
||||
|
||||
syntaxHighlightingTheme: Appearance.m3colors.darkmode ? "Monokai" : "ayu Light"
|
||||
}
|
||||
@@ -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,15 +0,0 @@
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Label {
|
||||
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,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,155 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Widgets
|
||||
|
||||
/**
|
||||
* Material 3 slider. See https://m3.material.io/components/sliders/overview
|
||||
* It doesn't exactly match the spec because it does not make sense to have stuff on a computer that fucking huge.
|
||||
* Should be at 3/4 scale...
|
||||
*/
|
||||
|
||||
Slider {
|
||||
id: root
|
||||
|
||||
property list<real> stopIndicatorValues: [1]
|
||||
enum Configuration {
|
||||
XS = 12,
|
||||
S = 18,
|
||||
M = 30,
|
||||
L = 42,
|
||||
XL = 72
|
||||
}
|
||||
|
||||
property var configuration: StyledSlider.Configuration.S
|
||||
|
||||
property real handleDefaultWidth: 3
|
||||
property real handlePressedWidth: 1.5
|
||||
|
||||
property color highlightColor: Appearance.colors.colPrimary
|
||||
property color trackColor: Appearance.colors.colSecondaryContainer
|
||||
property color handleColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
property color dotColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
property color dotColorHighlighted: Appearance.m3colors.m3onPrimary
|
||||
property real unsharpenRadius: Appearance.rounding.unsharpen
|
||||
property real trackWidth: configuration
|
||||
property real trackRadius: trackWidth >= StyledSlider.Configuration.XL ? 21
|
||||
: trackWidth >= StyledSlider.Configuration.L ? 12
|
||||
: trackWidth >= StyledSlider.Configuration.M ? 9
|
||||
: 6
|
||||
property real handleHeight: Math.max(33, trackWidth + 9)
|
||||
property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth
|
||||
property real handleMargins: 4
|
||||
onHandleMarginsChanged: {
|
||||
console.log("Handle margins changed to", handleMargins);
|
||||
}
|
||||
property real trackDotSize: 3
|
||||
property string tooltipContent: `${Math.round(value * 100)}%`
|
||||
|
||||
leftPadding: handleMargins
|
||||
rightPadding: handleMargins
|
||||
property real effectiveDraggingWidth: width - leftPadding - rightPadding
|
||||
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 1
|
||||
|
||||
Behavior on value { // This makes the adjusted value (like volume) shift smoothly
|
||||
SmoothedAnimation {
|
||||
velocity: Appearance.animation.elementMoveFast.velocity
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on handleMargins {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
component TrackDot: Rectangle {
|
||||
required property real value
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: root.handleMargins + (value * root.effectiveDraggingWidth) - (root.trackDotSize / 2)
|
||||
width: root.trackDotSize
|
||||
height: root.trackDotSize
|
||||
radius: Appearance.rounding.full
|
||||
color: value > root.visualPosition ? root.dotColor : root.dotColorHighlighted
|
||||
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onPressed: (mouse) => mouse.accepted = false
|
||||
cursorShape: root.pressed ? Qt.ClosedHandCursor : Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
background: Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
implicitHeight: trackWidth
|
||||
|
||||
// Fill left
|
||||
Rectangle {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
width: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
|
||||
height: trackWidth
|
||||
color: root.highlightColor
|
||||
topLeftRadius: root.trackRadius
|
||||
bottomLeftRadius: root.trackRadius
|
||||
topRightRadius: root.unsharpenRadius
|
||||
bottomRightRadius: root.unsharpenRadius
|
||||
}
|
||||
|
||||
// Fill right
|
||||
Rectangle {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: parent.right
|
||||
}
|
||||
width: root.handleMargins + ((1 - root.visualPosition) * root.effectiveDraggingWidth) - (root.handleWidth / 2 + root.handleMargins)
|
||||
height: trackWidth
|
||||
color: root.trackColor
|
||||
topRightRadius: root.trackRadius
|
||||
bottomRightRadius: root.trackRadius
|
||||
topLeftRadius: root.unsharpenRadius
|
||||
bottomLeftRadius: root.unsharpenRadius
|
||||
}
|
||||
|
||||
// Stop indicators
|
||||
Repeater {
|
||||
model: root.stopIndicatorValues
|
||||
TrackDot {
|
||||
required property real modelData
|
||||
value: modelData
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
id: handle
|
||||
|
||||
implicitWidth: root.handleWidth
|
||||
implicitHeight: root.handleHeight
|
||||
x: root.handleMargins + (root.visualPosition * root.effectiveDraggingWidth) - (root.handleWidth / 2)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: Appearance.rounding.full
|
||||
color: root.handleColor
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
StyledToolTip {
|
||||
extraVisibleCondition: root.pressed
|
||||
content: root.tooltipContent
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,77 +0,0 @@
|
||||
|
||||
/**
|
||||
* @param { string } summary
|
||||
* @returns { string }
|
||||
*/
|
||||
function findSuitableMaterialSymbol(summary = "") {
|
||||
const defaultType = 'chat';
|
||||
if(summary.length === 0) return defaultType;
|
||||
|
||||
const keywordsToTypes = {
|
||||
'reboot': 'restart_alt',
|
||||
'recording': 'screen_record',
|
||||
'battery': 'power',
|
||||
'power': 'power',
|
||||
'screenshot': 'screenshot_monitor',
|
||||
'welcome': 'waving_hand',
|
||||
'time': 'scheduleb',
|
||||
'installed': 'download',
|
||||
'configuration reloaded': 'reset_wrench',
|
||||
'config': 'reset_wrench',
|
||||
'update': 'update',
|
||||
'ai response': 'neurology',
|
||||
'control': 'settings',
|
||||
'upscale': 'compare',
|
||||
'install': 'deployed_code_update',
|
||||
'startswith:file': 'folder_copy', // Declarative startsWith check
|
||||
};
|
||||
|
||||
const lowerSummary = summary.toLowerCase();
|
||||
|
||||
for (const [keyword, type] of Object.entries(keywordsToTypes)) {
|
||||
if (keyword.startsWith('startswith:')) {
|
||||
const startsWithKeyword = keyword.replace('startswith:', '');
|
||||
if (lowerSummary.startsWith(startsWithKeyword)) {
|
||||
return type;
|
||||
}
|
||||
} else if (lowerSummary.includes(keyword)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { number | string | Date } timestamp
|
||||
* @returns { string }
|
||||
*/
|
||||
const getFriendlyNotifTimeString = (timestamp) => {
|
||||
if (!timestamp) return '';
|
||||
const messageTime = new Date(timestamp);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - messageTime.getTime();
|
||||
|
||||
// Less than 1 minute
|
||||
if (diffMs < 60000)
|
||||
return 'Now';
|
||||
|
||||
// Same day - show relative time
|
||||
if (messageTime.toDateString() === now.toDateString()) {
|
||||
const diffMinutes = Math.floor(diffMs / 60000);
|
||||
const diffHours = Math.floor(diffMs / 3600000);
|
||||
|
||||
if (diffHours > 0) {
|
||||
return `${diffHours}h`;
|
||||
} else {
|
||||
return `${diffMinutes}m`;
|
||||
}
|
||||
}
|
||||
|
||||
// Yesterday
|
||||
if (messageTime.toDateString() === new Date(now.getTime() - 86400000).toDateString())
|
||||
return 'Yesterday';
|
||||
|
||||
// Older dates
|
||||
return Qt.formatDateTime(messageTime, "MMMM dd");
|
||||
};
|
||||
@@ -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,246 +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.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: overviewScope
|
||||
property bool dontAutoCancelSearch: false
|
||||
Variants {
|
||||
id: overviewVariants
|
||||
model: Quickshell.screens
|
||||
PanelWindow {
|
||||
id: root
|
||||
required property var modelData
|
||||
property string searchingText: ""
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
|
||||
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
|
||||
screen: modelData
|
||||
visible: GlobalStates.overviewOpen
|
||||
|
||||
WlrLayershell.namespace: "quickshell:overview"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
// WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
color: "transparent"
|
||||
|
||||
mask: Region {
|
||||
item: GlobalStates.overviewOpen ? columnLayout : null
|
||||
}
|
||||
// HyprlandWindow.visibleMask: Region { // Buggy with scaled monitors
|
||||
// item: GlobalStates.overviewOpen ? columnLayout : null
|
||||
// }
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: !(Config?.options.overview.enable ?? true)
|
||||
right: !(Config?.options.overview.enable ?? true)
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [root]
|
||||
property bool canBeActive: root.monitorIsFocused
|
||||
active: false
|
||||
onCleared: () => {
|
||||
if (!active)
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onOverviewOpenChanged() {
|
||||
if (!GlobalStates.overviewOpen) {
|
||||
searchWidget.disableExpandAnimation();
|
||||
overviewScope.dontAutoCancelSearch = false;
|
||||
} else {
|
||||
if (!overviewScope.dontAutoCancelSearch) {
|
||||
searchWidget.cancelSearch();
|
||||
}
|
||||
delayedGrabTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedGrabTimer
|
||||
interval: Config.options.hacks.arbitraryRaceConditionDelay
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (!grab.canBeActive)
|
||||
return;
|
||||
grab.active = GlobalStates.overviewOpen;
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchWidget.setSearchingText(text);
|
||||
searchWidget.focusFirstItem();
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
visible: GlobalStates.overviewOpen
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
GlobalStates.overviewOpen = false;
|
||||
} else if (event.key === Qt.Key_Left) {
|
||||
if (!root.searchingText)
|
||||
Hyprland.dispatch("workspace r-1");
|
||||
} else if (event.key === Qt.Key_Right) {
|
||||
if (!root.searchingText)
|
||||
Hyprland.dispatch("workspace r+1");
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
height: 1 // Prevent Wayland protocol error
|
||||
width: 1 // Prevent Wayland protocol error
|
||||
}
|
||||
|
||||
SearchWidget {
|
||||
id: searchWidget
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onSearchingTextChanged: text => {
|
||||
root.searchingText = searchingText;
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: overviewLoader
|
||||
active: GlobalStates.overviewOpen && (Config?.options.overview.enable ?? true)
|
||||
sourceComponent: OverviewWidget {
|
||||
panelWindow: root
|
||||
visible: (root.searchingText == "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleClipboard() {
|
||||
if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
|
||||
GlobalStates.overviewOpen = false;
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < overviewVariants.instances.length; i++) {
|
||||
let panelWindow = overviewVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
overviewScope.dontAutoCancelSearch = true;
|
||||
panelWindow.setSearchingText(Config.options.search.prefix.clipboard);
|
||||
GlobalStates.overviewOpen = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleEmojis() {
|
||||
if (GlobalStates.overviewOpen && overviewScope.dontAutoCancelSearch) {
|
||||
GlobalStates.overviewOpen = false;
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < overviewVariants.instances.length; i++) {
|
||||
let panelWindow = overviewVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
overviewScope.dontAutoCancelSearch = true;
|
||||
panelWindow.setSearchingText(Config.options.search.prefix.emojis);
|
||||
GlobalStates.overviewOpen = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "overview"
|
||||
|
||||
function toggle() {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
}
|
||||
function close() {
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
function open() {
|
||||
GlobalStates.overviewOpen = true;
|
||||
}
|
||||
function toggleReleaseInterrupt() {
|
||||
GlobalStates.superReleaseMightTrigger = false;
|
||||
}
|
||||
function clipboardToggle() {
|
||||
overviewScope.toggleClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "overviewToggle"
|
||||
description: "Toggles overview on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewClose"
|
||||
description: "Closes overview"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewToggleRelease"
|
||||
description: "Toggles overview on release"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.superReleaseMightTrigger = true;
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
if (!GlobalStates.superReleaseMightTrigger) {
|
||||
GlobalStates.superReleaseMightTrigger = true;
|
||||
return;
|
||||
}
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewToggleReleaseInterrupt"
|
||||
description: "Interrupts possibility of overview being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.superReleaseMightTrigger = false;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewClipboardToggle"
|
||||
description: "Toggle clipboard query on overview widget"
|
||||
|
||||
onPressed: {
|
||||
overviewScope.toggleClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "overviewEmojiToggle"
|
||||
description: "Toggle emoji query on overview widget"
|
||||
|
||||
onPressed: {
|
||||
overviewScope.toggleEmojis();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,423 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
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
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Item { // Wrapper
|
||||
id: root
|
||||
readonly property string xdgConfigHome: Directories.config
|
||||
property string searchingText: ""
|
||||
property bool showResults: searchingText != ""
|
||||
property real searchBarHeight: searchBar.height + Appearance.sizes.elevationMargin * 2
|
||||
implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: searchWidgetContent.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||
|
||||
property string mathResult: ""
|
||||
|
||||
function disableExpandAnimation() {
|
||||
searchWidthBehavior.enabled = false;
|
||||
}
|
||||
|
||||
function cancelSearch() {
|
||||
searchInput.selectAll();
|
||||
root.searchingText = "";
|
||||
searchWidthBehavior.enabled = true;
|
||||
}
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchInput.text = text;
|
||||
root.searchingText = text;
|
||||
}
|
||||
|
||||
property var searchActions: [
|
||||
{
|
||||
action: "dark",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "light",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "wall",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "konachanwall",
|
||||
execute: () => {
|
||||
Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random_konachan_wall.sh")]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "accentcolor",
|
||||
execute: args => {
|
||||
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]);
|
||||
}
|
||||
},
|
||||
{
|
||||
action: "todo",
|
||||
execute: args => {
|
||||
Todo.addTask(args);
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
function focusFirstItem() {
|
||||
appResults.currentIndex = 0;
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: nonAppResultsTimer
|
||||
interval: Config.options.search.nonAppResultDelay
|
||||
onTriggered: {
|
||||
mathProcess.calculateExpression(root.searchingText);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: mathProcess
|
||||
property list<string> baseCommand: ["qalc", "-t"]
|
||||
function calculateExpression(expression) {
|
||||
mathProcess.running = false;
|
||||
mathProcess.command = baseCommand.concat(expression);
|
||||
mathProcess.running = true;
|
||||
}
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
root.mathResult = data;
|
||||
root.focusFirstItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
// Prevent Esc and Backspace from registering
|
||||
if (event.key === Qt.Key_Escape)
|
||||
return;
|
||||
|
||||
// Handle Backspace: focus and delete character if not focused
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
if (!searchInput.activeFocus) {
|
||||
searchInput.forceActiveFocus();
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
// Delete word before cursor
|
||||
let text = searchInput.text;
|
||||
let pos = searchInput.cursorPosition;
|
||||
if (pos > 0) {
|
||||
// Find the start of the previous word
|
||||
let left = text.slice(0, pos);
|
||||
let match = left.match(/(\s*\S+)\s*$/);
|
||||
let deleteLen = match ? match[0].length : 1;
|
||||
searchInput.text = text.slice(0, pos - deleteLen) + text.slice(pos);
|
||||
searchInput.cursorPosition = pos - deleteLen;
|
||||
}
|
||||
} else {
|
||||
// Delete character before cursor if any
|
||||
if (searchInput.cursorPosition > 0) {
|
||||
searchInput.text = searchInput.text.slice(0, searchInput.cursorPosition - 1) + searchInput.text.slice(searchInput.cursorPosition);
|
||||
searchInput.cursorPosition -= 1;
|
||||
}
|
||||
}
|
||||
// Always move cursor to end after programmatic edit
|
||||
searchInput.cursorPosition = searchInput.text.length;
|
||||
event.accepted = true;
|
||||
}
|
||||
// If already focused, let TextField handle it
|
||||
return;
|
||||
}
|
||||
|
||||
// Only handle visible printable characters (ignore control chars, arrows, etc.)
|
||||
if (event.text && event.text.length === 1 && event.key !== Qt.Key_Enter && event.key !== Qt.Key_Return && event.text.charCodeAt(0) >= 0x20) // ignore control chars like Backspace, Tab, etc.
|
||||
{
|
||||
if (!searchInput.activeFocus) {
|
||||
searchInput.forceActiveFocus();
|
||||
// Insert the character at the cursor position
|
||||
searchInput.text = searchInput.text.slice(0, searchInput.cursorPosition) + event.text + searchInput.text.slice(searchInput.cursorPosition);
|
||||
searchInput.cursorPosition += 1;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: searchWidgetContent
|
||||
}
|
||||
Rectangle { // Background
|
||||
id: searchWidgetContent
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
radius: Appearance.rounding.large
|
||||
color: Appearance.colors.colLayer0
|
||||
border.width: 1
|
||||
border.color: Appearance.colors.colLayer0Border
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
// clip: true
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: searchWidgetContent.width
|
||||
height: searchWidgetContent.width
|
||||
radius: searchWidgetContent.radius
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: searchBar
|
||||
spacing: 5
|
||||
MaterialSymbol {
|
||||
id: searchIcon
|
||||
Layout.leftMargin: 15
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
text: root.searchingText.startsWith(Config.options.search.prefix.clipboard) ? 'content_paste_search' : 'search'
|
||||
}
|
||||
TextField { // Search box
|
||||
id: searchInput
|
||||
|
||||
focus: GlobalStates.overviewOpen
|
||||
Layout.rightMargin: 15
|
||||
padding: 15
|
||||
renderType: Text.NativeRendering
|
||||
font {
|
||||
family: Appearance?.font.family.main ?? "sans-serif"
|
||||
pixelSize: Appearance?.font.pixelSize.small ?? 15
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
}
|
||||
color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant
|
||||
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
selectionColor: Appearance.colors.colSecondaryContainer
|
||||
placeholderText: Translation.tr("Search, calculate or run")
|
||||
placeholderTextColor: Appearance.m3colors.m3outline
|
||||
implicitWidth: root.searchingText == "" ? Appearance.sizes.searchWidthCollapsed : Appearance.sizes.searchWidth
|
||||
|
||||
Behavior on implicitWidth {
|
||||
id: searchWidthBehavior
|
||||
enabled: false
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
onTextChanged: root.searchingText = text
|
||||
|
||||
onAccepted: {
|
||||
if (appResults.count > 0) {
|
||||
// Get the first visible delegate and trigger its click
|
||||
let firstItem = appResults.itemAtIndex(0);
|
||||
if (firstItem && firstItem.clicked) {
|
||||
firstItem.clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: null
|
||||
|
||||
cursorDelegate: Rectangle {
|
||||
width: 1
|
||||
color: searchInput.activeFocus ? Appearance.colors.colPrimary : "transparent"
|
||||
radius: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
// Separator
|
||||
visible: root.showResults
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
StyledListView { // App results
|
||||
id: appResults
|
||||
visible: root.showResults
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Math.min(600, appResults.contentHeight + topMargin + bottomMargin)
|
||||
clip: true
|
||||
topMargin: 10
|
||||
bottomMargin: 10
|
||||
spacing: 2
|
||||
KeyNavigation.up: searchBar
|
||||
highlightMoveDuration: 100
|
||||
add: null
|
||||
remove: null
|
||||
|
||||
onFocusChanged: {
|
||||
if (focus)
|
||||
appResults.currentIndex = 1;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onSearchingTextChanged() {
|
||||
if (appResults.count > 0)
|
||||
appResults.currentIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
id: model
|
||||
onValuesChanged: {
|
||||
root.focusFirstItem();
|
||||
}
|
||||
values: {
|
||||
// Search results are handled here
|
||||
////////////////// Skip? //////////////////
|
||||
if (root.searchingText == "")
|
||||
return [];
|
||||
|
||||
///////////// Special cases ///////////////
|
||||
if (root.searchingText.startsWith(Config.options.search.prefix.clipboard)) {
|
||||
// Clipboard
|
||||
const searchString = root.searchingText.slice(Config.options.search.prefix.clipboard.length);
|
||||
return Cliphist.fuzzyQuery(searchString).map(entry => {
|
||||
return {
|
||||
cliphistRawString: entry,
|
||||
name: entry.replace(/^\s*\S+\s+/, ""),
|
||||
clickActionName: "",
|
||||
type: `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`,
|
||||
execute: () => {
|
||||
Cliphist.copy(entry)
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
name: "Delete",
|
||||
icon: "delete",
|
||||
execute: () => {
|
||||
Cliphist.deleteEntry(entry);
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
if (root.searchingText.startsWith(Config.options.search.prefix.emojis)) {
|
||||
// Clipboard
|
||||
const searchString = root.searchingText.slice(Config.options.search.prefix.emojis.length);
|
||||
return Emojis.fuzzyQuery(searchString).map(entry => {
|
||||
return {
|
||||
cliphistRawString: entry,
|
||||
bigText: entry.match(/^\s*(\S+)/)?.[1] || "",
|
||||
name: entry.replace(/^\s*\S+\s+/, ""),
|
||||
clickActionName: "",
|
||||
type: "Emoji",
|
||||
execute: () => {
|
||||
Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1];
|
||||
}
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
////////////////// Init ///////////////////
|
||||
nonAppResultsTimer.restart();
|
||||
const mathResultObject = {
|
||||
name: root.mathResult,
|
||||
clickActionName: Translation.tr("Copy"),
|
||||
type: Translation.tr("Math result"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'calculate',
|
||||
execute: () => {
|
||||
Quickshell.clipboardText = root.mathResult;
|
||||
}
|
||||
};
|
||||
const commandResultObject = {
|
||||
name: searchingText.replace("file://", ""),
|
||||
clickActionName: Translation.tr("Run"),
|
||||
type: Translation.tr("Run command"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'terminal',
|
||||
execute: () => {
|
||||
const cleanedCommand = root.searchingText.replace("file://", "");
|
||||
Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]);
|
||||
}
|
||||
};
|
||||
const launcherActionObjects = root.searchActions.map(action => {
|
||||
const actionString = `${Config.options.search.prefix.action}${action.action}`;
|
||||
if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) {
|
||||
return {
|
||||
name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString,
|
||||
clickActionName: Translation.tr("Run"),
|
||||
type: Translation.tr("Action"),
|
||||
materialSymbol: 'settings_suggest',
|
||||
execute: () => {
|
||||
action.execute(root.searchingText.split(" ").slice(1).join(" "));
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean);
|
||||
|
||||
let result = [];
|
||||
|
||||
//////////////// Apps //////////////////
|
||||
result = result.concat(AppSearch.fuzzyQuery(root.searchingText).map(entry => {
|
||||
entry.clickActionName = Translation.tr("Launch");
|
||||
entry.type = Translation.tr("App");
|
||||
return entry;
|
||||
}));
|
||||
|
||||
////////// Launcher actions ////////////
|
||||
result = result.concat(launcherActionObjects);
|
||||
|
||||
/////////// Math result & command //////////
|
||||
const startsWithNumber = /^\d/.test(root.searchingText);
|
||||
if (startsWithNumber) {
|
||||
result.push(mathResultObject);
|
||||
result.push(commandResultObject);
|
||||
} else {
|
||||
result.push(commandResultObject);
|
||||
result.push(mathResultObject);
|
||||
}
|
||||
|
||||
///////////////// Web search ////////////////
|
||||
result.push({
|
||||
name: root.searchingText,
|
||||
clickActionName: Translation.tr("Search"),
|
||||
type: Translation.tr("Search the web"),
|
||||
materialSymbol: 'travel_explore',
|
||||
execute: () => {
|
||||
let url = Config.options.search.engineBaseUrl + root.searchingText;
|
||||
for (let site of Config.options.search.excludedSites) {
|
||||
url += ` -site:${site}`;
|
||||
}
|
||||
Qt.openUrlExternally(url);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
delegate: SearchItem {
|
||||
// The selectable item for each search result
|
||||
required property var modelData
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
entry: modelData
|
||||
query: root.searchingText.startsWith(Config.options.search.prefix.clipboard) ? root.searchingText.slice(Config.options.search.prefix.clipboard.length) : root.searchingText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,695 +0,0 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import "./aiChat/"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var inputField: messageInputField
|
||||
property string commandPrefix: "/"
|
||||
|
||||
property var suggestionQuery: ""
|
||||
property var suggestionList: []
|
||||
|
||||
onFocusChanged: (focus) => {
|
||||
if (focus) {
|
||||
root.inputField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
messageInputField.forceActiveFocus()
|
||||
if (event.modifiers === Qt.NoModifier) {
|
||||
if (event.key === Qt.Key_PageUp) {
|
||||
messageListView.contentY = Math.max(0, messageListView.contentY - messageListView.height / 2)
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_PageDown) {
|
||||
messageListView.contentY = Math.min(messageListView.contentHeight - messageListView.height / 2, messageListView.contentY + messageListView.height / 2)
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var allCommands: [
|
||||
{
|
||||
name: "model",
|
||||
description: Translation.tr("Choose model"),
|
||||
execute: (args) => {
|
||||
Ai.setModel(args[0]);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "tool",
|
||||
description: Translation.tr("Set the tool to use for the model."),
|
||||
execute: (args) => {
|
||||
// console.log(args)
|
||||
if (args.length == 0 || args[0] == "get") {
|
||||
Ai.addMessage(Translation.tr("Usage: %1tool TOOL_NAME").arg(root.commandPrefix), Ai.interfaceRole);
|
||||
} else {
|
||||
const tool = args[0];
|
||||
const switched = Ai.setTool(tool);
|
||||
if (switched) {
|
||||
Ai.addMessage(Translation.tr("Tool set to: %1").arg(tool), Ai.interfaceRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "prompt",
|
||||
description: Translation.tr("Set the system prompt for the model."),
|
||||
execute: (args) => {
|
||||
if (args.length === 0 || args[0] === "get") {
|
||||
Ai.printPrompt();
|
||||
return;
|
||||
}
|
||||
Ai.loadPrompt(args.join(" ").trim());
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "key",
|
||||
description: Translation.tr("Set API key"),
|
||||
execute: (args) => {
|
||||
if (args[0] == "get") {
|
||||
Ai.printApiKey()
|
||||
} else {
|
||||
Ai.setApiKey(args[0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "save",
|
||||
description: Translation.tr("Save chat"),
|
||||
execute: (args) => {
|
||||
const joinedArgs = args.join(" ")
|
||||
if (joinedArgs.trim().length == 0) {
|
||||
Ai.addMessage(Translation.tr("Usage: %1save CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole);
|
||||
return;
|
||||
}
|
||||
Ai.saveChat(joinedArgs)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "load",
|
||||
description: Translation.tr("Load chat"),
|
||||
execute: (args) => {
|
||||
const joinedArgs = args.join(" ")
|
||||
if (joinedArgs.trim().length == 0) {
|
||||
Ai.addMessage(Translation.tr("Usage: %1load CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole);
|
||||
return;
|
||||
}
|
||||
Ai.loadChat(joinedArgs)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "clear",
|
||||
description: Translation.tr("Clear chat history"),
|
||||
execute: () => {
|
||||
Ai.clearMessages();
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "temp",
|
||||
description: Translation.tr("Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5."),
|
||||
execute: (args) => {
|
||||
// console.log(args)
|
||||
if (args.length == 0 || args[0] == "get") {
|
||||
Ai.printTemperature()
|
||||
} else {
|
||||
const temp = parseFloat(args[0]);
|
||||
Ai.setTemperature(temp);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "test",
|
||||
description: Translation.tr("Markdown test"),
|
||||
execute: () => {
|
||||
Ai.addMessage(`
|
||||
<think>
|
||||
A longer think block to test revealing animation
|
||||
OwO wem ipsum dowo sit amet, consekituwet awipiscing ewit, sed do eiuwsmod tempow inwididunt ut wabowe et dowo mawa. Ut enim ad minim weniam, quis nostwud exeucitation uwuwamcow bowowis nisi ut awiquip ex ea commowo consequat. Duuis aute iwuwe dowo in wepwependewit in wowuptate velit esse ciwwum dowo eu fugiat nuwa pawiatuw. Excepteuw sint occaecat cupidatat non pwowoident, sunt in cuwpa qui officia desewunt mowit anim id est wabowum. Meouw! >w<
|
||||
Mowe uwu wem ipsum!
|
||||
</think>
|
||||
## ✏️ Markdown test
|
||||
### Formatting
|
||||
|
||||
- *Italic*, \`Monospace\`, **Bold**, [Link](https://example.com)
|
||||
- Arch lincox icon <img src="${Quickshell.shellPath("assets/icons/arch-symbolic.svg")}" height="${Appearance.font.pixelSize.small}"/>
|
||||
|
||||
### Table
|
||||
|
||||
Quickshell vs AGS/Astal
|
||||
|
||||
| | Quickshell | AGS/Astal |
|
||||
|--------------------------|------------------|-------------------|
|
||||
| UI Toolkit | Qt | Gtk3/Gtk4 |
|
||||
| Language | QML | Js/Ts/Lua |
|
||||
| Reactivity | Implied | Needs declaration |
|
||||
| Widget placement | Mildly difficult | More intuitive |
|
||||
| Bluetooth & Wifi support | ❌ | ✅ |
|
||||
| No-delay keybinds | ✅ | ❌ |
|
||||
| Development | New APIs | New syntax |
|
||||
|
||||
### Code block
|
||||
|
||||
Just a hello world...
|
||||
|
||||
\`\`\`cpp
|
||||
#include <bits/stdc++.h>
|
||||
// This is intentionally very long to test scrolling
|
||||
const std::string GREETING = \"UwU\";
|
||||
int main(int argc, char* argv[]) {
|
||||
std::cout << GREETING;
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### LaTeX
|
||||
|
||||
|
||||
Inline w/ dollar signs: $\\frac{1}{2} = \\frac{2}{4}$
|
||||
|
||||
Inline w/ double dollar signs: $$\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}$$
|
||||
|
||||
Inline w/ backslash and square brackets \\[\\int_0^\\infty \\frac{1}{x^2} dx = \\infty\\]
|
||||
|
||||
Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
||||
`,
|
||||
Ai.interfaceRole);
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
function handleInput(inputText) {
|
||||
if (inputText.startsWith(root.commandPrefix)) {
|
||||
// Handle special commands
|
||||
const command = inputText.split(" ")[0].substring(1);
|
||||
const args = inputText.split(" ").slice(1);
|
||||
const commandObj = root.allCommands.find(cmd => cmd.name === `${command}`);
|
||||
if (commandObj) {
|
||||
commandObj.execute(args);
|
||||
} else {
|
||||
Ai.addMessage(Translation.tr("Unknown command: ") + command, Ai.interfaceRole);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Ai.sendUserMessage(inputText);
|
||||
}
|
||||
}
|
||||
|
||||
component StatusItem: MouseArea {
|
||||
id: statusItem
|
||||
property string icon
|
||||
property string statusText
|
||||
property string description
|
||||
hoverEnabled: true
|
||||
implicitHeight: statusItemRowLayout.implicitHeight
|
||||
implicitWidth: statusItemRowLayout.implicitWidth
|
||||
|
||||
RowLayout {
|
||||
id: statusItemRowLayout
|
||||
spacing: 0
|
||||
MaterialSymbol {
|
||||
text: statusItem.icon
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
color: Appearance.colors.colSubtext
|
||||
}
|
||||
StyledText {
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
text: statusItem.statusText
|
||||
color: Appearance.colors.colSubtext
|
||||
}
|
||||
}
|
||||
|
||||
StyledToolTip {
|
||||
content: statusItem.description
|
||||
extraVisibleCondition: false
|
||||
alternativeVisibleCondition: statusItem.containsMouse
|
||||
}
|
||||
}
|
||||
|
||||
component StatusSeparator: Rectangle {
|
||||
implicitWidth: 4
|
||||
implicitHeight: 4
|
||||
radius: implicitWidth / 2
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout { // Status
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 10
|
||||
|
||||
StatusItem {
|
||||
icon: Ai.currentModelHasApiKey ? "key" : "key_off"
|
||||
statusText: ""
|
||||
description: Ai.currentModelHasApiKey ? Translation.tr("API key is set\nChange with /key YOUR_API_KEY") : Translation.tr("No API key\nSet it with /key YOUR_API_KEY")
|
||||
}
|
||||
StatusSeparator {}
|
||||
StatusItem {
|
||||
icon: "device_thermostat"
|
||||
statusText: Ai.temperature.toFixed(1)
|
||||
description: Translation.tr("Temperature\nChange with /temp VALUE")
|
||||
}
|
||||
StatusSeparator {
|
||||
visible: Ai.tokenCount.total > 0
|
||||
}
|
||||
StatusItem {
|
||||
visible: Ai.tokenCount.total > 0
|
||||
icon: "token"
|
||||
statusText: Ai.tokenCount.total
|
||||
description: Translation.tr("Total token count\nInput: %1\nOutput: %2")
|
||||
.arg(Ai.tokenCount.input)
|
||||
.arg(Ai.tokenCount.output)
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Messages
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
StyledListView { // Message list
|
||||
id: messageListView
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
popin: false
|
||||
|
||||
touchpadScrollFactor: Config.options.interactions.scrolling.touchpadScrollFactor * 1.4
|
||||
mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4
|
||||
|
||||
property int lastResponseLength: 0
|
||||
|
||||
clip: true
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: swipeView.width
|
||||
height: swipeView.height
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
|
||||
add: null // Prevent function calls from being janky
|
||||
|
||||
model: ScriptModel {
|
||||
values: Ai.messageIDs.filter(id => {
|
||||
const message = Ai.messageByID[id];
|
||||
return message?.visibleToUser ?? true;
|
||||
})
|
||||
}
|
||||
delegate: AiMessage {
|
||||
required property var modelData
|
||||
required property int index
|
||||
messageIndex: index
|
||||
messageData: {
|
||||
Ai.messageByID[modelData]
|
||||
}
|
||||
messageInputField: root.inputField
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Placeholder when list is empty
|
||||
opacity: Ai.messageIDs.length === 0 ? 1 : 0
|
||||
visible: opacity > 0
|
||||
anchors.fill: parent
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
iconSize: 60
|
||||
color: Appearance.m3colors.m3outline
|
||||
text: "neurology"
|
||||
}
|
||||
StyledText {
|
||||
id: widgetNameText
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
font.family: Appearance.font.family.title
|
||||
color: Appearance.m3colors.m3outline
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Translation.tr("Large language models")
|
||||
}
|
||||
StyledText {
|
||||
id: widgetDescriptionText
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.m3colors.m3outline
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
wrapMode: Text.Wrap
|
||||
text: Translation.tr("Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DescriptionBox {
|
||||
text: root.suggestionList[suggestions.selectedIndex]?.description ?? ""
|
||||
showArrows: root.suggestionList.length > 1
|
||||
}
|
||||
|
||||
FlowButtonGroup { // Suggestions
|
||||
id: suggestions
|
||||
visible: root.suggestionList.length > 0 && messageInputField.text.length > 0
|
||||
property int selectedIndex: 0
|
||||
Layout.fillWidth: true
|
||||
spacing: 5
|
||||
|
||||
Repeater {
|
||||
id: suggestionRepeater
|
||||
model: {
|
||||
suggestions.selectedIndex = 0
|
||||
return root.suggestionList.slice(0, 10)
|
||||
}
|
||||
delegate: ApiCommandButton {
|
||||
id: commandButton
|
||||
colBackground: suggestions.selectedIndex === index ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colSecondaryContainer
|
||||
bounce: false
|
||||
contentItem: StyledText {
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: modelData.displayName ?? modelData.name
|
||||
}
|
||||
|
||||
onHoveredChanged: {
|
||||
if (commandButton.hovered) {
|
||||
suggestions.selectedIndex = index;
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
suggestions.acceptSuggestion(modelData.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function acceptSuggestion(word) {
|
||||
const words = messageInputField.text.trim().split(/\s+/);
|
||||
if (words.length > 0) {
|
||||
words[words.length - 1] = word;
|
||||
} else {
|
||||
words.push(word);
|
||||
}
|
||||
const updatedText = words.join(" ") + " ";
|
||||
messageInputField.text = updatedText;
|
||||
messageInputField.cursorPosition = messageInputField.text.length;
|
||||
messageInputField.forceActiveFocus();
|
||||
}
|
||||
|
||||
function acceptSelectedWord() {
|
||||
if (suggestions.selectedIndex >= 0 && suggestions.selectedIndex < suggestionRepeater.count) {
|
||||
const word = root.suggestionList[suggestions.selectedIndex].name;
|
||||
suggestions.acceptSuggestion(word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Input area
|
||||
id: inputWrapper
|
||||
property real columnSpacing: 5
|
||||
Layout.fillWidth: true
|
||||
radius: Appearance.rounding.small
|
||||
color: Appearance.colors.colLayer1
|
||||
implicitWidth: messageInputField.implicitWidth
|
||||
implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin
|
||||
+ commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + columnSpacing, 45)
|
||||
clip: true
|
||||
border.color: Appearance.colors.colOutlineVariant
|
||||
border.width: 1
|
||||
|
||||
Behavior on implicitHeight {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
RowLayout { // Input field and send button
|
||||
id: inputFieldRowLayout
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 5
|
||||
spacing: 0
|
||||
|
||||
StyledTextArea { // The actual TextArea
|
||||
id: messageInputField
|
||||
wrapMode: TextArea.Wrap
|
||||
Layout.fillWidth: true
|
||||
padding: 10
|
||||
color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant
|
||||
placeholderText: Translation.tr('Message the model... "%1" for commands').arg(root.commandPrefix)
|
||||
|
||||
background: null
|
||||
|
||||
onTextChanged: { // Handle suggestions
|
||||
if (messageInputField.text.length === 0) {
|
||||
root.suggestionQuery = ""
|
||||
root.suggestionList = []
|
||||
return
|
||||
} else if (messageInputField.text.startsWith(`${root.commandPrefix}model`)) {
|
||||
root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
|
||||
const modelResults = Fuzzy.go(root.suggestionQuery, Ai.modelList.map(model => {
|
||||
return {
|
||||
name: Fuzzy.prepare(model),
|
||||
obj: model,
|
||||
}
|
||||
}), {
|
||||
all: true,
|
||||
key: "name"
|
||||
})
|
||||
root.suggestionList = modelResults.map(model => {
|
||||
return {
|
||||
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "model ") : ""}${model.target}`,
|
||||
displayName: `${Ai.models[model.target].name}`,
|
||||
description: `${Ai.models[model.target].description}`,
|
||||
}
|
||||
})
|
||||
} else if (messageInputField.text.startsWith(`${root.commandPrefix}prompt`)) {
|
||||
root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
|
||||
const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.promptFiles.map(file => {
|
||||
return {
|
||||
name: Fuzzy.prepare(file),
|
||||
obj: file,
|
||||
}
|
||||
}), {
|
||||
all: true,
|
||||
key: "name"
|
||||
})
|
||||
root.suggestionList = promptFileResults.map(file => {
|
||||
return {
|
||||
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "prompt ") : ""}${file.target}`,
|
||||
displayName: `${FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target))}`,
|
||||
description: Translation.tr("Load prompt from %1").arg(file.target),
|
||||
}
|
||||
})
|
||||
} else if (messageInputField.text.startsWith(`${root.commandPrefix}save`)) {
|
||||
root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
|
||||
const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.savedChats.map(file => {
|
||||
return {
|
||||
name: Fuzzy.prepare(file),
|
||||
obj: file,
|
||||
}
|
||||
}), {
|
||||
all: true,
|
||||
key: "name"
|
||||
})
|
||||
root.suggestionList = promptFileResults.map(file => {
|
||||
const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim()
|
||||
return {
|
||||
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "save ") : ""}${chatName}`,
|
||||
displayName: `${chatName}`,
|
||||
description: Translation.tr("Save chat to %1").arg(chatName),
|
||||
}
|
||||
})
|
||||
} else if (messageInputField.text.startsWith(`${root.commandPrefix}load`)) {
|
||||
root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
|
||||
const promptFileResults = Fuzzy.go(root.suggestionQuery, Ai.savedChats.map(file => {
|
||||
return {
|
||||
name: Fuzzy.prepare(file),
|
||||
obj: file,
|
||||
}
|
||||
}), {
|
||||
all: true,
|
||||
key: "name"
|
||||
})
|
||||
root.suggestionList = promptFileResults.map(file => {
|
||||
const chatName = FileUtils.trimFileExt(FileUtils.fileNameForPath(file.target)).trim()
|
||||
return {
|
||||
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "load ") : ""}${chatName}`,
|
||||
displayName: `${chatName}`,
|
||||
description: Translation.tr(`Load chat from %1`).arg(file.target),
|
||||
}
|
||||
})
|
||||
} else if (messageInputField.text.startsWith(`${root.commandPrefix}tool`)) {
|
||||
root.suggestionQuery = messageInputField.text.split(" ")[1] ?? ""
|
||||
const toolResults = Fuzzy.go(root.suggestionQuery, Ai.availableTools.map(tool => {
|
||||
return {
|
||||
name: Fuzzy.prepare(tool),
|
||||
obj: tool,
|
||||
}
|
||||
}), {
|
||||
all: true,
|
||||
key: "name"
|
||||
})
|
||||
root.suggestionList = toolResults.map(tool => {
|
||||
const toolName = tool.target
|
||||
return {
|
||||
name: `${messageInputField.text.trim().split(" ").length == 1 ? (root.commandPrefix + "tool ") : ""}${tool.target}`,
|
||||
displayName: toolName,
|
||||
description: Ai.toolDescriptions[toolName],
|
||||
}
|
||||
})
|
||||
} else if(messageInputField.text.startsWith(root.commandPrefix)) {
|
||||
root.suggestionQuery = messageInputField.text
|
||||
root.suggestionList = root.allCommands.filter(cmd => cmd.name.startsWith(messageInputField.text.substring(1))).map(cmd => {
|
||||
return {
|
||||
name: `${root.commandPrefix}${cmd.name}`,
|
||||
description: `${cmd.description}`,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function accept() {
|
||||
root.handleInput(text)
|
||||
text = ""
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_Tab) {
|
||||
suggestions.acceptSelectedWord();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Up && suggestions.visible) {
|
||||
suggestions.selectedIndex = Math.max(0, suggestions.selectedIndex - 1);
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Down && suggestions.visible) {
|
||||
suggestions.selectedIndex = Math.min(root.suggestionList.length - 1, suggestions.selectedIndex + 1);
|
||||
event.accepted = true;
|
||||
} else if ((event.key === Qt.Key_Enter || event.key === Qt.Key_Return)) {
|
||||
if (event.modifiers & Qt.ShiftModifier) {
|
||||
// Insert newline
|
||||
messageInputField.insert(messageInputField.cursorPosition, "\n")
|
||||
event.accepted = true
|
||||
} else { // Accept text
|
||||
const inputText = messageInputField.text
|
||||
messageInputField.clear()
|
||||
root.handleInput(inputText)
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RippleButton { // Send button
|
||||
id: sendButton
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.rightMargin: 5
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: messageInputField.text.length > 0
|
||||
toggled: enabled
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: sendButton.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: {
|
||||
const inputText = messageInputField.text
|
||||
root.handleInput(inputText)
|
||||
messageInputField.clear()
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
// fill: sendButton.enabled ? 1 : 0
|
||||
color: sendButton.enabled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2Disabled
|
||||
text: "send"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout { // Controls
|
||||
id: commandButtonsRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 5
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 5
|
||||
spacing: 4
|
||||
|
||||
property var commandsShown: [
|
||||
{
|
||||
name: "",
|
||||
sendDirectly: false,
|
||||
dontAddSpace: true,
|
||||
},
|
||||
{
|
||||
name: "clear",
|
||||
sendDirectly: true,
|
||||
},
|
||||
]
|
||||
|
||||
ApiInputBoxIndicator { // Model indicator
|
||||
icon: "api"
|
||||
text: Ai.getModel().name
|
||||
tooltipText: Translation.tr("Current model: %1\nSet it with %2model MODEL")
|
||||
.arg(Ai.getModel().name)
|
||||
.arg(root.commandPrefix)
|
||||
}
|
||||
|
||||
ApiInputBoxIndicator { // Tool indicator
|
||||
icon: "service_toolbox"
|
||||
text: Ai.currentTool.charAt(0).toUpperCase() + Ai.currentTool.slice(1)
|
||||
tooltipText: Translation.tr("Current tool: %1\nSet it with %2tool TOOL")
|
||||
.arg(Ai.currentTool)
|
||||
.arg(root.commandPrefix)
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
ButtonGroup { // Command buttons
|
||||
padding: 0
|
||||
|
||||
Repeater { // Command buttons
|
||||
model: commandButtonsRow.commandsShown
|
||||
delegate: ApiCommandButton {
|
||||
property string commandRepresentation: `${root.commandPrefix}${modelData.name}`
|
||||
buttonText: commandRepresentation
|
||||
onClicked: {
|
||||
if(modelData.sendDirectly) {
|
||||
root.handleInput(commandRepresentation)
|
||||
} else {
|
||||
messageInputField.text = commandRepresentation + (modelData.dontAddSpace ? "" : " ")
|
||||
messageInputField.cursorPosition = messageInputField.text.length
|
||||
messageInputField.forceActiveFocus()
|
||||
}
|
||||
if (modelData.name === "clear") {
|
||||
messageInputField.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,180 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property var taskList;
|
||||
property string emptyPlaceholderIcon
|
||||
property string emptyPlaceholderText
|
||||
property int todoListItemSpacing: 5
|
||||
property int todoListItemPadding: 8
|
||||
property int listBottomPadding: 80
|
||||
|
||||
StyledFlickable {
|
||||
id: flickable
|
||||
anchors.fill: parent
|
||||
contentHeight: columnLayout.height
|
||||
|
||||
clip: true
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: flickable.width
|
||||
height: flickable.height
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: taskList
|
||||
}
|
||||
delegate: Item {
|
||||
id: todoItem
|
||||
property bool pendingDoneToggle: false
|
||||
property bool pendingDelete: false
|
||||
property bool enableHeightAnimation: false
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: todoItemRectangle.implicitHeight + todoListItemSpacing
|
||||
height: implicitHeight
|
||||
clip: true
|
||||
|
||||
Behavior on implicitHeight {
|
||||
enabled: enableHeightAnimation
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
}
|
||||
|
||||
function startAction() {
|
||||
enableHeightAnimation = true
|
||||
todoItem.implicitHeight = 0
|
||||
actionTimer.start()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: actionTimer
|
||||
interval: Appearance.animation.elementMoveFast.duration
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (todoItem.pendingDelete) {
|
||||
Todo.deleteItem(modelData.originalIndex)
|
||||
} else if (todoItem.pendingDoneToggle) {
|
||||
if (!modelData.done) Todo.markDone(modelData.originalIndex)
|
||||
else Todo.markUnfinished(modelData.originalIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: todoItemRectangle
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
implicitHeight: todoContentRowLayout.implicitHeight
|
||||
color: Appearance.colors.colLayer2
|
||||
radius: Appearance.rounding.small
|
||||
ColumnLayout {
|
||||
id: todoContentRowLayout
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true // Needed for wrapping
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: 10
|
||||
Layout.topMargin: todoListItemPadding
|
||||
id: todoContentText
|
||||
text: modelData.content
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
RowLayout {
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: 10
|
||||
Layout.bottomMargin: todoListItemPadding
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
TodoItemActionButton {
|
||||
Layout.fillWidth: false
|
||||
onClicked: {
|
||||
todoItem.pendingDoneToggle = true
|
||||
todoItem.startAction()
|
||||
}
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: modelData.done ? "remove_done" : "check"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: Appearance.colors.colOnLayer1
|
||||
}
|
||||
}
|
||||
TodoItemActionButton {
|
||||
Layout.fillWidth: false
|
||||
onClicked: {
|
||||
todoItem.pendingDelete = true
|
||||
todoItem.startAction()
|
||||
}
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: "delete_forever"
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: Appearance.colors.colOnLayer1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Bottom padding
|
||||
Item {
|
||||
implicitHeight: listBottomPadding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Placeholder when list is empty
|
||||
visible: opacity > 0
|
||||
opacity: taskList.length === 0 ? 1 : 0
|
||||
anchors.fill: parent
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
MaterialSymbol {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
iconSize: 55
|
||||
color: Appearance.m3colors.m3outline
|
||||
text: emptyPlaceholderIcon
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.m3colors.m3outline
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: emptyPlaceholderText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,63 +0,0 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property PwNode node
|
||||
PwObjectTracker {
|
||||
objects: [node]
|
||||
}
|
||||
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
anchors.fill: parent
|
||||
spacing: 6
|
||||
|
||||
Image {
|
||||
property real size: slider.height * 0.9
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
visible: source != ""
|
||||
sourceSize.width: size
|
||||
sourceSize.height: size
|
||||
source: {
|
||||
let icon;
|
||||
icon = AppSearch.guessIcon(root.node.properties["application.icon-name"]);
|
||||
if (AppSearch.iconExists(icon))
|
||||
return Quickshell.iconPath(icon, "image-missing");
|
||||
icon = AppSearch.guessIcon(root.node.properties["node.name"]);
|
||||
return Quickshell.iconPath(icon, "image-missing");
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: -4
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colSubtext
|
||||
elide: Text.ElideRight
|
||||
text: {
|
||||
// application.name -> description -> name
|
||||
const app = root.node.properties["application.name"] ?? (root.node.description != "" ? root.node.description : root.node.name);
|
||||
const media = root.node.properties["media.name"];
|
||||
return media != undefined ? `${app} • ${media}` : app;
|
||||
}
|
||||
}
|
||||
|
||||
StyledSlider {
|
||||
id: slider
|
||||
value: root.node.audio.volume
|
||||
onValueChanged: root.node.audio.volume = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1754725699,
|
||||
"narHash": "sha256-iAcj9T/Y+3DBy2J0N+yF9XQQQ8IEb5swLFzs23CdP88=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
{
|
||||
description = "A flake that provides a runnable switchwall script.";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system: let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
||||
pythonWithPackages = pkgs.python3.withPackages (ps:
|
||||
with ps; [
|
||||
dbus-python
|
||||
materialyoucolor
|
||||
pillow
|
||||
]);
|
||||
|
||||
switchwallScript = pkgs.stdenv.mkDerivation {
|
||||
pname = "switchwall";
|
||||
version = "1.0";
|
||||
src = ./.;
|
||||
|
||||
buildInputs = [
|
||||
pkgs.makeWrapper
|
||||
pkgs.jq
|
||||
pkgs.imagemagick
|
||||
pkgs.ffmpeg
|
||||
pkgs.mpvpaper
|
||||
pkgs.libnotify
|
||||
pkgs.hyprpicker
|
||||
pkgs.matugen
|
||||
pkgs.bash
|
||||
pkgs.pipewire
|
||||
pkgs.dbus
|
||||
pkgs.kdePackages.plasma-desktop
|
||||
# pkgs.kdePackages.plasma-framework
|
||||
pkgs.kdePackages.kdialog
|
||||
pythonWithPackages
|
||||
];
|
||||
|
||||
nativeBuildInputs = [pkgs.qt6.wrapQtAppsHook];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
# CHANGE HERE: Copy to 'switchwall' instead of 'switchwall.sh'
|
||||
cp switchwall.sh $out/bin/switchwall
|
||||
chmod +x $out/bin/switchwall
|
||||
|
||||
# UPDATE HERE: Wrap the new filename
|
||||
wrapProgram $out/bin/switchwall \
|
||||
--prefix PATH : "${pkgs.lib.makeBinPath [
|
||||
pkgs.jq
|
||||
pkgs.imagemagick
|
||||
pkgs.ffmpeg
|
||||
pkgs.mpvpaper
|
||||
pkgs.kdePackages.kdialog
|
||||
pkgs.libnotify
|
||||
pkgs.hyprpicker
|
||||
pkgs.matugen
|
||||
pythonWithPackages
|
||||
pkgs.bash
|
||||
pkgs.pipewire
|
||||
pkgs.kdePackages.plasma-desktop
|
||||
]}"
|
||||
'';
|
||||
};
|
||||
in {
|
||||
packages.default = switchwallScript;
|
||||
apps.default = flake-utils.lib.mkApp {
|
||||
drv = switchwallScript;
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Execute the switchwall.sh script within the Nix environment.
|
||||
nix develop --command ./switchwall.sh "$@"
|
||||
@@ -1 +0,0 @@
|
||||
]4;0;#$term0 #\]1;0;#$term0 #\]4;1;#$term1 #\]4;2;#$term2 #\]4;3;#$term3 #\]4;4;#$term4 #\]4;5;#$term5 #\]4;6;#$term6 #\]4;7;#$term7 #\]4;8;#$term8 #\]4;9;#$term9 #\]4;10;#$term10 #\]4;11;#$term11 #\]4;12;#$term12 #\]4;13;#$term13 #\]4;14;#$term14 #\]4;15;#$term15 #\]10;#$term7 #\]11;[100]#$term0 #\]12;#$term7 #\]13;#$term7 #\]17;#$term7 #\]19;#$term0 #\]4;232;#$term7 #\]4;256;#$term7 #\]708;[100]#$term0 #\]11;#$term0 #\
|
||||
@@ -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,117 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import qs.modules.common
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
/**
|
||||
* Simple hyprsunset service with automatic mode.
|
||||
* In theory we don't need this because hyprsunset has a config file, but it somehow doesn't work.
|
||||
* It should also be possible to control it via hyprctl, but it doesn't work consistently either so we're just killing and launching.
|
||||
*/
|
||||
Singleton {
|
||||
id: root
|
||||
property var manualActive
|
||||
property string from: Config.options?.light?.night?.from ?? "19:00" // Default to 7 PM
|
||||
property string to: Config.options?.light?.night?.to ?? "06:30" // Default to 6:30 AM
|
||||
property bool automatic: Config.options?.light?.night?.automatic && (Config?.ready ?? true)
|
||||
property int colorTemperature: Config.options?.light?.night?.colorTemperature ?? 5000 // Default color temperature
|
||||
property bool shouldBeOn
|
||||
property bool firstEvaluation: true
|
||||
property bool active: false
|
||||
|
||||
property int fromHour: Number(from.split(":")[0])
|
||||
property int fromMinute: Number(from.split(":")[1])
|
||||
property int toHour: Number(to.split(":")[0])
|
||||
property int toMinute: Number(to.split(":")[1])
|
||||
|
||||
property int clockHour: DateTime.clock.hours
|
||||
property int clockMinute: DateTime.clock.minutes
|
||||
|
||||
|
||||
function isNoLater(hour1, minute1, hour2, minute2) {
|
||||
if (hour1 < hour2)
|
||||
return true;
|
||||
if (hour1 === hour2 && minute1 < minute2)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
onClockMinuteChanged: reEvaluate()
|
||||
onAutomaticChanged: {
|
||||
root.manualActive = undefined;
|
||||
root.firstEvaluation = true;
|
||||
reEvaluate();
|
||||
}
|
||||
function reEvaluate() {
|
||||
const toHourIsNextDay = !isNoLater(fromHour, fromMinute, toHour, toMinute);
|
||||
const toHourWrapped = toHourIsNextDay ? toHour + 24 : toHour;
|
||||
const toMinuteWrapped = toMinute;
|
||||
root.shouldBeOn = isNoLater(fromHour, fromMinute, clockHour, clockMinute) && isNoLater(clockHour, clockMinute, toHourWrapped, toMinuteWrapped);
|
||||
if (firstEvaluation) {
|
||||
firstEvaluation = false;
|
||||
root.ensureState();
|
||||
}
|
||||
}
|
||||
|
||||
onShouldBeOnChanged: ensureState()
|
||||
function ensureState() {
|
||||
// console.log("[Hyprsunset] Ensuring state:", root.shouldBeOn, "Automatic mode:", root.automatic);
|
||||
if (!root.automatic || root.manualActive !== undefined)
|
||||
return;
|
||||
if (root.shouldBeOn) {
|
||||
root.enable();
|
||||
} else {
|
||||
root.disable();
|
||||
}
|
||||
}
|
||||
|
||||
function load() { } // Dummy to force init
|
||||
|
||||
function enable() {
|
||||
root.active = true;
|
||||
// console.log("[Hyprsunset] Enabling");
|
||||
Quickshell.execDetached(["bash", "-c", `pidof hyprsunset || hyprsunset --temperature ${root.colorTemperature}`]);
|
||||
}
|
||||
|
||||
function disable() {
|
||||
root.active = false;
|
||||
// console.log("[Hyprsunset] Disabling");
|
||||
Quickshell.execDetached(["bash", "-c", `pkill hyprsunset`]);
|
||||
}
|
||||
|
||||
function fetchState() {
|
||||
fetchProc.running = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: fetchProc
|
||||
running: true
|
||||
command: ["bash", "-c", "hyprctl hyprsunset temperature"]
|
||||
stdout: StdioCollector {
|
||||
id: stateCollector
|
||||
onStreamFinished: {
|
||||
const output = stateCollector.text.trim();
|
||||
if (output.length == 0 || output.startsWith("Couldn't"))
|
||||
root.active = false;
|
||||
else
|
||||
root.active = (output != "6500");
|
||||
// console.log("[Hyprsunset] Fetched state:", output, "->", root.active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
if (root.manualActive === undefined)
|
||||
root.manualActive = root.active;
|
||||
|
||||
root.manualActive = !root.manualActive;
|
||||
if (root.manualActive) {
|
||||
root.enable();
|
||||
} else {
|
||||
root.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,346 +0,0 @@
|
||||
{
|
||||
"Output": "Вывод",
|
||||
"Markdown test": "Тест Markdown",
|
||||
"Intelligence": "ИИ",
|
||||
"Load chat": "Загрузить чат",
|
||||
"Workspaces shown": "Кол-во рабочих столов",
|
||||
"Style": "Стиль",
|
||||
"Reject": "Отклонить",
|
||||
"Volume": "Громкость",
|
||||
"Shutdown": "Завершение работы",
|
||||
"Fidelity": "Верность",
|
||||
"Switched to search mode. Continue with the user's request.": "Переключено в режим поиска. Продолжай с запросом пользователя.",
|
||||
"No notifications": "Нет уведомлений",
|
||||
"EasyEffects | Right-click to configure": "EasyEffects | ПКМ, чтобы настроить",
|
||||
"Suspend at": "Переход в режим сна на",
|
||||
"Brightness": "Яркость",
|
||||
"Volume mixer": "Микшер громкости",
|
||||
"Discussions": "Обсуждения",
|
||||
"Earbang protection": "Защита от громких звуков",
|
||||
"Message the model... \"%1\" for commands": "Сообщение для ИИ... \"%1\" для команд",
|
||||
"Decorations & Effects": "Декорации и эффекты",
|
||||
"Load chat from %1": "Загрузить чат из %1",
|
||||
"OK": "ОК",
|
||||
"<i>No further instruction provided</i>": "<i>Больше инструкций не предоставлено</i>",
|
||||
"Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "Установить температуру (случайность) модели. Выберите значение от 0 до 2 для Gemini, от 0 до 1 для других моделей. По умолчанию: 0.5",
|
||||
"Open file link": "Открыть ссылку на файл",
|
||||
"API key is set\nChange with /key YOUR_API_KEY": "API ключ установлен\nПоменять с помощью /key ВАШ_КЛЮЧ_API",
|
||||
"Advanced": "Продвинутые",
|
||||
"Title bar": "Заголовок",
|
||||
"Keybinds": "Шорткаты",
|
||||
"Alternatively use /dark, /light, /img in the launcher": "Также можно использовать /dark, /light, /img в лаунчере",
|
||||
"Dark/Light toggle": "Темный/светлый",
|
||||
"Shell & utilities": "Оболочка",
|
||||
"Clipboard": "Буфер обмена",
|
||||
"Yooooo hi there": "Йооооу привет",
|
||||
"Show app icons": "Показывать иконки",
|
||||
"Save": "Сохранить",
|
||||
"Style & wallpaper": "Стиль и обои",
|
||||
"Battery": "Батарея",
|
||||
"Expressive": "Выразительность",
|
||||
"Reboot": "Перезагрузка",
|
||||
"AI": "ИИ",
|
||||
"Sleep": "Спящий режим",
|
||||
"Allow NSFW": "Разрешить NSFW",
|
||||
"Please charge!\nAutomatic suspend triggers at %1": "Подключите ПК к источнику питания!\nПереход в спящий режим на %1",
|
||||
"Select input device": "Выберите микрофон",
|
||||
"Hover to reveal": "Наведите, чтобы раскрыть",
|
||||
"Unknown Artist": "Неизвестный исполнитель",
|
||||
"Connection failed. Please inspect manually with the <tt>warp-cli</tt> command": "Ошибка подключения. Проверьте вручную командой <tt>warp-cli</tt>",
|
||||
"Lock": "Блокировка",
|
||||
"Monochrome": "Монохром",
|
||||
"**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": "**Цена**: бесплатно. Данные используются для обучения.\n\n**Инстуркции**: Войдите в аккаунт Google, разрешите AI Studio создать проект Google Cloud или что оно попросит, вернитесь и нажмите Get API key",
|
||||
"Online models disallowed\n\nControlled by `policies.ai` config option": "Онлайн модели запрещены\n\nУправляется опцией `policies.ai` в конфиге",
|
||||
"Emojis": "Эмодзи",
|
||||
"Wallpaper parallax": "Параллакс обоев",
|
||||
"Interface": "Интерфейс",
|
||||
"System prompt": "Системный промпт",
|
||||
"Edit config": "Ред. конфиг",
|
||||
"Page %1": "Страница %1",
|
||||
"Task description": "Описание задания",
|
||||
"Fruit Salad": "Фруктовый салад",
|
||||
"%1 Safe Storage": "Безопасное хранилище %1",
|
||||
"Distro": "Дистрибутив",
|
||||
"Add": "Добавить",
|
||||
"Closet": "Закрытый",
|
||||
"Task Manager": "Системный монитор",
|
||||
"Configuration": "Настройка",
|
||||
"There might be a download in progress": "Возможно происходит скачивание",
|
||||
"Visibility": "Видимость",
|
||||
"Desktop": "Рабочий стол",
|
||||
"Run": "Запустить",
|
||||
"Sunrise": "Рассвет",
|
||||
"Set API key": "Задать API ключ",
|
||||
"Shell windows": "Окна оболочки",
|
||||
"Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)",
|
||||
"Action": "Действие",
|
||||
"Elements": "Элементы",
|
||||
"Resources": "Ресурсы",
|
||||
"**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": "**Цена**: бесплатно. Политика использования данных зависит от настроек OpenRouter.\n\n**Инструкции**: Войдите в аккаунт OpenRouter, зайдите в Keys в меню сверху справа, нажмите Create API Key",
|
||||
"Game mode": "Игровой режим",
|
||||
"Code saved to file": "Код сохранен в файл",
|
||||
"Online | Google's model\nGives up-to-date information with search.": "Онлайн | Модель Google\nДает точную информацию благодаря поиску",
|
||||
"Issues": "Проблемы",
|
||||
"Depends on sidebars": "Зависит от панелей",
|
||||
"Translation goes here...": "Здесь будет перевод...",
|
||||
"Bar style": "Стиль панели",
|
||||
"Unknown command:": "Неизвестная команда:",
|
||||
"Invalid API provider. Supported: \n-": "Неизвестный API провайдер. Поддерживаемые: \n-",
|
||||
"Clear chat history": "Очистить историю",
|
||||
"Run command": "Выполнить команду",
|
||||
"Local only": "Локальные",
|
||||
"Tonal Spot": "Тональное пятно",
|
||||
"No corresponding search model found for %1": "Не найдено поисковой модели для %1",
|
||||
"Content": "Контент",
|
||||
"Wind": "Ветер",
|
||||
"Total token count\nInput: %1\nOutput: %2": "Количество токенов\nВвод: %1\nВывод: %2",
|
||||
"Current model: %1\nSet it with %2model MODEL": "Текущая модель: %1\nСменить с помощью %2model МОДЕЛЬ",
|
||||
"Dark": "Темный",
|
||||
"Hug": "Захват",
|
||||
"Hibernate": "Гиберрнация",
|
||||
"Registration failed. Please inspect manually with the <tt>warp-cli</tt> command": "Ошибка регистрации. Проверьте вручную командой <tt>warp-cli</tt>",
|
||||
"Calendar": "Календарь",
|
||||
"Save chat to %1": "Сохранить чат в %1",
|
||||
"Finished tasks will go here": "Здесь будут выполненные задачи",
|
||||
"Set the current API provider": "Задать текущего провайдера API",
|
||||
"Weather Service": "Сервис погоды",
|
||||
"Fake screen rounding": "Фейковое округление экрана",
|
||||
"View Markdown source": "Посмотреть исходной Markdown",
|
||||
"Change any time later with /dark, /light, /img in the launcher": "Изменяется в любое время с помощью /dark, /light, /img в лаунчере",
|
||||
"Critical warning": "Критический",
|
||||
"Waifus only | Excellent quality, limited quantity": "Только вайфу | Превосходное качество, ограниченное количество",
|
||||
"Unknown function call: %1": "Неизвестный вызов функции: %1",
|
||||
"Neutral": "Нейтрал",
|
||||
"Anime": "Аниме",
|
||||
"Cannot switch to search mode from %1": "Невозможно переключиться в режим поиска из %1",
|
||||
"Useless buttons": "Бесполезные кнопочки",
|
||||
"Unfinished": "Незавершенные",
|
||||
"Privacy Policy": "Политика конфиденциальности",
|
||||
"Online via %1 | %2's model": "Онлайн через %1 | Модель %2",
|
||||
"Large images | God tier quality, no NSFW.": "Огромные изображение | Божественное качество, нету NSFW.",
|
||||
"Base URL": "Базовый URL",
|
||||
"Not visible to model": "Невидимый для модели",
|
||||
"Auto": "Авто",
|
||||
"Might look ass. Unsupported.": "Может выглядеть хуево. Не поддерживается",
|
||||
"%1 • %2 tasks": "%1 • %2 заданий",
|
||||
"Search the web": "Искать в интернете",
|
||||
"Scroll to change brightness": "Листайте, чтобы изменить яркость",
|
||||
"Invalid arguments. Must provide `key` and `value`.": "Неправильные аргументы. Нужно предоставить `key` и `value`.",
|
||||
"Settings": "Настройки",
|
||||
"Show regions of potential interest": "Показывать регионы с потенциальным интересом",
|
||||
"Pressure": "Давление",
|
||||
"No": "Нет",
|
||||
"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.": "Такие регионы могут быть изображениями или части экрана, которые имеют некую ограниченность.\nМожет быть неверно.\nЭто происходит с помощью алгоритма обработки изобржений локально и ИИ не используется",
|
||||
"Select Language": "Выбрать Язык",
|
||||
"Command rejected by user": "Команда отменена пользователем",
|
||||
"Approve": "Разрешить",
|
||||
"Terminal": "Терминал",
|
||||
"Search": "Поиск",
|
||||
"or": "или",
|
||||
"Experimental | Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput.": "Эксперементальная | Онлайн | Модель Google\nМодель Gemini 2.5 Flash, оптимизированная под экономию и высокую пропускную способность",
|
||||
"Uptime: %1": "Время работы: %1",
|
||||
"Save chat": "Сохранить чат",
|
||||
"Load prompt from %1": "Загрузить промпт из %1",
|
||||
"Critically low battery": "Критически низкий заряд батареи",
|
||||
"The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "Хентай | Отличный выбор, качество сильно варьируется",
|
||||
"Input": "Ввод",
|
||||
"Borderless": "Безрамочный",
|
||||
"Loaded the following system prompt\n\n---\n\n%1": "Загружен следующий системный промпт\n\n---\n\n%1",
|
||||
"Thought": "Мысли",
|
||||
"Your package manager is running": "Ваш менеджер пакетов работает",
|
||||
"12h AM/PM": "12 ч. AM/PM",
|
||||
"Scale (%)": "Размер (%)",
|
||||
"Waiting for response...": "Ожидание ответа...",
|
||||
"%1 does not require an API key": "%1 не требует API ключ",
|
||||
"Edit": "Изменить",
|
||||
"Dock": "Панель задач",
|
||||
"Set with /mode PROVIDER": "Установить с помощью /mode ПРОВАЙДЕР",
|
||||
"Low warning": "Низкий",
|
||||
"Silent": "Выкл. звук",
|
||||
"Rainbow": "Радуга",
|
||||
"Anime boorus": "Аниме боору",
|
||||
"Nothing here!": "Ничего нету!",
|
||||
"Documentation": "Документация",
|
||||
"Clear": "Очистить",
|
||||
"Transparency": "Прозрачность",
|
||||
"Show background": "Показывать фон",
|
||||
"Info": "Инфо",
|
||||
"12h am/pm": "12 ч. am/pm",
|
||||
"Reload Hyprland & Quickshell": "Перезагрузить Hyprland и Quickshell",
|
||||
"%1 characters": "%1 символов",
|
||||
". 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.)!": ". Напоминание для Zerochan:\n- Вы должны ввести цвет\n- Установите свой юзернейм Zerochan с помощью `sidebar.booru.zerochan.username` в конфиге. Вы [можете быть забанены, если не сделаете этого](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.)!",
|
||||
"For desktop wallpapers | Good quality": "Для обоев | Отличное качество",
|
||||
"Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Введите /key, чтобы начать работу с онлайн моделями\nCtrl+O для расширения панели\nCtrl+P для отсоединения панели",
|
||||
"Close": "Закрыть",
|
||||
"Search, calculate or run": "Поиск, вычислить, выполнить",
|
||||
"Add task": "Добавить задание",
|
||||
"Enable": "Включить",
|
||||
"Temperature set to %1": "Температура установлена на %1",
|
||||
"No media": "Нет медиа",
|
||||
"Screen snip": "Скриншот",
|
||||
"Go to source (%1)": "Перейти к источнику (%1)",
|
||||
"Download": "Скачать",
|
||||
"Sunset": "Закат",
|
||||
"Weeb": "Аниме",
|
||||
"The current system prompt is\n\n---\n\n%1": "Текущий системный промпт\n\n---\n\n%1",
|
||||
"Scroll to change volume": "Листайте, чтобы изменить громкость",
|
||||
"Unknown Title": "Неизвестное Название",
|
||||
"Policies": "Политики",
|
||||
"Disable NSFW content": "Отключить NSFW контент",
|
||||
"The current API used. Endpoint:": "Текущий используемый API. Точка входа:",
|
||||
"Humidity": "Влажность",
|
||||
"Cheat sheet": "Шпаргалка",
|
||||
"Translator": "Переводчик",
|
||||
"Be patient...": "Подождите...",
|
||||
"Experimental | Online | Google's model\nCan do a little more but takes an extra turn to perform search": "Эксперементальная | Онлайн | Модель Google\nМожет сделать чуть больше, но нужен дополнительный запрос для поиска",
|
||||
"Audio": "Аудио",
|
||||
"Preferred wallpaper zoom (%)": "Предпочитаемый зум обоев (%)",
|
||||
"Model set to %1": "Установлена модель %1",
|
||||
"Enter text to translate...": "Введите текст для перевода...",
|
||||
"Use Levenshtein distance-based algorithm instead of fuzzy": "Использовать алгоритм, основанный на расстоянии Левенштейна,\n вместо нечёткого сопоставления.",
|
||||
"Depends on workspace": "Зависит от пространства",
|
||||
"All-rounder | Good quality, decent quantity": "Универсальный | Хорошее качество и количество",
|
||||
"Timeout (ms)": "Таймаут (мс)",
|
||||
"Plain rectangle": "Обычный прямоугольник",
|
||||
"No API key\nSet it with /key YOUR_API_KEY": "Нет API ключа.\nУстановите его с помощью /key ВАШ_КЛЮЧ_API",
|
||||
"Center title": "Центрировать название",
|
||||
"Buttons": "Кнопки",
|
||||
"Copy code": "Скопировать код",
|
||||
"Precipitation": "Осадки",
|
||||
"The popular one | Best quantity, but quality can vary wildly": "Популярный | Огромный выбор, но качество сильно варьируется",
|
||||
"Temperature: %1": "Температура: %1",
|
||||
"Qt apps": "Qt",
|
||||
"Delete": "Удалить",
|
||||
"Saved to %1": "Сохранено в %1",
|
||||
"Temperature\nChange with /temp VALUE": "Температура\nИзмените с помощью /temp ЗНАЧЕНИЕ",
|
||||
"App": "Приложение",
|
||||
"Bluetooth": "Bluetooth",
|
||||
"Current API endpoint: %1\nSet it with %2mode PROVIDER": "Текущий API: %1\nИзменить с помощью %2mode ПРОВАЙДЕР",
|
||||
"Cancel": "Отменить",
|
||||
"Clean stuff | Excellent quality, no NSFW": "Чистый контент | Превосходное качество, нету NSFW",
|
||||
"Wallpaper": "Обои",
|
||||
"Provider set to": "Провайдер установлен на",
|
||||
"Weather": "Погода",
|
||||
"24h": "24 ч.",
|
||||
"Show next time": " Показать в следующий раз",
|
||||
"Unknown": "Неизвестно",
|
||||
"Launch": "Запустить",
|
||||
"Overview": "Обзор",
|
||||
"Help & Support": "Помощь и поддержка",
|
||||
"Night Light | Right-click to toggle Auto mode": "Ночной свет | ПКМ для переключения Авто режима",
|
||||
"Web search": "Веб-поиск",
|
||||
"Cannot find a GPS service. Using the fallback method instead.": "Не удалось найти GPS сервис. Используем запасный вариант.",
|
||||
"Large language models": "Большие языковые модели",
|
||||
"Dotfiles": "Дотфайлы",
|
||||
"When not fullscreen": "Когда не в полноэкранном режиме",
|
||||
"Screenshot tool": "Инструмент скриншотов",
|
||||
"Get the next page of results": "Получить резултаты следующей страницы",
|
||||
"Drag or click a region • LMB: Copy • RMB: Edit": "Проведите или кликните на регион • ЛКМ: Скопировать • ПКМ: Редактировать",
|
||||
"Material palette": "Материальная палитра",
|
||||
"Columns": "Столбцы",
|
||||
"Bar": "Панель",
|
||||
"Max allowed increase": "Макс. рост",
|
||||
"Always show numbers": "Всегда показывать номера",
|
||||
"%1 notifications": "%1 уведомлений",
|
||||
"Rows": "Ряды",
|
||||
"Invalid arguments. Must provide `command`.": "Неправильный аргумент. Нужно предоставить `command`.",
|
||||
"System": "Система",
|
||||
"Shell & utilities theming must also be enabled": "Расцветка оболочки также должна быть включена.",
|
||||
"%1 queries pending": "%1 запросов в очереди",
|
||||
"Copy": "Скопировать",
|
||||
"Logout": "Выход",
|
||||
"Pinned on startup": "Закреплена на запуске",
|
||||
"%1 | Right-click to configure": "%1 | ПКМ, чтобы настроить",
|
||||
"Donate": "Задонатить",
|
||||
"Temperature must be between 0 and 2": "Температура должна быть между 0 и 2",
|
||||
"Session": "Сессия",
|
||||
"Mic toggle": "Перекл. микрофон",
|
||||
"Reboot to firmware settings": "Перезагрузиться в настройки BIOS/UEFI",
|
||||
"Low battery": "Низкий",
|
||||
"Usage": "Использование",
|
||||
"Notifications": "Уведомления",
|
||||
"Consider plugging in your device": "Задумайтесь о подключении ПК к источнику питания",
|
||||
"Cloudflare WARP": "Cloudflare WARP",
|
||||
"Automatically suspends the system when battery is low": "Автоматически переходит в режим сна когда заряд низкий",
|
||||
"Services": "Сервисы",
|
||||
"Thinking": "Думаю",
|
||||
"Color generation": "Генерация цветов",
|
||||
"Number show delay when pressing Super (ms)": "Задержка показа номеров при нажатии Super (мс)",
|
||||
"illogical-impulse Welcome": "Добро пожаловать в illogical-impulse",
|
||||
"User agent (for services that require it)": "User agent (для сервисов, которым он нужен)",
|
||||
"Appearance": "Внешний вид",
|
||||
"On-screen display": "On-screen display",
|
||||
"Download complete": "Загрузка завершена",
|
||||
"Time": "Время",
|
||||
"Float": "Поверх",
|
||||
"Pick wallpaper image on your system": "Выберите изображение для обоев на своём ПК",
|
||||
"Prevents abrupt increments and restricts volume limit": "Предотвращает резкие увеличения громкости и закрепляет лимит громоксти",
|
||||
"Unknown Album": "Неизвестный Альбом",
|
||||
"Math result": "Результат вычисления",
|
||||
"Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "Случайные SFW обои с Konachan\nИзображение сохраняются в ~/Изображения/Wallpapers",
|
||||
"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)": "Может быть лучше, если вы сделаете много ошибок,\nно результаты могут быть странными и могут не работать с сокращениями (т.е. \"GIMP\" не выдаст программу для рисования)",
|
||||
"Select output device": "Выберите динамики",
|
||||
"Set the system prompt for the model.": "Задать системный промпт для этой модели.",
|
||||
"Choose file": "Выберите файл",
|
||||
"Choose model": "Выберите модель",
|
||||
"Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience": "Подсказка: Включите \"Показывать иконик \" и \"Всегда показывать номера\"\nдля классического опыта illogical-impulse",
|
||||
"Yes": "Да",
|
||||
"Local Ollama model | %1": "Локальная модель Ollama | %1",
|
||||
"API key set for %1": "API ключ установлен на %1",
|
||||
"Format": "Формат",
|
||||
"Colors & Wallpaper": "Цвета и Обои",
|
||||
"Note: turning off can hurt readability": "Подсказка: отключение может повредить читабельность",
|
||||
"illogical-impulse": "illogical-impulse",
|
||||
"Automatic suspend": "Авто-сон",
|
||||
"Random: Konachan": "Рандом: Konachan",
|
||||
"Workspace": "Рабочее пространство",
|
||||
"About": "О системе",
|
||||
"Color picker": "Пипетка",
|
||||
"Report a Bug": "Сообщить об ошибке",
|
||||
"Volume limit": "Лимит громкости",
|
||||
"GitHub": "GitHub",
|
||||
"Prefixes": "Префиксы",
|
||||
"Done": "Готово",
|
||||
"Invalid model. Supported: \n```": "Неправильная модель. Доступны: \n```",
|
||||
"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": "Чтобы установить API ключ, добавьте ее к команде\n\nЧтобы просмотреть ключ, добавьте \"get\" к команде<br/>\n\n### Для %1:\n\n**Ссылка**: %2\n\n%3",
|
||||
"UV Index": "УФ-индекс",
|
||||
"Clear the current list of images": "Очистить список изображений",
|
||||
"No audio source": "Нет источников аудио",
|
||||
"API key:\n\n```txt\n%1\n```": "API ключ:\n\n```txt\n%1\n```",
|
||||
"For storing API keys and other sensitive information": "Для хранения API ключей и другой чувствительной информации",
|
||||
"Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "Стрелочки для навигации, Enter для выбора\nEsc или клик везде чтобы отменить",
|
||||
"Networking": "Сеть",
|
||||
"Keep system awake": "Держать ПК включённым",
|
||||
"Polling interval (ms)": "Интервал опроса (мс)",
|
||||
"To Do": "Задачи",
|
||||
"Workspaces": "Рабочие пространства",
|
||||
"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": "Это не сработало. Подсказки:\n- Проверьте теги и настройки NSFW\n- Если на уме нету тегов, введите номер страницы",
|
||||
"Jump to current month": "Перейти к текущему месяцу",
|
||||
"Enter tags, or \"%1\" for commands": "Введите теги, или \"%1\" для команд",
|
||||
"No API key set for %1": "API ключ не установлен для %1",
|
||||
"Allow NSFW content": "Разрешить NSFW контент",
|
||||
"Save to Downloads": "Сохранить в загрузки",
|
||||
"Light": "Светлый",
|
||||
"Keyboard toggle": "Экранная клавиатура",
|
||||
"Night Light": "Night Light",
|
||||
"We": "Ср/*keep*/",
|
||||
"Mo": "Пн/*keep*/",
|
||||
"Su": "Вс/*keep*/",
|
||||
"Th": "Чт/*keep*/",
|
||||
"Tu": "Вт/*keep*/",
|
||||
"Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Эксперементальная | Онлайн | Модель Google\nМожет немного больше но не ищет очень быстро",
|
||||
"Sa": "Сб/*keep*/",
|
||||
"Chain of Thought": "Цепочка мыслей",
|
||||
"Fr": "Пт/*keep*/",
|
||||
"Usage: %1load CHAT_NAME": "Использование: %1load ИМЯ_ЧАТА",
|
||||
"Tool set to %1": "Установлен инструмент %1",
|
||||
"Set the tool to use for the model.": "Установите инструмент для этой модели.",
|
||||
"Invalid tool. Supported tools:\n- %1": "Неправильный инструмент. Доступны:\n- %1",
|
||||
"Usage: %1tool TOOL_NAME": "Использование: %1tool ИНСТРУМЕНТ",
|
||||
"Performance Profile toggle": "Профили производительности",
|
||||
"Usage: %1save CHAT_NAME": "Использование: %1save ИМЯ_ЧАТА",
|
||||
"Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput.": "Онлайн | Модель Google\nМодель Gemini 2.5 Flash оптимизирована под меньшие затраты и высокую производительнность",
|
||||
"Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers": "Онлайн | Модель Google\nНовая модель, которая медленее, чем ее предшественник, но выдает лучшее качество",
|
||||
"Online | Google's model\nFast, can perform searches for up-to-date information": "Онлайн | Модель Google\nБыстрая, может выполнять поиск актуальной информации",
|
||||
"Current tool: %1\nSet it with %2tool TOOL": "Текущий инструмент: %1\nИзменяется с помощью %2tool ИНСТРУМЕНТ"
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
{
|
||||
"Mo": "一/*keep*/",
|
||||
"Tu": "二/*keep*/",
|
||||
"We": "三/*keep*/",
|
||||
"Th": "四/*keep*/",
|
||||
"Fr": "五/*keep*/",
|
||||
"Sa": "六/*keep*/",
|
||||
"Su": "日/*keep*/",
|
||||
"%1 characters": "%1 个字符",
|
||||
"**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": "**价格**:免费。数据用于训练。\n\n**说明**:登录 Google 账户,允许 AI Studio 创建 Google Cloud 项目或其他要求,然后返回并点击获取 API 密钥",
|
||||
"**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": "**价格**:免费。数据使用政策取决于您的 OpenRouter 账户设置。\n\n**说明**:登录 OpenRouter 账户,在右上角菜单中选择 Keys,点击创建 API 密钥",
|
||||
". 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>未提供进一步说明</i>",
|
||||
"API key set for %1": "已为 %1 设置 API 密钥",
|
||||
"API key:\n\n```txt\n%1\n```": "API 密钥:\n\n```txt\n%1\n```",
|
||||
"Action": "操作",
|
||||
"Add": "添加",
|
||||
"Add task": "添加任务",
|
||||
"All-rounder | Good quality, decent quantity": "全能型 | 质量好,数量适中",
|
||||
"Allow NSFW": "允许 NSFW",
|
||||
"Allow NSFW content": "允许 NSFW 内容",
|
||||
"Anime": "动漫",
|
||||
"Anime boorus": "动漫图库",
|
||||
"App": "应用",
|
||||
"Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "方向键导航,回车选择\nEsc 或点击任意地方取消",
|
||||
"Bluetooth": "蓝牙",
|
||||
"Brightness": "亮度",
|
||||
"Cancel": "取消",
|
||||
"Chain of Thought": "思维链",
|
||||
"Cheat sheet": "快捷键表",
|
||||
"Choose model": "选择模型",
|
||||
"Clean stuff | Excellent quality, no NSFW": "清洁内容 | 优秀质量,无 NSFW",
|
||||
"Clear": "清除",
|
||||
"Clear chat history": "清除聊天记录",
|
||||
"Clear the current list of images": "清除当前图片列表",
|
||||
"Close": "关闭",
|
||||
"Copy": "复制",
|
||||
"Copy code": "复制代码",
|
||||
"Current API endpoint: %1\nSet it with %2mode PROVIDER": "当前 API 端点:%1\n使用 %2mode PROVIDER 设置",
|
||||
"Delete": "删除",
|
||||
"Desktop": "桌面",
|
||||
"Disable NSFW content": "禁用 NSFW 内容",
|
||||
"Done": "完成",
|
||||
"Download": "下载",
|
||||
"Edit": "编辑",
|
||||
"Enter text to translate...": "输入要翻译的文本...",
|
||||
"Finished tasks will go here": "已完成的任务将显示在这里",
|
||||
"For desktop wallpapers | Good quality": "桌面壁纸专用 | 质量好",
|
||||
"For storing API keys and other sensitive information": "用于存储 API 密钥和其他敏感信息",
|
||||
"Game mode": "游戏模式",
|
||||
"Get the next page of results": "获取下一页结果",
|
||||
"Go to source (%1)": "转到源 (%1)",
|
||||
"Hibernate": "休眠",
|
||||
"Input": "输入",
|
||||
"Intelligence": "智能体",
|
||||
"Interface": "界面",
|
||||
"Invalid arguments. Must provide `key` and `value`.": "参数无效。必须提供 `key` 和 `value`。",
|
||||
"Jump to current month": "跳转到当前月份",
|
||||
"Keep system awake": "保持系统唤醒",
|
||||
"Large images | God tier quality, no NSFW.": "大尺寸图片 | 顶级质量,无 NSFW",
|
||||
"Large language models": "大语言模型",
|
||||
"Launch": "启动",
|
||||
"Local Ollama model | %1": "本地 Ollama 模型 | %1",
|
||||
"Lock": "锁定",
|
||||
"Logout": "注销",
|
||||
"Markdown test": "Markdown 测试",
|
||||
"Math result": "数学结果",
|
||||
"Night Light": "护眼模式",
|
||||
"No API key set for %1": "未为 %1 设置 API 密钥",
|
||||
"No audio source": "无音频源",
|
||||
"No media": "无媒体",
|
||||
"No notifications": "无通知",
|
||||
"Not visible to model": "对模型不可见",
|
||||
"Nothing here!": "这里什么都没有!",
|
||||
"Notifications": "通知",
|
||||
"OK": "确定",
|
||||
"Open file link": "打开文件链接",
|
||||
"Output": "输出",
|
||||
"Page %1": "第 %1 页",
|
||||
"Reboot": "重启",
|
||||
"Reboot to firmware settings": "重启到固件设置",
|
||||
"Reload Hyprland & Quickshell": "重新加载 Hyprland 和 Quickshell",
|
||||
"Run": "运行",
|
||||
"Run command": "运行命令",
|
||||
"Save": "保存",
|
||||
"Save to Downloads": "保存到下载文件夹",
|
||||
"Search": "搜索",
|
||||
"Search the web": "在网络上搜索",
|
||||
"Search, calculate or run": "搜索、计算或运行",
|
||||
"Select Language": "选择语言",
|
||||
"Session": "会话",
|
||||
"Set API key": "设置 API 密钥",
|
||||
"Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "设置模型的温度(随机性)。Gemini 模型范围为 0 到 2,其他模型为 0 到 1。默认值为 0.5。",
|
||||
"Set the current API provider": "设置当前 API 提供商",
|
||||
"Shutdown": "关机",
|
||||
"Silent": "静音",
|
||||
"Sleep": "睡眠",
|
||||
"System": "系统",
|
||||
"Task Manager": "任务管理器",
|
||||
"Task description": "任务描述",
|
||||
"Temperature must be between 0 and 2": "温度必须在 0 到 2 之间",
|
||||
"Temperature set to %1": "温度设置为 %1",
|
||||
"Temperature: %1": "温度:%1",
|
||||
"The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "成人向 | 数量巨大,大量 NSFW,质量参差不齐",
|
||||
"The popular one | Best quantity, but quality can vary wildly": "最受欢迎 | 数量最多,但质量参差不齐",
|
||||
"Thinking": "思考中",
|
||||
"Translation goes here...": "翻译结果会显示在这里...",
|
||||
"Translator": "翻译器",
|
||||
"Unfinished": "未完成",
|
||||
"Unknown": "未知",
|
||||
"Unknown Album": "未知专辑",
|
||||
"Unknown Artist": "未知艺术家",
|
||||
"Unknown Title": "未知标题",
|
||||
"Unknown function call: %1": "未知函数调用:%1",
|
||||
"Uptime: %1": "运行时间:%1",
|
||||
"View Markdown source": "查看 Markdown 源码",
|
||||
"Volume": "音量",
|
||||
"Volume mixer": "音量混合器",
|
||||
"Waifus only | Excellent quality, limited quantity": "仅限角色 | 优秀质量,数量有限",
|
||||
"Waiting for response...": "等待响应...",
|
||||
"Workspace": "工作区",
|
||||
"%1 Safe Storage": "%1 安全存储",
|
||||
"%1 does not require an API key": "%1 不需要 API 密钥",
|
||||
"%1 queries pending": "%1 个查询等待中",
|
||||
"%1 | Right-click to configure": "%1 | 右键点击进行配置",
|
||||
"Set with /mode PROVIDER": "使用 /mode PROVIDER 设置",
|
||||
"Invalid API provider. Supported: \n-": "无效的 API 提供商。支持的:\n-",
|
||||
"Unknown command:": "未知命令:",
|
||||
"Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "输入 /key 开始使用在线模型\nCtrl+O 展开侧边栏\nCtrl+P 将侧边栏分离为窗口",
|
||||
"The current API used. Endpoint:": "当前使用的 API。端点:",
|
||||
"Provider set to": "提供商设置为",
|
||||
"Invalid model. Supported: \n```": "无效模型。支持的:\n```",
|
||||
"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": "实验性 | 在线 | Google 模型\n功能更多但搜索速度较慢",
|
||||
"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": "要设置 API 密钥,请将其与命令一起传递\n\n要查看密钥,请将 \"get\" 与命令一起传递<br/>\n\n### 对于 %1:\n\n**链接**:%2\n\n%3",
|
||||
"Enter tags, or \"%1\" for commands": "输入标签,或 \"%1\" 查看命令",
|
||||
"Online via %1 | %2's model": "通过 %1 在线 | %2 的模型",
|
||||
"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": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码",
|
||||
"Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。",
|
||||
"Settings": "设置",
|
||||
"Save chat": "保存对话",
|
||||
"Load chat": "加载对话",
|
||||
"or": "或",
|
||||
"Set the system prompt for the model.": "为模型设置系统提示。",
|
||||
"To Do": "待办",
|
||||
"Calendar": "日历",
|
||||
"Advanced": "高级",
|
||||
"About": "关于",
|
||||
"Services": "服务",
|
||||
"Style": "样式",
|
||||
"Edit config": "编辑配置",
|
||||
"Colors & Wallpaper": "颜色和壁纸",
|
||||
"Light": "浅色",
|
||||
"Dark": "深色",
|
||||
"Material palette": "颜色主题",
|
||||
"Fidelity": "保真度",
|
||||
"Fruit Salad": "水果沙拉",
|
||||
"Alternatively use /dark, /light, /img in the launcher": "或者在启动器中使用 /dark、/light、/img",
|
||||
"Fake screen rounding": "伪造屏幕圆角",
|
||||
"When not fullscreen": "非全屏时",
|
||||
"Choose file": "选择文件",
|
||||
"Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "随机 Konachan SFW 动漫壁纸\n图片保存到 ~/图片/Wallpapers",
|
||||
"Be patient...": "请耐心等待...",
|
||||
"Decorations & Effects": "装饰与特效",
|
||||
"Tonal Spot": "色调点",
|
||||
"Shell windows": "Shell 窗口",
|
||||
"Auto": "自动",
|
||||
"Wallpaper": "壁纸",
|
||||
"Content": "内容",
|
||||
"Title bar": "标题栏",
|
||||
"Transparency": "透明度",
|
||||
"Expressive": "表现力",
|
||||
"Yes": "是",
|
||||
"Enable": "启用",
|
||||
"Rainbow": "彩虹",
|
||||
"Might look ass. Unsupported.": "可能效果很差。不支持。",
|
||||
"Monochrome": "单色",
|
||||
"Random: Konachan": "随机:Konachan",
|
||||
"Center title": "标题居中",
|
||||
"Neutral": "中性",
|
||||
"Pick wallpaper image on your system": "在系统中选择壁纸图片",
|
||||
"No": "否",
|
||||
"AI": "AI",
|
||||
"Local only": "仅本地",
|
||||
"Policies": "策略",
|
||||
"Weeb": "二次元",
|
||||
"Closet": "隐藏",
|
||||
"Bar style": "条栏样式",
|
||||
"Show next time": "下次显示",
|
||||
"Usage": "用法",
|
||||
"Plain rectangle": "纯矩形",
|
||||
"Useless buttons": "无用按钮",
|
||||
"GitHub": "GitHub",
|
||||
"Style & wallpaper": "样式与壁纸",
|
||||
"Configuration": "配置",
|
||||
"Change any time later with /dark, /light, /img in the launcher": "之后可在启动器用 /dark、/light、/img 更改",
|
||||
"Keybinds": "快捷键",
|
||||
"Float": "浮动",
|
||||
"Hug": "贴合",
|
||||
"Yooooo hi there": "哟嗬,您好呀",
|
||||
"illogical-impulse Welcome": "illogical-impulse 欢迎页",
|
||||
"Info": "信息",
|
||||
"Volume limit": "音量限制",
|
||||
"Prevents abrupt increments and restricts volume limit": "防止骤增并限制音量",
|
||||
"Resources": "资源",
|
||||
"12h am/pm": "12小时 上午/下午",
|
||||
"Base URL": "基础 URL",
|
||||
"Audio": "声音",
|
||||
"Networking": "网络",
|
||||
"Format": "格式",
|
||||
"Time": "时间",
|
||||
"Battery": "电池",
|
||||
"Prefixes": "前缀",
|
||||
"Emojis": "表情符号",
|
||||
"Earbang protection": "防爆音保护",
|
||||
"Automatically suspends the system when battery is low": "电池电量低时自动挂起系统",
|
||||
"Automatic suspend": "自动挂起",
|
||||
"Suspend at": "挂起阈值",
|
||||
"Max allowed increase": "最大允许增幅",
|
||||
"Web search": "网页搜索",
|
||||
"Polling interval (ms)": "轮询间隔(毫秒)",
|
||||
"Clipboard": "剪贴板",
|
||||
"Low warning": "低电量警告",
|
||||
"24h": "24小时制",
|
||||
"Use Levenshtein distance-based algorithm instead of fuzzy": "使用 Levenshtein 距离算法替代模糊匹配",
|
||||
"System prompt": "系统提示词",
|
||||
"12h AM/PM": "12小时 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)": "如果你经常打错字可能更好用,但结果可能很奇怪,并且可能无法匹配缩写(如 \"GIMP\" 可能搜不到绘图程序)",
|
||||
"Critical warning": "临界警告",
|
||||
"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.": "这些区域可能是图片或屏幕中具有一定包容性的部分。\n可能并不总是准确。\n这是通过本地运行的图像处理算法实现的,没有使用 AI。",
|
||||
"Note: turning off can hurt readability": "注意:关闭后可能影响可读性",
|
||||
"Workspaces shown": "显示的工作区数",
|
||||
"Dark/Light toggle": "深浅色切换",
|
||||
"Dock": "停靠栏",
|
||||
"Weather": "天气",
|
||||
"Pinned on startup": "启动时固定",
|
||||
"Tip: Hide icons and always show numbers for\nthe classic illogical-impulse experience": "提示:隐藏图标并始终显示数字以获得经典体验",
|
||||
"Appearance": "外观",
|
||||
"Always show numbers": "总是显示数字",
|
||||
"Buttons": "按钮",
|
||||
"Keyboard toggle": "键盘切换",
|
||||
"Scale (%)": "缩放比例(%)",
|
||||
"Overview": "概览",
|
||||
"Rows": "行数",
|
||||
"Borderless": "无边框",
|
||||
"Screenshot tool": "截图工具",
|
||||
"Number show delay when pressing Super (ms)": "按下 Super 时数字显示延迟(ms)",
|
||||
"Timeout (ms)": "超时时间(ms)",
|
||||
"Show app icons": "显示应用图标",
|
||||
"Workspaces": "工作区",
|
||||
"Columns": "列数",
|
||||
"On-screen display": "屏幕显示",
|
||||
"Screen snip": "屏幕截图",
|
||||
"Mic toggle": "麦克风切换",
|
||||
"Hover to reveal": "悬停显示",
|
||||
"Bar": "条栏",
|
||||
"Show background": "显示背景",
|
||||
"Show regions of potential interest": "显示可能感兴趣的区域",
|
||||
"Color picker": "取色器",
|
||||
"Help & Support": "帮助与支持",
|
||||
"Discussions": "讨论区",
|
||||
"Color generation": "配色生成",
|
||||
"Dotfiles": "配置文件",
|
||||
"Distro": "发行版",
|
||||
"Privacy Policy": "隐私政策",
|
||||
"Documentation": "文档",
|
||||
"Shell & utilities theming must also be enabled": "必须同时启用 Shell 与工具主题",
|
||||
"illogical-impulse": "illogical-impulse",
|
||||
"Donate": "捐助",
|
||||
"Terminal": "终端",
|
||||
"Shell & utilities": "Shell 与工具",
|
||||
"Qt apps": "Qt 应用",
|
||||
"Report a Bug": "报告问题",
|
||||
"Issues": "问题追踪",
|
||||
"Drag or click a region • LMB: Copy • RMB: Edit": "拖动或点击一个区域 • 鼠标左键:复制 • 鼠标右键:编辑",
|
||||
"Current model: %1\nSet it with %2model MODEL": "当前模型:%1\n使用 %2model MODEL 设置",
|
||||
"Message the model... \"%1\" for commands": "与模型对话... \"%1\" 查看命令",
|
||||
"The current system prompt is\n\n---\n\n%1": "当前系统提示词为\n\n---\n\n%1",
|
||||
"Model set to %1": "模型已设置为 %1",
|
||||
"Loaded the following system prompt\n\n---\n\n%1": "已加载以下系统提示词\n\n---\n\n%1",
|
||||
"%1 notifications": "%1 条通知",
|
||||
"Save chat to %1": "保存聊天记录到 %1",
|
||||
"Load chat from %1": "从 %1 加载聊天记录",
|
||||
"Load prompt from %1": "从 %1 加载提示词",
|
||||
"Select output device": "选择输出设备",
|
||||
"%1 • %2 tasks": "%1 • %2 个任务",
|
||||
"Online models disallowed\n\nControlled by `policies.ai` config option": "禁止在线模型\n\n由 `policies.ai` 配置项控制",
|
||||
"Select input device": "选择输入设备",
|
||||
"Low battery": "电量低",
|
||||
"Registration failed. Please inspect manually with the <tt>warp-cli</tt> command": "注册失败。请使用 <tt>warp-cli</tt> 命令手动检查",
|
||||
"Code saved to file": "代码已保存到文件",
|
||||
"Consider plugging in your device": "请考虑连接您的设备",
|
||||
"Weather Service": "天气服务",
|
||||
"Please charge!\nAutomatic suspend triggers at %1": "请充电!\n自动挂起将在 %1 时触发",
|
||||
"Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)",
|
||||
"Cloudflare WARP": "Cloudflare WARP",
|
||||
"Download complete": "下载完成",
|
||||
"Critically low battery": "电量极低",
|
||||
"Scroll to change brightness": "滚动以调节亮度",
|
||||
"Saved to %1": "已保存到 %1",
|
||||
"Cannot find a GPS service. Using the fallback method instead.": "无法找到 GPS 服务。正在使用备用方法。",
|
||||
"Elements": "元素",
|
||||
"Scroll to change volume": "滚动以调节音量",
|
||||
"Connection failed. Please inspect manually with the <tt>warp-cli</tt> command": "连接失败。请使用 <tt>warp-cli</tt> 命令手动检查",
|
||||
"UV Index": "紫外线指数",
|
||||
"Pressure": "气压",
|
||||
"Visibility": "能见度",
|
||||
"Sunrise": "日出",
|
||||
"Sunset": "日落",
|
||||
"Humidity": "湿度",
|
||||
"Wind": "风",
|
||||
"Precipitation": "降水量"
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
@@ -0,0 +1,64 @@
|
||||
# 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).
|
||||
|
||||
# Translations
|
||||
|
||||
See `dots/.config/quickshell/ii/translations/tools`
|
||||
|
||||
# Code
|
||||
|
||||
## Dynamic loading
|
||||
|
||||
- If something's not always necessary, especially when guarded by a config option to enable/disable, put it in a `Loader`
|
||||
- Note that you will need to declare positioning properties (like `anchors`) in the `Loader`, not the `sourceComponent`
|
||||
- When something that's to be dynamically loaded doesn't affect its parent layout, you can have a fading animation by using FadeLoader and set the `shown` prop instead of `active` and `visible`
|
||||
|
||||
## 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 (example: constantly rotating background clock)
|
||||
|
||||
## Style
|
||||
|
||||
- Spaces
|
||||
- Space properties and children data into meaningful groups. (but of course, don't use 2+ blanks in a row)
|
||||
- Put spaces between text and operators: `if (condition) { ... } else { ... }` instead of `if(condition){ ... }else{ ... }`
|
||||
- As you can see, it's pretty easy to use lots of nesting. There's no hard limit, end-4 himself nests a lot too, but avoid/mitigate that:
|
||||
- Prefer early return: Use something like `if (!condition) return; doStuff();` instead of `if (condition) { doStuff() }`
|
||||
- If you feel it's a bother to refractor something into a new file, remember there's `component` to declare reusable components in the same file.
|
||||
|
||||
# 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:
|
||||
- type: markdown
|
||||
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, please submit [Discussion at Extra Distros](https://github.com/end-4/dots-hyprland/discussions/new?category=extra-distros) instead."
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
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."
|
||||
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
|
||||
- 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
|
||||
- label: I've successfully updated the system packages to the latest.
|
||||
required: false # Not required cuz user may have failed to do so
|
||||
- label: I've ticked the checkboxes without reading their contents
|
||||
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
|
||||
attributes:
|
||||
label: "Step 2. Quick diagnose info"
|
||||
@@ -33,7 +33,7 @@ body:
|
||||
**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.
|
||||
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.
|
||||
- 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).
|
||||
|
||||
@@ -4,7 +4,7 @@ labels: ["FEATURE"]
|
||||
body:
|
||||
- type: markdown
|
||||
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
|
||||
attributes:
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
<div align="center">
|
||||
<h1>【 end_4's Hyprland dotfiles 】</h1>
|
||||
<h3></h3>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
|
||||

|
||||

|
||||

|
||||
<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 align="center">
|
||||
<h2>• overview •</h2>
|
||||
<h3></h3>
|
||||
</div>
|
||||
|
||||
<details>
|
||||
<summary>What this is/isn't</summary>
|
||||
|
||||
- Technically, configuration files
|
||||
- Realistically, mostly the custom graphical shell
|
||||
- NOT a system setup script: no graphic drivers, no zram setup, etc.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Notable features</summary>
|
||||
|
||||
- **Overview**: Shows open apps with live previews
|
||||
- **AI**: Gemini, Ollama, and more
|
||||
- **QoL**: screen translation, anti-flashbang, Google Lens
|
||||
- **Material themes**: Choose your wallpaper, done, enjoy
|
||||
- **Transparent installation**: Every command is shown before it's run
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Installation</summary>
|
||||
|
||||
- _If you're new to Linux and decide to use Hyprland, you're in for a tough ride._
|
||||
- Just run `bash <(curl -s https://ii.clsty.link/get)`
|
||||
- Or, clone this repo and run `./setup install`
|
||||
- See [the wiki](https://ii.clsty.link/en/ii-qs/01setup/) for more details
|
||||
- **Keybinds**: Should be somewhat familiar to Windows or GNOME users. Important ones:
|
||||
- `Super`+`/` = keybind list
|
||||
- `Super`+`Enter` = terminal
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Software overview</summary>
|
||||
|
||||
| Software | Purpose |
|
||||
| ------------- | ------------- |
|
||||
| [Hyprland](https://github.com/hyprwm/hyprland) | The compositor (manages and renders windows) |
|
||||
| [Quickshell](https://quickshell.outfoxxed.me/) | A QtQuick-based widget system, used for the status bar, sidebars, etc. |
|
||||
| Others | See [deps-info.md](https://github.com/end-4/dots-hyprland/blob/main/sdata/deps-info.md) |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Discord</summary>
|
||||
<a href="https://discord.gg/GtdRBXgMwq"> Server link</a> | I hope this provides a friendlier environment for support without needing me to personally accept every friend request/DM. For real issues, prefer GitHub
|
||||
|
||||
</details>
|
||||
|
||||
<div align="center">
|
||||
<h2>• screenshots •</h2>
|
||||
<h3></h3>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<img src="assets/illogical-impulse.svg" alt="illogical-impulse logo" style="float:left; width:400;">
|
||||
</div>
|
||||
|
||||
Widget system: Quickshell | Support: Yes
|
||||
|
||||
[Showcase video](https://www.youtube.com/watch?v=RPwovTInagE)
|
||||
|
||||
| AI, settings app | Some widgets |
|
||||
|:---|:---------------|
|
||||
| <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/5d4e7d07-d0b4-4406-a4c9-ed7ba90e3fe4" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/6a32395f-9437-4192-8faf-2951a9e84cbe" /> |
|
||||
| Window management | wow look its orange |
|
||||
| <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/c51bed8b-3670-4d4c-9074-873be224fb8e" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/98703a66-0743-439f-a721-cef7afa6ab95" /> |
|
||||
|
||||
<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>
|
||||
|
||||
- I promise not to attempt an +ULTRARICOSHOT irl... Coins can go here: https://github.com/sponsors/end-4
|
||||
- Tentacle cat hub twinkle internet points
|
||||
|
||||
[](https://starchart.cc/end-4/dots-hyprland)
|
||||
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<h2>• previous styles •</h2>
|
||||
<h3></h3>
|
||||
</div>
|
||||
|
||||
- **Unsupported!**
|
||||
- **Source**: illogical-impulse AGS in `ii-ags` branch, others in `archive` branch.
|
||||
- List is in reverse chronological order
|
||||
|
||||
### illogical-impulse (AGS)
|
||||
|
||||
Widget system: AGS | Support: No
|
||||
|
||||
| AI | Common widgets |
|
||||
|:---|:---------------|
|
||||
|  |  |
|
||||
| Window management | Weeb power |
|
||||
|  |  |
|
||||
|
||||
#### m3ww
|
||||
|
||||
Widget system: EWW | Support: No
|
||||
|
||||
<a href="https://streamable.com/85ch8x">
|
||||
<img src="https://github.com/end-4/dots-hyprland/assets/97237370/09533e64-b6d7-47eb-a840-ee90c6776adf" alt="Material Eww!">
|
||||
</a>
|
||||
|
||||
#### NovelKnock
|
||||
|
||||
Widget system: EWW | Support: No
|
||||
|
||||
<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">
|
||||
</a>
|
||||
|
||||
#### Hybrid
|
||||
|
||||
Widget system: EWW | Support: No
|
||||
|
||||
<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!">
|
||||
</a>
|
||||
|
||||
#### Windoes
|
||||
|
||||
Widget system: EWW | Support: No
|
||||
|
||||
<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">
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<div align="center">
|
||||
<h2>• inspirations/copying •</h2>
|
||||
<h3></h3>
|
||||
</div>
|
||||
|
||||
- Inspiration: osu!lazer (Hybrid), Windows 11 (Windoes), AvdanOS (NovelKnock), Material Design 3 (m3ww & later)
|
||||
- Copying: Absolutely, feel free. Just follow the license and it's all good
|
||||
|
||||
@@ -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,50 @@
|
||||
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
|
||||
if: github.ref == 'refs/heads/main' && github.repository == 'end-4/dots-hyprland'
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
'
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user