Compare commits
1503 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b25513a06f | |||
| 370140143d | |||
| 10092ba792 | |||
| d376e202c8 | |||
| d7646274c0 | |||
| f3d767d794 | |||
| 8cb0f916aa | |||
| ba1c274ffb | |||
| 3708680aed | |||
| e96acf011b | |||
| 9034c9a2c2 | |||
| 504346d9a0 | |||
| ae5b140da0 | |||
| a33c3d8602 | |||
| fc5bfeaee1 | |||
| c4b761cce0 | |||
| 2b98a1e0a1 | |||
| 27d3449960 | |||
| 44ffd2aea1 | |||
| cc56552cb0 | |||
| ca85c9e803 | |||
| a70d98338b | |||
| ecef927e95 | |||
| fb9816791e | |||
| 44ed070a28 | |||
| 8a2a84fa35 | |||
| 0825ea0160 | |||
| bc3b798d04 | |||
| 598e696338 | |||
| 6e90f6876e | |||
| d3aec6b6d0 | |||
| 9af2f378e8 | |||
| e0461b89fb | |||
| 7e6abebf9e | |||
| ec5514a4a0 | |||
| e88cd97b30 | |||
| d6e35d35c3 | |||
| f2b212dc2e | |||
| 9681e7eb19 | |||
| 0e1b53dfa7 | |||
| d9649bc8d9 | |||
| 3cd2246a51 | |||
| fdf75eebf3 | |||
| 697f5b77b5 | |||
| 9e63567532 | |||
| bf7c6acba9 | |||
| 5700265c39 | |||
| 6267eed8b3 | |||
| 52cc4762ba | |||
| 1beea1586e | |||
| cbef2a0cfc | |||
| f778829e81 | |||
| bba4eba3d5 | |||
| 4c6f774553 | |||
| 1fc7a6de95 | |||
| 47489fd513 | |||
| caf68ba5e6 | |||
| 5532acf426 | |||
| bfe2b8bc10 | |||
| 876117bbf4 | |||
| 092f81dad6 | |||
| 5d768d81b0 | |||
| 56851519da | |||
| 810ebff849 | |||
| 7e77f629f6 | |||
| 1abea47914 | |||
| ae7ab838f4 | |||
| c79f62d874 | |||
| f34e62f24a | |||
| 4f89780cfe | |||
| f1b3588465 | |||
| fdc0f1e648 | |||
| c0bcdc3829 | |||
| d34f875d35 | |||
| 7b22a2aea7 | |||
| 4b26971b48 | |||
| 640d1f459b | |||
| 8386608953 | |||
| d7cfa68a3f | |||
| 2a816019b6 | |||
| e9944ba83d | |||
| 9c21d99e01 | |||
| 7b720b2a3f | |||
| 3bddcb5461 | |||
| 4187b3a3c9 | |||
| 4e526476e3 | |||
| 3697e1b674 | |||
| 58c7467720 | |||
| 1d035109ea | |||
| fc85b09e88 | |||
| b51c0f1ee5 | |||
| 4d692e635b | |||
| 07a952a2a6 | |||
| 2589a244dd | |||
| 99ed365695 | |||
| 7f93d7ec6f | |||
| 17b72ef1bd | |||
| e99442cb1c | |||
| 085f139abd | |||
| f616d19a4e | |||
| 61cf3a960c | |||
| f94ec5a8a9 | |||
| a2f228e74d | |||
| 3ec1ff8c7e | |||
| 631fe4f362 | |||
| 6153480e43 | |||
| e1789d40d4 | |||
| 0fff162c65 | |||
| 0b70be9a9b | |||
| 04aec11daa | |||
| 91145dc868 | |||
| 252bf3e363 | |||
| 22449a0913 | |||
| 5ef60debc2 | |||
| eac7141ce7 | |||
| 6468b8ab19 | |||
| 11e84ffbef | |||
| 6a1212de82 | |||
| 92192715bb | |||
| 786e8999e1 | |||
| 02f0d7bb8a | |||
| f543c2569e | |||
| 9878ee6fa7 | |||
| 827ec824d1 | |||
| c9ca79ab47 | |||
| ed1e3cabc3 | |||
| 6419bb15fb | |||
| d53ee9c241 | |||
| 5bb03928a1 | |||
| c354ed7814 | |||
| 3fbe30231e | |||
| b5f3ea60b7 | |||
| bc2eb3df82 | |||
| 92488b29d5 | |||
| ef848e0a1b | |||
| 4319245dd3 | |||
| 5eed8136dd | |||
| 66b38ccb5f | |||
| aae5631f19 | |||
| 6cc58e3701 | |||
| 2cd90eb3ce | |||
| 1b96772126 | |||
| f31421a311 | |||
| e3a34eae29 | |||
| ce10b71122 | |||
| d39af8edf3 | |||
| b87df709df | |||
| 0e7a483c7b | |||
| 668a91bd15 | |||
| 59e9a91a1e | |||
| 33dda9dffd | |||
| f9d86283a3 | |||
| c5317e6de9 | |||
| d36f7d598c | |||
| 59e64f56fa | |||
| 05d2881c16 | |||
| ab61ddaf16 | |||
| cfa8ecbb2e | |||
| d6cb8d62af | |||
| faa7c1ca55 | |||
| 52615a3ccb | |||
| de737b3cbe | |||
| 9245c2e49b | |||
| 365cd728b8 | |||
| d6d7046edb | |||
| 418233fa2e | |||
| 37d870fdd9 | |||
| 9af4fb4a26 | |||
| 24edbf43ed | |||
| 8ed9085474 | |||
| a1389ac778 | |||
| 4f8b3efa5c | |||
| 2200dbb406 | |||
| f6d1208992 | |||
| 5ff29c41c1 | |||
| 1d04cae3c2 | |||
| 57c5ddc774 | |||
| 6f4c9f6f5a | |||
| 0dd4722865 | |||
| 045cb6822f | |||
| f4cbbb251f | |||
| 978116b107 | |||
| 2ff6ac44dc | |||
| 0a43ca759b | |||
| fcb695d691 | |||
| 51021d1508 | |||
| ac3c731b64 | |||
| 2994268b83 | |||
| 05cf8aaa0f | |||
| 9a9307ead3 | |||
| 33f86dc7b0 | |||
| a4e7923b15 | |||
| 4a0b47d286 | |||
| b4b3446135 | |||
| 82650f5420 | |||
| 4646cc7f21 | |||
| 2810b186bb | |||
| 8f2fbff93e | |||
| cdf1f16944 | |||
| 8dfb14cf7a | |||
| 432072a3d4 | |||
| c773f16a4a | |||
| bdcdafab2d | |||
| a8b59804fa | |||
| 07250a1bcf | |||
| d0e24b555c | |||
| 61f89d7b33 | |||
| 88404cfddb | |||
| 111b15a855 | |||
| 263ddc3bcf | |||
| 4009a47377 | |||
| 7cd4bd3cd4 | |||
| 1c94acdeca | |||
| e37bbb2bb7 | |||
| d1cac7804a | |||
| b3e5356b07 | |||
| 84e7520707 | |||
| e3e72ffd01 | |||
| 3f84f27354 | |||
| 4b470a9205 | |||
| 2079c8d4cf | |||
| 7871bc7097 | |||
| ae8de84828 | |||
| 9fc2ed338d | |||
| d9c6e3a322 | |||
| ca3eb7dca4 | |||
| f6abf7a2d2 | |||
| a8f9cf430d | |||
| 98f342227f | |||
| 55e284729e | |||
| fef4e14fb0 | |||
| eeafe97614 | |||
| f869ad620e | |||
| a5307489ff | |||
| b1d21600cf | |||
| 3bbef51448 | |||
| a17199c7a9 | |||
| 13ddffd66a | |||
| cebd3edb9a | |||
| b7b6329e7a | |||
| 6e21752528 | |||
| c9fcb8fdb4 | |||
| 172d842635 | |||
| 7a45cfa8e9 | |||
| a50dbd5886 | |||
| a17f20463f | |||
| b80f25fbc5 | |||
| 9cb41f4517 | |||
| 494833236f | |||
| 7c26a251cf | |||
| d164a49675 | |||
| 59b089a0cc | |||
| c031f09886 | |||
| 3af2bf61fe | |||
| 6f4ed6883b | |||
| ec94dc26ac | |||
| 8decb55e51 | |||
| 8346a90d65 | |||
| 4191ecc2e6 | |||
| 783ed506bd | |||
| 00bd28278d | |||
| b51c0ca5e7 | |||
| c6c2479268 | |||
| 300e0f2fb7 | |||
| 00154dcbe9 | |||
| 3884348e07 | |||
| a7745a2086 | |||
| 9caeea7edd | |||
| ae4e5ad54a | |||
| 18ea990e7a | |||
| 5d557b44e4 | |||
| ec312a1f15 | |||
| 87877fb506 | |||
| 9dce30e904 | |||
| 6fc6bb48ff | |||
| 8fecab58a9 | |||
| df71142949 | |||
| 4b65fd1126 | |||
| 814739b52b | |||
| 2f5970d57c | |||
| 79263209b3 | |||
| d56c2c5fc3 | |||
| 9e922e0610 | |||
| c9792e7bbc | |||
| 8c85ece0cb | |||
| 10bb323eb5 | |||
| 2c16e4b897 | |||
| 969cc0778b | |||
| b83533637d | |||
| c8d1044596 | |||
| 7011e205d5 | |||
| 6d2cf80f10 | |||
| 172bbb1b29 | |||
| cf45065e15 | |||
| d3460cafd0 | |||
| 3386e427a0 | |||
| a1a078f998 | |||
| a4da0f67f7 | |||
| fe04679307 | |||
| 8628e79245 | |||
| edb549bef6 | |||
| 085c4ba65d | |||
| 805225de3f | |||
| 8de878693d | |||
| 90ec66c34c | |||
| eba39d12aa | |||
| 5328e8a750 | |||
| df27d58597 | |||
| 8a519995da | |||
| 407bf19641 | |||
| 7091a3d156 | |||
| 990dc0f95c | |||
| 77f89d1967 | |||
| 3d2b8439ee | |||
| f3315e2c45 | |||
| 4fcaa1d1bb | |||
| 883a61e5a0 | |||
| 2f2fae3892 | |||
| 1fa0a8e7ee | |||
| cd1e0d710d | |||
| beeb4663c4 | |||
| a6d5300057 | |||
| a228e9c0ad | |||
| 829b0b5d27 | |||
| 7e07336e3b | |||
| 4663ea659a | |||
| 86a07d3ab0 | |||
| 2119dae0b5 | |||
| 6769944e16 | |||
| 6b45727129 | |||
| d98b5b2fe6 | |||
| 959863c70a | |||
| 689fc57932 | |||
| 5732b4f48c | |||
| 9febcd88d4 | |||
| af550199d4 | |||
| 87eedb822c | |||
| cbe908da79 | |||
| 666e40f002 | |||
| 2c003bc22e | |||
| bcaa449510 | |||
| 08f9fb2492 | |||
| 6bcf4cb42d | |||
| a36d90481c | |||
| 0f85080c15 | |||
| afddcbdd40 | |||
| ca63b12670 | |||
| 74e1c8c7d8 | |||
| a504dc2287 | |||
| 82884b8311 | |||
| 30fa759cde | |||
| 0fad7fc234 | |||
| 92fc06fd51 | |||
| f04a4d5c7f | |||
| d958f66e77 | |||
| aa74efdee7 | |||
| 76c5a7f8d0 | |||
| 3ec37968e9 | |||
| bb6fc19768 | |||
| b6ae741000 | |||
| 0c3cec0da4 | |||
| fe199447d5 | |||
| 96f23bf006 | |||
| 4aee7bfbc1 | |||
| 892de6154d | |||
| 6df0333ca2 | |||
| f5486e3090 | |||
| 989cbf0837 | |||
| 4d8244edd5 | |||
| 4c6404ed64 | |||
| 82f9aa1a2a | |||
| 73d0b4cb84 | |||
| 94b8685f23 | |||
| 8f56ef4502 | |||
| 27b041c4c5 | |||
| d4ad55f006 | |||
| 50cd572754 | |||
| f8efc992ef | |||
| 95a53d51a3 | |||
| f62ae325df | |||
| 8a1001333e | |||
| 8158c7f798 | |||
| 3c8d8d9243 | |||
| 319a914b66 | |||
| d24e73d6c7 | |||
| 175cb10ef6 | |||
| 1b153ded83 | |||
| 8349e7504a | |||
| 828eed0c34 | |||
| c4e53b55ce | |||
| 3e6da6210e | |||
| aa5e0440d4 | |||
| 8f4b6f80ad | |||
| b5b563fdec | |||
| faa48cad44 | |||
| 71afa9a481 | |||
| bd6c50c4c2 | |||
| cec81616a4 | |||
| 59bf5515c3 | |||
| d5f4c5366c | |||
| ee073f2e9b | |||
| b86ed7775a | |||
| 90191819bd | |||
| 094c43293d | |||
| d099e29fb9 | |||
| 90297d838d | |||
| 2c9137571e | |||
| a479459d76 | |||
| 0685687161 | |||
| 9f4923fc79 | |||
| f2c80f5b44 | |||
| bcb447520e | |||
| e66a5a3442 | |||
| 4b3945dec8 | |||
| daa46ab296 | |||
| 055bea4562 | |||
| 73dd472e28 | |||
| 763e9dad87 | |||
| 6eb486d885 | |||
| 48027ae66b | |||
| c0ae279031 | |||
| 74f2c49284 | |||
| 42e5bdeca5 | |||
| 7cd8b21af9 | |||
| fbc217d60c | |||
| ec3d981998 | |||
| 75632f7c86 | |||
| c7ec23b872 | |||
| 5472f97797 | |||
| 79d97463ec | |||
| 4015192c4a | |||
| c467238344 | |||
| 3a75568c2f | |||
| b6b9edcbea | |||
| 0f337ebbea | |||
| 65094326e8 | |||
| a29acd4882 | |||
| fa8eaaff4f | |||
| de42fb83c5 | |||
| e974d02b44 | |||
| e3f2e68947 | |||
| 5907145fcc | |||
| 2e7c5c8d7b | |||
| 6a187bff11 | |||
| 8579f1db95 | |||
| aeed2a0bcd | |||
| 3747d2d91d | |||
| 97f120a6e2 | |||
| 1f3ac5170f | |||
| 60ff46b012 | |||
| dc113c0620 | |||
| 5b10802cc8 | |||
| 6b5b32a62a | |||
| 3ceb28cd5e | |||
| 7e6fcc09f0 | |||
| c22e6b7cbf | |||
| bb84330289 | |||
| ee84743fb0 | |||
| da6956df3d | |||
| 0d003967e8 | |||
| 589e486cb8 | |||
| fd634164ed | |||
| 82ffe0f91f | |||
| f992678e3a | |||
| 23c6d595ff | |||
| 4221ea66da | |||
| c34f25f2bd | |||
| 5080556836 | |||
| 62674467ed | |||
| 42eaddfaba | |||
| 5f60eb53ff | |||
| b01e0b77fe | |||
| d1406f3f17 | |||
| 7942a24c0c | |||
| 5a2c0b1a19 | |||
| 36373574cf | |||
| 8585e8cb0c | |||
| 9fe4419269 | |||
| 8b37591882 | |||
| 7a5eefcc70 | |||
| bb6a008309 | |||
| 77529fb698 | |||
| edbdbef2eb | |||
| 0d93cb580d | |||
| 4f446e0102 | |||
| 969108381a | |||
| 53dc6d45b9 | |||
| db8878932f | |||
| cbf8f791bb | |||
| 27e04f3e0c | |||
| 58a2c0f834 | |||
| bb4d4e239f | |||
| ab1af9371e | |||
| 73baa8e679 | |||
| 6e89ec538c | |||
| c992e883b5 | |||
| 39ce6a1a0a | |||
| a91c2cd0a2 | |||
| b28b46bdc9 | |||
| f055478b6e | |||
| 7a586e680c | |||
| 54da1bf21b | |||
| 2b89fb9d40 | |||
| 07ca74ca0b | |||
| 554ba411f6 | |||
| 9f887dda4b | |||
| 7bb2f590c7 | |||
| ee58a97156 | |||
| 41aabe486b | |||
| 5515a662b8 | |||
| 66c450fc0d | |||
| 7e7af4118d | |||
| 5fec51d0c6 | |||
| 47e1d56033 | |||
| 89bee2dd51 | |||
| 26b437fcd6 | |||
| ad38e06555 | |||
| 64c172f590 | |||
| 471eaf7498 | |||
| b8888025ec | |||
| b0227e1bf6 | |||
| ecff8e3670 | |||
| d5ce6fff77 | |||
| b311c33473 | |||
| d58ec07343 | |||
| 8e746dcfbd | |||
| 2411009071 | |||
| 646f8c32f9 | |||
| 83de6c640c | |||
| bbdc6abea8 | |||
| 49e75e1ab0 | |||
| c24a58672b | |||
| a98b2f4a39 | |||
| 0328de897c | |||
| 48de6c495e | |||
| 39b3f0a386 | |||
| a0304b5147 | |||
| b06573b5c9 | |||
| 8c131cc616 | |||
| dc3d27993b | |||
| 9ed0520607 | |||
| c32fbf918f | |||
| dafcaa66a4 | |||
| d7f440513a | |||
| 8145b0b96e | |||
| 9e482556ea | |||
| 546c786ed3 | |||
| e613a89d02 | |||
| 3c3683a41e | |||
| 945373eb5b | |||
| b21e6860b3 | |||
| 99a5218eb3 | |||
| 5ccb58b1bb | |||
| 303d10939e | |||
| 963db7487b | |||
| 150f7dedcd | |||
| 80abcd70b5 | |||
| bbf23cefb6 | |||
| f73793ce8a | |||
| d48ceab73d | |||
| 4935ceda5b | |||
| 73e45b9cc7 | |||
| fe03dbab96 | |||
| 507af0a14d | |||
| 1dca7c2a69 | |||
| 712e79d0a2 | |||
| 353c95adae | |||
| c64f175b19 | |||
| b185900b4f | |||
| b55d80212c | |||
| 5f61c96ed9 | |||
| fa7b00c0e2 | |||
| 9dc17b1daf | |||
| a3d03a3c63 | |||
| 3fc8796bda | |||
| 34c9b24123 | |||
| 591f131972 | |||
| 8f610401ae | |||
| 1fa219c1cd | |||
| 19a1bd9451 | |||
| 577e8f14af | |||
| 3da3334131 | |||
| 3a5b2c6ecf | |||
| 391a3b02f0 | |||
| c57e40620b | |||
| 0dd30d4668 | |||
| 66e4cab8f8 | |||
| d462ca3241 | |||
| 1100b38b4c | |||
| f07510bd67 | |||
| 82d496ba24 | |||
| b6b6e18896 | |||
| f36ad1c37a | |||
| 21c6efa32a | |||
| c12e152618 | |||
| d455d6ee32 | |||
| 4069e4412c | |||
| 4d76520ce3 | |||
| 0bc9ee3cf1 | |||
| 90eda7c03a | |||
| eb6e6ec89a | |||
| 358a9f87a1 | |||
| 8224e6ece1 | |||
| 87ebd2720f | |||
| 98a6af1ca9 | |||
| ac19655d21 | |||
| 53a2d4704d | |||
| 994dd2e05c | |||
| 28637308ed | |||
| 68dd72f4fe | |||
| 5a39484407 | |||
| 4fcfbed7d2 | |||
| 5b3d9cf3ba | |||
| 9f57a82c75 | |||
| 1e6f7c7f3f | |||
| 8890ed606e | |||
| 41b699d8ad | |||
| b4f7c0fe9b | |||
| ba4715a3af | |||
| e49bf706c5 | |||
| 1c9ac520cb | |||
| 102f882ca4 | |||
| b93c93a20c | |||
| abb73d624e | |||
| 0aa7e85228 | |||
| dea23ce3c9 | |||
| aa60283fcb | |||
| 96231c217c | |||
| 7868e5e905 | |||
| d061c569b7 | |||
| 04eb23ae32 | |||
| 6edf31beb6 | |||
| 5dbeedd914 | |||
| 1d7c45b324 | |||
| 5b9ab96b19 | |||
| cfad40f7bb | |||
| 840366b385 | |||
| b4c04bd3b5 | |||
| 8bd64c62c2 | |||
| 42e7f625cd | |||
| 55fb7aeb68 | |||
| dfc6718bea | |||
| 06c5154e99 | |||
| 294516c2be | |||
| f1117b0071 | |||
| 776260d265 | |||
| af4c7bc754 | |||
| ff54ea88e2 | |||
| eef6ba797f | |||
| 18e9882916 | |||
| c299e673ad | |||
| 9533e95dd2 | |||
| 1610ad345f | |||
| 4ba2581df8 | |||
| d9b9a40119 | |||
| bb6481f194 | |||
| 1cbcef17c8 | |||
| 921d5a62d2 | |||
| 2cce6ddb3e | |||
| 73cbef5b82 | |||
| dc2d6e9d12 | |||
| b5f8abf45c | |||
| 36a3542d63 | |||
| 4fd4b14fcc | |||
| 5b916a5f5e | |||
| 7d1a7c2b6c | |||
| aa78825b7b | |||
| 308116f792 | |||
| d581bdcf56 | |||
| cf7ebb0f4a | |||
| 9275b45ab8 | |||
| a8452601d7 | |||
| a242e76f26 | |||
| 380c333185 | |||
| 02eac1ee14 | |||
| d0d1d91ef3 | |||
| 91df896f8e | |||
| 537375b92b | |||
| a5a43dcc7f | |||
| a4f0a636e2 | |||
| d189b1966e | |||
| 55649cba30 | |||
| 4b521e687e | |||
| 6ed80c9706 | |||
| 898eebbf4b | |||
| c4847e752c | |||
| 8aa09d32dc | |||
| 00ef43657e | |||
| 7c5475e62a | |||
| 8256ebd5aa | |||
| 38f059cfb2 | |||
| fca5b6f378 | |||
| 57b8740adf | |||
| c86d2c6986 | |||
| ccb563eff1 | |||
| e0d28a580c | |||
| f85da1ad12 | |||
| 87ef5d06df | |||
| f0a47c8769 | |||
| 27080d4b96 | |||
| a61334247d | |||
| f5281b7418 | |||
| 08be7f1960 | |||
| 5e44170091 | |||
| ff3dc8987d | |||
| f4a2be34ea | |||
| b1a537801c | |||
| 5521ff621a | |||
| 37037d0713 | |||
| 9672c147e2 | |||
| b7d3aa4b07 | |||
| 09f9819233 | |||
| cf2a44792f | |||
| c61c249405 | |||
| 3670e1d9db | |||
| aa582be321 | |||
| 4dff43a65e | |||
| 97345dbd59 | |||
| 6564a42f21 | |||
| f7679c50d8 | |||
| dbb7e34950 | |||
| 34f0d7026e | |||
| 59ecfd67f0 | |||
| 6acc2633a5 | |||
| 07649b3c16 | |||
| a2c268c923 | |||
| 0b98b88dfc | |||
| c2f64700c1 | |||
| f6ac2c5aa9 | |||
| ff08ab1023 | |||
| b30948fd81 | |||
| 008600f6d3 | |||
| 73686ae930 | |||
| 3223e6389c | |||
| b9d3b9364c | |||
| 7f71e9dd52 | |||
| 71497e592c | |||
| b24a13d962 | |||
| 4212be1b6c | |||
| 5f1121123c | |||
| 68e1504685 | |||
| ffea1e3917 | |||
| 8eab8af02d | |||
| 9d371ddb94 | |||
| 58d334c1c8 | |||
| 5507e9c00d | |||
| 2de7fb5c25 | |||
| 485d820fe2 | |||
| b1d08b3e30 | |||
| e86fec4ab6 | |||
| a4c4ea129c | |||
| 56e675ec9c | |||
| 61b7b2d5dc | |||
| 64562b6171 | |||
| ec7c76b8d2 | |||
| 1a35929eb8 | |||
| 151f3f6cc4 | |||
| dc6b9dda4d | |||
| f9bf1e9372 | |||
| e3689a1f12 | |||
| c65ab01647 | |||
| 63a76b273a | |||
| cc7ad543ee | |||
| 48e2e2c2db | |||
| f8fc9956d0 | |||
| 719cae0406 | |||
| 7a9b62fc40 | |||
| d804a327aa | |||
| 87caf0e2c4 | |||
| a08f8d890d | |||
| d9ddb12500 | |||
| f68db94c81 | |||
| b84345c083 | |||
| f06ca5a07c | |||
| 25026d1657 | |||
| 1782f5c463 | |||
| 66a5e67cbf | |||
| f1bf8e554e | |||
| d4a5bb788c | |||
| df1a8346e0 | |||
| ccce8756ed | |||
| 1e0a16a214 | |||
| d7e8199365 | |||
| cec580a645 | |||
| 8a9409005f | |||
| 8c7d38ea25 | |||
| fe4f460a3b | |||
| 47beb2e7e1 | |||
| 04ccdc4b30 | |||
| ad5b451182 | |||
| 11474102cd | |||
| 5f8702fc62 | |||
| ba7b3422c9 | |||
| 115017c8cd | |||
| 769da0ec15 | |||
| 151b169dda | |||
| 84b7de1405 | |||
| 3a7bf449ed | |||
| f2701e89bd | |||
| 1bb89e818e | |||
| 28af45c65a | |||
| d52d82f7e6 | |||
| ca0c9ab659 | |||
| de4d55e06a | |||
| 221d79283d | |||
| 1705e5a03e | |||
| 06c5b6c2fb | |||
| 9b0b154bd3 | |||
| 15778fc6db | |||
| 932ea86b45 | |||
| 4ea9355f5f | |||
| e8a98abcd1 | |||
| af5695d462 | |||
| bfc1c83c0b | |||
| e5f2bcad03 | |||
| e972a297e1 | |||
| 439a8543e2 | |||
| 02bf8e52af | |||
| c569781921 | |||
| 343261d6ae | |||
| 7c0698739b | |||
| c30608d7eb | |||
| e37894d011 | |||
| 43581b0b64 | |||
| 79195cac36 | |||
| 38917114f0 | |||
| ddb3798ab0 | |||
| a58047e88a | |||
| 794c2c495d | |||
| 284173ec3c | |||
| ae3925b9cd | |||
| 20f79c5ae5 | |||
| cb902dae76 | |||
| 2ce5e5530b | |||
| 70e7758c0c | |||
| d349a28d76 | |||
| 9efa707b64 | |||
| 15be845421 | |||
| d343431e75 | |||
| f1199852a2 | |||
| aa89e2c5e7 | |||
| 826b3912ce | |||
| 87f53ce1bf | |||
| b622ca45d2 | |||
| c9ac77ccbf | |||
| 8a98369a0e | |||
| c1ec676262 | |||
| 6327bac6c2 | |||
| cd5b185c12 | |||
| f93e1f4188 | |||
| 72f690ce99 | |||
| 90129c10d8 | |||
| e5804fb213 | |||
| 2aae67cb81 | |||
| efc31d6e58 | |||
| 0c9340e380 | |||
| c39c0b597c | |||
| 6d0ff399fb | |||
| d77e23df86 | |||
| 5022950c32 | |||
| 8d08d83ba1 | |||
| 2418fb311e | |||
| a83890b03e | |||
| 0f6a5480df | |||
| 4d767fb279 | |||
| 3621dc754a | |||
| f14c592148 | |||
| f5bee133a4 | |||
| fc2fd60cdb | |||
| edc3006fbe | |||
| e1a2fd2481 | |||
| de1e3cc975 | |||
| d88f817874 | |||
| 3e7fa1bde3 | |||
| a9e35afbd7 | |||
| f60dfaf14a | |||
| cefce69fda | |||
| 5ab68c9ebb | |||
| 5c62bce037 | |||
| 92fe61610f | |||
| ab41a469c7 | |||
| 75636d69ff | |||
| c20bf7aeb1 | |||
| 4c2d72d00b | |||
| 2e538b5d38 | |||
| 660e753ce7 | |||
| f4dec97178 | |||
| e9fced5c2c | |||
| 3dba01535b | |||
| e509aa8072 | |||
| e85bb2085c | |||
| aa94491ad7 | |||
| 7969795a01 | |||
| 62d32a1bdf | |||
| 4e9c0ea3a5 | |||
| 5c7d2723ed | |||
| 17e8fd5636 | |||
| 18ab2dd0d1 | |||
| 03e78536f0 | |||
| 7fb0e3900f | |||
| 9b483c390d | |||
| 385f05b21b | |||
| 7d310e56cc | |||
| 7899e4943b | |||
| 1848748118 | |||
| 04dbbab4a1 | |||
| 3ebec4db75 | |||
| ff3b0fe2d9 | |||
| d62f294494 | |||
| 46758a82a7 | |||
| 1292bbd954 | |||
| 234e0b247b | |||
| d9d0410797 | |||
| eabeae20b6 | |||
| 215303cc69 | |||
| 17db3361d3 | |||
| 91fb6d405d | |||
| d5634d5aa4 | |||
| dbeebd2efd | |||
| 0bcec5f10c | |||
| d285b2409b | |||
| 3030a80f37 | |||
| 47bf9f2429 | |||
| 827bd7c9a9 | |||
| 9dc236b685 | |||
| 0cddac16fb | |||
| c26d158bc7 | |||
| 17281314e7 | |||
| aa7d8cd39c | |||
| 6489311ab8 | |||
| 6b8e680510 | |||
| 52a522c5b8 | |||
| a0604c5594 | |||
| 1b12c3379f | |||
| 12610c994a | |||
| 666b9a354f | |||
| be8a3c8a61 | |||
| 5a6a38da29 | |||
| ce8682e0ee | |||
| 79c537eeb8 | |||
| a671960ba7 | |||
| 7ffebafb60 | |||
| dff3698432 | |||
| ca8959fc67 | |||
| 3d6b1feee0 | |||
| f9e2b5f9a2 | |||
| 3134f6a08f | |||
| d98011ccdc | |||
| 7201f8a86e | |||
| 770c39677d | |||
| 8bfd3bee99 | |||
| 1a29b6ac6e | |||
| b2531b588f | |||
| f7098efc36 | |||
| 491f569ca3 | |||
| 8d258d013a | |||
| 2fc23dc8fb | |||
| a22f6c28d6 | |||
| 4cdea65aa5 | |||
| a8a3c1555b | |||
| b9f1c21b5e | |||
| 4c5ca97e75 | |||
| 8c86a6e55e | |||
| c66c670ce3 | |||
| fede50ef48 | |||
| 648c60bee8 | |||
| dd8368a58b | |||
| 4a3be62dd1 | |||
| 06fec79ce7 | |||
| cf72307b95 | |||
| 48c7a4046c | |||
| 9952048550 | |||
| b6d19149d9 | |||
| 4107597a3a | |||
| 6092d9a4ad | |||
| e46d2615a6 | |||
| c91baa5179 | |||
| 0543491bab | |||
| cb13636622 | |||
| 51178d7dd7 | |||
| 359675b446 | |||
| 95387d58b0 | |||
| 6387a1591c | |||
| 93542f89fb | |||
| b49f2a7187 | |||
| 4732d7810e | |||
| b915198205 | |||
| b36d165fc3 | |||
| c6817a60cb | |||
| 5d9271ea86 | |||
| dfd5df4265 | |||
| f777ad8d8c | |||
| c085245747 | |||
| e04b6e9201 | |||
| b6121a5d64 | |||
| 2a48c002f9 | |||
| 446b3941c8 | |||
| 0134b928dc | |||
| 17b88c46b9 | |||
| d6765ab394 | |||
| 09c43a0f1d | |||
| 9e434a9136 | |||
| d80cd8d7aa | |||
| 2f80db329b | |||
| cfc8d28a59 | |||
| 6619cccd3e | |||
| b28cc7c548 | |||
| 12b192a15b | |||
| 29462e44dc | |||
| 535f5410ad | |||
| f25c5f7bfe | |||
| e6e9dc201b | |||
| 92bb0910c0 | |||
| 48aecae2d9 | |||
| 6e368fedeb | |||
| e1be829246 | |||
| 5f4335d560 | |||
| 86b4155605 | |||
| 6b0fc1bef6 | |||
| 51d2276259 | |||
| 36c398908a | |||
| 1a388698f4 | |||
| 63d69a4faa | |||
| 16b667040d | |||
| 96696c3983 | |||
| 1695bdd63c | |||
| edae1efabe | |||
| d1caea2a3a | |||
| 5c87c43007 | |||
| 998b3af598 | |||
| 647c2b351b | |||
| 9ea08a28d6 | |||
| 0d4a6f8e8d | |||
| 5521b4e2bb | |||
| 5f4c3841a0 | |||
| 2ccb55f675 | |||
| 2a28b53400 | |||
| 7884b7de44 | |||
| d96ed0d241 | |||
| b3dcb1596a | |||
| 93539f078e | |||
| 32ea2638c4 | |||
| d367b0902d | |||
| 5edea056ef | |||
| 73658c3604 | |||
| e6ceea6bfc | |||
| df14515927 | |||
| 8dd754c1fa | |||
| 8aa13b4910 | |||
| b0f7846832 | |||
| e94373f264 | |||
| 8d85f1d76f | |||
| 955319fc23 | |||
| dad5f9511c | |||
| 3c3a65cc70 | |||
| 3e7a9943a7 | |||
| 5aed7a7b89 | |||
| b0bcb2132c | |||
| 9468dad455 | |||
| 9ea48a12dd | |||
| 737ea42698 | |||
| 27db7eee26 | |||
| f45925fb54 | |||
| 3c15ec6e49 | |||
| b6ec635c52 | |||
| 9694a0c296 | |||
| 63e21abb60 | |||
| 1651f54208 | |||
| c80d0b73c2 | |||
| 2015a1c69f | |||
| 36e312193f | |||
| bb5e765cfe | |||
| 61e3c5d730 | |||
| dfcc968c3a | |||
| 19f2579640 | |||
| ed8b3e1bba | |||
| c9dbb83fba | |||
| 25cd88fb01 | |||
| ecc4a034ca | |||
| c46096ade2 | |||
| 35f4f40b3e | |||
| 66c080559d | |||
| 9dc71ad9fa | |||
| 28d22d2332 | |||
| 6590a8b769 | |||
| a7ae420645 | |||
| 0f2ae9bde6 | |||
| afae88f6cc | |||
| 606bc5523d | |||
| 85e6b0b018 | |||
| aab3ad6bc0 | |||
| 71158ca331 | |||
| 7d172a6427 | |||
| c3d17d1fd5 | |||
| 1b26fea1e0 | |||
| ca2bb144b9 | |||
| d06a9539da | |||
| 50c7308980 | |||
| 868e1fe22d | |||
| b5cd717a8b | |||
| f96fe33747 | |||
| 24c8738ad7 | |||
| a5dba00155 | |||
| fcbdf3e87b | |||
| 51bbe4d2bb | |||
| 3304801e9a | |||
| b2169ff7d7 | |||
| 026b4df014 | |||
| 86a622c49c | |||
| f0599a34c7 | |||
| 4189c0b2dc | |||
| eb1d383c05 | |||
| be1fc22a05 | |||
| 15bd2132c2 | |||
| f9174327d8 | |||
| 134b4b4273 | |||
| 0bbab2bd59 | |||
| affbcea601 | |||
| 85456f401a | |||
| 4e0e87aa49 | |||
| cdf3481de9 | |||
| 7418e6acbf | |||
| 89e750a6aa | |||
| 7a573bf809 | |||
| 2fd50c5401 | |||
| 949ea7b8df | |||
| c9b5d64c83 | |||
| c450e8374b | |||
| de0f768e7f | |||
| 41eb626338 | |||
| 3e456dbcb9 | |||
| 8086ab45ea | |||
| 60d9d92060 | |||
| 00fd140ff6 | |||
| f5aabb414a | |||
| 701215381a | |||
| 966968db7b | |||
| e3adbb469f | |||
| b67ac293fd | |||
| 1a7b934af9 | |||
| 372f7b4198 | |||
| 3f922f2333 | |||
| d556d3f575 | |||
| de3862950e | |||
| d6c3f0b5e0 | |||
| ed14a0342d | |||
| 79ff1c9902 | |||
| 42cb36481c | |||
| 08dae545a2 | |||
| a716f2be36 | |||
| 3a548a0383 | |||
| a9a47e6f55 | |||
| 3076747d3e | |||
| c977983a1f | |||
| 80099b3ff3 | |||
| 0e62acc2a6 | |||
| 41d825a4db | |||
| d3784d423b | |||
| 8c515d3751 | |||
| 28564475ea | |||
| ce59100bc6 | |||
| 8d7b75c329 | |||
| 1da7ba6446 | |||
| db09c071d8 | |||
| 8c0c87b2c9 | |||
| 1cb648c103 | |||
| aa56a7362a | |||
| 7d1bc28103 | |||
| 7a88485999 | |||
| 3c76f0f917 | |||
| 68abf86e2c | |||
| 16b9732b82 | |||
| 18661709d0 | |||
| 59c9f28078 | |||
| d26510a1e9 | |||
| ab27005dfc | |||
| b0e2f948a9 | |||
| 42f38b042f | |||
| e672041a79 | |||
| 9a04220129 | |||
| fa785b745b | |||
| 527e742a89 | |||
| 54683b2d21 | |||
| d33293ba6b | |||
| 64c5796d1a | |||
| f9699e19af | |||
| e5cc336011 | |||
| c215b9fd5f | |||
| 46cb49865a | |||
| e4c7d62012 | |||
| 85167d34e9 | |||
| 0e7a20fe9f | |||
| f9f3778a4a | |||
| 49be0d2679 | |||
| f7f3539f48 | |||
| 2a3c260dd6 | |||
| 1ac081d695 | |||
| e537279692 | |||
| a111727302 | |||
| 5c900c2b54 | |||
| dc3d4ad873 | |||
| 76e597f57f | |||
| 4d1b25ce78 | |||
| 4ef57e5ffd | |||
| 0368722c4d | |||
| baecfd6775 | |||
| ccf6428633 | |||
| e73fe08212 | |||
| 2c85060c49 | |||
| 0729bd7f83 | |||
| 27e1867346 | |||
| ecae9c7794 | |||
| 594dc126e7 | |||
| fafe2f96c1 | |||
| 4c2e73a3ae | |||
| eff86fc84d | |||
| 55624b3410 | |||
| e022c6e0f9 | |||
| 16442e2013 | |||
| 38832396bc | |||
| b60f6b0705 | |||
| f822d23509 | |||
| 55d43e7cad | |||
| af28ef7c94 | |||
| 81b095e73d | |||
| 49253a9586 | |||
| 374b877323 | |||
| 2b5732f586 | |||
| 38d8aa2a33 | |||
| ead60ebfcd | |||
| d2933428a4 | |||
| ec683d7488 | |||
| 0d88925f83 | |||
| f5319724e3 | |||
| d37216815d | |||
| 4982814d36 | |||
| e84e8a7cf6 | |||
| 480dcd8a7a | |||
| 6bda4dd0d8 | |||
| 18114458b8 | |||
| 7e5b85c4b3 | |||
| 4199bba706 | |||
| d7fa4048c1 | |||
| 20408aa531 | |||
| 518993cb4c | |||
| 8a017226c3 | |||
| 31f3d6810d | |||
| ac5b9a3e78 | |||
| cde0a11c74 | |||
| b0ddca72ae | |||
| 7dbeb56111 | |||
| df347ad0f4 | |||
| 4c5cf47bc9 | |||
| 53cb0e79cb | |||
| 9316cec95d | |||
| 86398de8bd | |||
| d966dc2343 | |||
| fd4c42a156 | |||
| 5b14d62ede | |||
| 1fa68effdd | |||
| 4fc45c590e | |||
| 8812b892c4 | |||
| e60db7cfb1 | |||
| 8c97e5f942 | |||
| 057e21905e | |||
| ca5e18391b | |||
| 802917de12 | |||
| 8d30a03f50 | |||
| 94b5a20c0a | |||
| 7c06e847f9 | |||
| 7a3f6a6994 | |||
| c39bc10d2f | |||
| 28acb19032 | |||
| d94abec352 | |||
| bf0ba68092 | |||
| df1410d84d | |||
| 1d0c41b295 | |||
| 7064de6eef | |||
| 0e36d45233 | |||
| fdfe1f1cb5 | |||
| 1e5a93249c | |||
| 808a9e6fb9 | |||
| ca4677686a | |||
| 7bf2318b80 | |||
| 194810cd56 | |||
| 5426bf1761 | |||
| f34995cfd3 | |||
| 795b73325a | |||
| cb28a50017 | |||
| f447423e2e | |||
| 1b3e275cf2 | |||
| a86529c22c | |||
| 47b2b604e4 | |||
| ff35468dac | |||
| 0c5c5d3c42 | |||
| 74f204e8e8 | |||
| 375a8cea12 | |||
| 156942f8fb | |||
| 4cd0e2c9d7 | |||
| ae851b8733 | |||
| 67818a2c28 | |||
| 7c675a378c | |||
| ff3f434acc | |||
| 5146c05ec3 | |||
| b97322c198 | |||
| d1abbae1b3 | |||
| ebc2190779 | |||
| 3fdfd76f6e | |||
| c7459af4ec | |||
| 61e0e39641 | |||
| da3b62341f | |||
| a5db6cd363 | |||
| 786b0e77b8 | |||
| 6dce4e360f | |||
| f40064333e | |||
| 160075c22b | |||
| ce0f10f764 | |||
| a7def1ee87 | |||
| 6bdbf85279 | |||
| c7dd173858 | |||
| 9541e983d2 | |||
| 1ea7d979a8 | |||
| fa190a74da | |||
| 1696e4495a | |||
| 8b3498aaf1 | |||
| 5865891797 | |||
| 4c655a6b9d | |||
| d0448569b7 | |||
| 03d7736b99 | |||
| 70604b6e8b | |||
| 2f6e8bbef9 | |||
| e2ed9baacb | |||
| b45896b294 | |||
| 3ea8d14521 | |||
| 9fb25bfada | |||
| 481de1e3e3 | |||
| 2781e6c304 | |||
| a05d85673a | |||
| 2c68df9696 | |||
| 97a1d5c592 | |||
| 7134363905 | |||
| e6369eaae1 | |||
| 8585a00051 | |||
| 1f072631e5 | |||
| 8343178219 | |||
| b28bb112fe | |||
| 68f65ea000 | |||
| 49b27d6b0d | |||
| 5a425dc5be | |||
| 18acef841b | |||
| 6022c79cbb | |||
| a86c84b1a1 | |||
| 70bddf76fa | |||
| 82c6e50753 | |||
| d3f2270f22 | |||
| fb90685a7c | |||
| bbd865ddec | |||
| 895c988ba6 | |||
| e15d380dd1 | |||
| 1f6806d68b | |||
| dde6ee67a2 | |||
| dbfc556cb9 | |||
| 6e412f43b2 | |||
| d6d4dc9826 | |||
| 96230fb4d0 | |||
| cd065beb0c | |||
| 5f8a9dddff | |||
| 659640b851 | |||
| e8566002d6 | |||
| 4f96812b90 | |||
| 20b5de755d | |||
| b8a17d4266 | |||
| 5d9523466b | |||
| 4a1a21bc4f | |||
| 5eeb9e561d | |||
| 4ffddae162 | |||
| 309e3f804d | |||
| 0d853c9393 | |||
| 6d145be8d2 | |||
| 81820a220d | |||
| 513d4f962f | |||
| 9d09a637fe | |||
| b958e0a275 | |||
| 8952277ae9 | |||
| 4a482540cb | |||
| f9ba0092bc | |||
| b0fa6170e5 | |||
| 83668b3b93 | |||
| e5683489bb | |||
| f82a6f7f98 | |||
| 3502287832 | |||
| 38a3f00de2 | |||
| c5d4b3affc | |||
| 36da011736 | |||
| 58e9d933c3 | |||
| 38e586c8e2 | |||
| 9027e3b7ee | |||
| bac03ac47b | |||
| e6c01c4434 | |||
| bb7c4fbb21 | |||
| e6ad773c89 | |||
| e3d7791a9c | |||
| 9f65e14adb | |||
| a05d7aaf91 | |||
| 350a5dd7bc | |||
| be92c59b1b | |||
| 29028d60cd | |||
| dd424ef25d | |||
| 64975f2dd5 | |||
| 13d4a2f8ca | |||
| b498a41f58 | |||
| 80ba8a93b0 | |||
| adcae206b2 | |||
| 20e6e771d7 | |||
| 813b12baeb | |||
| 1e6912f560 | |||
| e26e73b6f2 | |||
| bb3f54efcb | |||
| 65c7d0bc39 | |||
| 89848a644d | |||
| b370b859aa | |||
| 73c1adea5f | |||
| 6d66194c38 | |||
| 5396d78f9a | |||
| 1ba2e78f4d | |||
| d8a3df644b | |||
| 05d3c2dc4a | |||
| f4515558c2 | |||
| 5726026289 | |||
| c8c327566b | |||
| 02667395ee | |||
| e3465ba688 | |||
| 29ca797b3b | |||
| 3ac4eefe1d | |||
| 34c35ece99 | |||
| a5f76235eb | |||
| a8f3a04b43 | |||
| 9f36721631 | |||
| 3959c4fac6 | |||
| 91ef6f814a | |||
| a2af6d8d46 | |||
| 2f9853799d | |||
| ae725fa0db | |||
| 75fe14ad28 | |||
| 490c07bfe0 | |||
| 6e04ad19d4 | |||
| 19e965ce45 | |||
| 58cd2b2a2e | |||
| ef7efd7796 | |||
| 607226d196 | |||
| 3366ddf0eb | |||
| db143b875b | |||
| f2dc58d4a8 | |||
| e9ea9832bd | |||
| 580c688b2b | |||
| 8638d26156 | |||
| e4a16e7e83 | |||
| 4e2114e2fe | |||
| 8bf30e67a9 | |||
| 4f8e230efb | |||
| 71a4d7d310 | |||
| 1002e8e6ad | |||
| cad9790e0b | |||
| 3aa5ad7204 | |||
| db2ccb7c68 | |||
| 60e114469a | |||
| 9f43dacf25 | |||
| a36a30ce01 | |||
| c294582ffe | |||
| ff3e346433 | |||
| 0dc2970e90 | |||
| 43e6431b4e | |||
| 0a98ee7a99 | |||
| 802373f108 | |||
| a7a0378cfb | |||
| 6ca4de8457 | |||
| 6a8707cc09 | |||
| 0b8877cc5b | |||
| 7472f612e6 | |||
| 992aaca272 | |||
| 093eb28e75 | |||
| 57754be6f0 | |||
| f13c6109db | |||
| f30e91c946 | |||
| 784e8f2799 | |||
| 13453351bf | |||
| 77f31ca4b5 | |||
| 7f70af58ee | |||
| c8d5817942 | |||
| 4a7148e20a | |||
| a96e00e44c | |||
| 89ddff7f1a | |||
| ebc22c5e3b | |||
| 926bb91fac | |||
| fb670b7cdf | |||
| b1d9dba775 | |||
| 6499665d98 | |||
| 3d61da74ed | |||
| 23d61dd1c0 | |||
| c695545f97 | |||
| a3465ea80c | |||
| 3ed0a093b0 | |||
| dc4e5185ed |
@@ -0,0 +1,559 @@
|
||||
# Phase Prompts
|
||||
|
||||
Use these templates as Codex subagent messages. Use them as same-session checklists only for Phase 0, intentional main-session build work, Phase 7, or when delegation is unavailable from the start. Replace `<TASK>`, `<PROJECT>`, `<LETTER>`, and `<REPO_ROOT>`.
|
||||
|
||||
## Orchestration Rules
|
||||
|
||||
- Phase 0 runs in the main session.
|
||||
- When delegation is available, use a fresh subagent for Phase 1, Phase 2, Phase 3, each Phase 4 implementation unit, and each Phase 6 pass. Do not switch those phases to same-session midstream because of a timeout or missing artifact.
|
||||
- Phase 7 runs in the main session on Windows because it depends on the final local diff and touched-file set.
|
||||
- Write each phase prompt to `.ai/<PROJECT>/<LETTER>/logs/phase-<name>.prompt.md` before execution.
|
||||
- If you delegate a phase, send the prompt file contents as the initial `spawn_agent` message.
|
||||
- When writing the phase prompt file, append the standard progress file contract and the standard compact reply block below so the subagent knows how to surface progress before the final artifact.
|
||||
- After each phase completes, write `.ai/<PROJECT>/<LETTER>/logs/phase-<name>.result.md` summarizing the status, files touched, and any follow-up notes.
|
||||
- Use `fork_context: false` by default. If the phase depends on thread-only context or UI attachments, pass that context explicitly or enable `fork_context` only for that phase.
|
||||
- Prefer `worker` for phases that write files. Use `default` for plan or review passes if that fits the host better. Use `explorer` only for narrow read-only questions.
|
||||
- When supported, request `model: gpt-5.4` and `reasoning_effort: xhigh` for delegated phases.
|
||||
- Default wait budget for delegated phases is 5 minutes while the phase is clearly still in progress. Successful completion may wake earlier, so this does not delay finished work.
|
||||
- When a phase appears close to landing, use 1-2 minute waits until it finishes.
|
||||
- A `wait_agent` timeout is not failure. On timeout, inspect both the expected artifact and the matching progress file before deciding anything.
|
||||
- If the expected artifact exists and shows progress, wait again.
|
||||
- If the expected artifact is not ready but the progress file mtime moved or its heartbeat counter increased since the previous check, wait again. Prefer mtime checks first and avoid rereading the file unless you need detail. Do not count that as a failed wait.
|
||||
- If neither the expected artifact nor the progress file moved since the previous blocked check, send one short follow-up asking the same agent to refresh the progress file, finish the required artifact, and return the standard compact reply block, then wait again.
|
||||
- If the same agent still produces no usable artifact and no meaningful progress-file movement after two full default waits and one follow-up, close it and retry the phase in a fresh subagent.
|
||||
- For Phase 1, Phase 2, Phase 3, Phase 4, and Phase 6, if delegated retries still fail, stop and ask the user rather than rerunning the phase locally.
|
||||
- Never use `codex exec`, background shell child processes, or JSONL child-session logging from this skill.
|
||||
|
||||
## Standard Progress File Contract
|
||||
|
||||
Append this verbatim to every delegated phase prompt:
|
||||
|
||||
```text
|
||||
Before deep work, create or update the matching progress file in `.ai/<PROJECT>/<LETTER>/logs/`.
|
||||
|
||||
Use `<phase-name>.progress.md` as a concise heartbeat with:
|
||||
- `Heartbeat: <N>` on the first line, incremented on each meaningful update
|
||||
- Current step
|
||||
- Files being read or edited
|
||||
- Concrete findings or decisions so far
|
||||
- Blocker or next checkpoint
|
||||
|
||||
Update it sparingly: preferably at natural milestones, and otherwise only after a longer quiet stretch such as roughly 5-10 minutes.
|
||||
Keep it tiny so the parent can usually rely on file mtime or the heartbeat counter instead of rereading the whole file.
|
||||
Do not wait until the final artifact to write progress.
|
||||
```
|
||||
|
||||
## Standard Compact Reply Block
|
||||
|
||||
Append this verbatim to every delegated phase prompt:
|
||||
|
||||
```text
|
||||
Before replying in chat, write the required artifact(s) to disk.
|
||||
|
||||
Reply in 8 lines or fewer using exactly these keys:
|
||||
STATUS: <DONE|BLOCKED|APPROVED|NEEDS_CHANGES>
|
||||
ARTIFACTS: <paths>
|
||||
TOUCHED: <repo paths or none>
|
||||
BLOCKER: <none or one short line>
|
||||
|
||||
Do not restate the full context, plan, diff, or long reasoning in the chat reply.
|
||||
```
|
||||
|
||||
## Artifact-Based Completion Checks
|
||||
|
||||
- Phase 1 is complete only when `about.md` and `context.md` both exist and are non-empty.
|
||||
- Phase 2 is complete only when `plan.md` exists, contains a `## Status` section, and no unintended source edits were made.
|
||||
- Phase 3 is complete only when `plan.md` contains both `Phases:` in the Status section and `Assessed: yes`.
|
||||
- Phase 4 is complete only when the target phase checkbox changed to checked and the touched-file list matches the owned write set, or the blocker explains any mismatch.
|
||||
- Phase 5 is complete only when the build outcome is known and the build checkbox is updated on success.
|
||||
- Phase 6a is complete only when `review<R>.md` exists and contains a verdict line.
|
||||
- Phase 6b is complete only when the requested fixes were applied and the post-fix build outcome is known.
|
||||
|
||||
## Phase 0: Setup
|
||||
|
||||
Record the current time now and store it as `$START_TIME`. You will use this at the end to display total elapsed time.
|
||||
|
||||
Before running any phase prompts, determine whether this is a new project or a follow-up task.
|
||||
|
||||
Follow-up detection:
|
||||
1. Extract the first word or token from the task description. Call it `FIRST_TOKEN`.
|
||||
2. Check `.ai/` to see existing project names.
|
||||
3. Check whether `.ai/<FIRST_TOKEN>/about.md` exists.
|
||||
4. If the file exists, this is a follow-up task. The project name is `FIRST_TOKEN`. The task description is everything after `FIRST_TOKEN`.
|
||||
5. If the file does not exist, this is a new project. The full input is the task description.
|
||||
|
||||
Do not proceed until you have determined follow-up vs new.
|
||||
|
||||
For new projects:
|
||||
- Using the list of existing projects, pick a unique short name (1-2 lowercase words, hyphen-separated) that does not collide.
|
||||
- Create `.ai/<PROJECT>/`, `.ai/<PROJECT>/a/`, and `.ai/<PROJECT>/a/logs/`.
|
||||
- Set `<LETTER>` = `a`.
|
||||
|
||||
For follow-up tasks:
|
||||
- Scan `.ai/<PROJECT>/` for existing task folders (`a/`, `b/`, ...). Find the latest one (highest letter).
|
||||
- The previous task letter = that highest letter.
|
||||
- The new task letter = next letter in sequence.
|
||||
- Create `.ai/<PROJECT>/<LETTER>/` and `.ai/<PROJECT>/<LETTER>/logs/`.
|
||||
|
||||
Then proceed to Phase 1. Follow-up tasks do not skip context gathering. They use a modified Phase 1F prompt.
|
||||
|
||||
## Phase 1: Context (New Project, letter = `a`)
|
||||
|
||||
```text
|
||||
You are a context-gathering agent for a large C++ codebase (Telegram Desktop).
|
||||
|
||||
TASK: <TASK>
|
||||
|
||||
YOUR JOB: Read AGENTS.md, inspect the codebase, find all files and code relevant to this task, and write two documents.
|
||||
|
||||
Steps:
|
||||
1. Read AGENTS.md for project conventions and build instructions.
|
||||
2. Search the codebase for files, classes, functions, and patterns related to the task.
|
||||
3. Read all potentially relevant files. Be thorough and prefer reading more rather than less.
|
||||
4. For each relevant file, note:
|
||||
- file path
|
||||
- relevant line ranges
|
||||
- what the code does and how it relates to the task
|
||||
- key data structures, function signatures, and patterns used
|
||||
5. Look for similar existing features that could serve as a reference implementation.
|
||||
6. Check api.tl if the task involves Telegram API.
|
||||
7. Check .style files if the task involves UI.
|
||||
8. Check lang.strings if the task involves user-visible text.
|
||||
|
||||
Write two files.
|
||||
|
||||
File 1: .ai/<PROJECT>/about.md
|
||||
|
||||
This file is not used by any agent in the current task. It exists solely as a starting point for a future follow-up task's context gatherer. No planning, implementation, or review phase should rely on it during the current task.
|
||||
|
||||
Write it as if the project is already fully implemented and working. It should contain:
|
||||
- Project: What this project does (feature description, goals, scope)
|
||||
- Architecture: High-level architectural decisions, which modules are involved, how they interact
|
||||
- Key Design Decisions: Important choices made about the approach
|
||||
- Relevant Codebase Areas: Which parts of the codebase this project touches, key types and APIs involved
|
||||
|
||||
Do not include temporal state like "Current State", "Pending Changes", "Not yet implemented", or "TODO". Describe the project as a complete, coherent whole.
|
||||
|
||||
File 2: .ai/<PROJECT>/a/context.md
|
||||
|
||||
This is the primary task-specific implementation context. All downstream phases should be able to work from this file plus the referenced source files. It must be self-contained. Include:
|
||||
- Task Description: The full task restated clearly
|
||||
- Relevant Files: Every file path with line ranges and descriptions
|
||||
- Key Code Patterns: How similar things are done in the codebase, with snippets when useful
|
||||
- Data Structures: Relevant types, structs, classes
|
||||
- API Methods: Any TL schema methods involved, copied from api.tl when useful
|
||||
- UI Styles: Any relevant style definitions
|
||||
- Localization: Any relevant string keys
|
||||
- Build Info: Build command and any special notes
|
||||
- Reference Implementations: Similar features that can serve as templates
|
||||
|
||||
Be extremely thorough. Another agent with no prior context will rely on this file.
|
||||
|
||||
Do not implement code in this phase.
|
||||
```
|
||||
|
||||
## Phase 1F: Context (Follow-up Task, letter = `b`, `c`, ...)
|
||||
|
||||
```text
|
||||
You are a context-gathering agent for a follow-up task on an existing project in a large C++ codebase (Telegram Desktop).
|
||||
|
||||
NEW TASK: <TASK>
|
||||
|
||||
YOUR JOB: Read the existing project state, gather any additional context needed, and produce fresh documents for the new task.
|
||||
|
||||
Steps:
|
||||
1. Read AGENTS.md for project conventions and build instructions.
|
||||
2. Read .ai/<PROJECT>/about.md. This is the project-level blueprint describing everything done so far.
|
||||
3. Read .ai/<PROJECT>/<PREV_LETTER>/context.md. This is the previous task's gathered context.
|
||||
4. Understand what has already been implemented by reading the actual source files referenced in about.md and the previous context.
|
||||
5. Based on the new task description, search the codebase for any additional files, classes, functions, and patterns that are relevant to the new task but not already covered.
|
||||
6. Read all newly relevant files thoroughly.
|
||||
|
||||
Write two files.
|
||||
|
||||
File 1: .ai/<PROJECT>/about.md (rewrite)
|
||||
|
||||
Rewrite this file instead of appending to it. The new about.md must be a single coherent document that describes the project as if everything, including this new task's changes, is already fully implemented and working.
|
||||
|
||||
It should incorporate:
|
||||
- everything from the old about.md that is still accurate and relevant
|
||||
- the new task's functionality described as part of the project, not as a pending change
|
||||
- any changed design decisions or architectural updates from the new task requirements
|
||||
|
||||
It should not contain:
|
||||
- temporal state such as "Current State", "Pending Changes", or "TODO"
|
||||
- history of how requirements changed between tasks
|
||||
- references to "the old approach" versus "the new approach"
|
||||
- task-by-task changelog or timeline
|
||||
- information that contradicts the new task requirements
|
||||
|
||||
File 2: .ai/<PROJECT>/<LETTER>/context.md
|
||||
|
||||
This is the primary document for the new task. It must be self-contained and should include:
|
||||
- Task Description: The new task restated clearly, with enough project background that an implementation agent can understand it without reading any other .ai files
|
||||
- Relevant Files: Every file path with line ranges relevant to this task
|
||||
- Key Code Patterns: How similar things are done in the codebase
|
||||
- Data Structures: Relevant types, structs, classes
|
||||
- API Methods: Any TL schema methods involved
|
||||
- UI Styles: Any relevant style definitions
|
||||
- Localization: Any relevant string keys
|
||||
- Build Info: Build command and any special notes
|
||||
- Reference Implementations: Similar features that can serve as templates
|
||||
|
||||
Be extremely thorough. Another agent with no prior context should be able to work from this file alone.
|
||||
|
||||
Do not implement code in this phase.
|
||||
```
|
||||
|
||||
## Phase 2: Plan
|
||||
|
||||
```text
|
||||
You are a planning agent. You must create a detailed implementation plan.
|
||||
|
||||
Read these files:
|
||||
- .ai/<PROJECT>/<LETTER>/context.md
|
||||
- Then read the specific source files referenced in context.md to understand the code deeply.
|
||||
|
||||
Create a detailed plan in: .ai/<PROJECT>/<LETTER>/plan.md
|
||||
|
||||
The plan.md should contain:
|
||||
|
||||
## Task
|
||||
<one-line summary>
|
||||
|
||||
## Approach
|
||||
<high-level description of the implementation approach>
|
||||
|
||||
## Files to Modify
|
||||
<list of files that will be created or modified>
|
||||
|
||||
## Files to Create
|
||||
<list of new files, if any>
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
Each step must be specific enough that an agent can execute it without ambiguity:
|
||||
- exact file paths
|
||||
- exact function names
|
||||
- what code to add, modify, or remove
|
||||
- where exactly in the file (after which function, in which class, and so on)
|
||||
|
||||
Number every step. Group steps into phases if there are more than about eight steps.
|
||||
|
||||
### Phase 1: <name>
|
||||
1. <specific step>
|
||||
2. <specific step>
|
||||
|
||||
### Phase 2: <name> (if needed)
|
||||
1. <specific step>
|
||||
|
||||
## Build Verification
|
||||
- build command to run
|
||||
- expected outcome
|
||||
|
||||
## Status
|
||||
- [ ] Phase 1: <name>
|
||||
- [ ] Phase 2: <name> (if applicable)
|
||||
- [ ] Build verification
|
||||
- [ ] Code review
|
||||
|
||||
Do not implement code in this phase.
|
||||
```
|
||||
|
||||
## Phase 3: Plan Assessment
|
||||
|
||||
```text
|
||||
You are a plan assessment agent. Review and refine an implementation plan.
|
||||
|
||||
Read these files:
|
||||
- .ai/<PROJECT>/<LETTER>/context.md
|
||||
- .ai/<PROJECT>/<LETTER>/plan.md
|
||||
- Then read the actual source files referenced to verify the plan makes sense.
|
||||
|
||||
Assess the plan:
|
||||
|
||||
1. Correctness: Are the file paths and line references accurate? Does the plan reference real functions and types?
|
||||
2. Completeness: Are there missing steps? Edge cases not handled?
|
||||
3. Code quality: Will the plan minimize code duplication? Does it follow existing codebase patterns from AGENTS.md?
|
||||
4. Design: Could the approach be improved? Are there better patterns already used in the codebase?
|
||||
5. Phase sizing: Each phase should be implementable by a single agent in one session. If a phase has more than about 8-10 substantive code changes, split it further.
|
||||
|
||||
Update plan.md with your refinements. Keep the same structure but:
|
||||
- fix any inaccuracies
|
||||
- add missing steps
|
||||
- improve the approach if you found better patterns
|
||||
- ensure phases are properly sized for single-agent execution
|
||||
- add a line at the top of the Status section: `Phases: <N>`
|
||||
- add `Assessed: yes` at the bottom of the file
|
||||
|
||||
If the plan is small enough for a single agent (roughly 8 steps or fewer), mark it as a single phase.
|
||||
|
||||
Do not implement code in this phase.
|
||||
```
|
||||
|
||||
## Phase 4: Implementation
|
||||
|
||||
Run one implementation unit per plan phase. Keep implementation phases sequential by default. Parallelize only if their write sets are disjoint and the plan makes that safe.
|
||||
|
||||
For each phase in the plan that is not yet marked as done, use this prompt:
|
||||
|
||||
```text
|
||||
You are an implementation agent working on phase <N> of an implementation plan.
|
||||
|
||||
Read these files first:
|
||||
- .ai/<PROJECT>/<LETTER>/context.md
|
||||
- .ai/<PROJECT>/<LETTER>/plan.md
|
||||
|
||||
Then read the source files you will be modifying.
|
||||
|
||||
Your owned write set for this phase:
|
||||
<OWNED_WRITE_SET>
|
||||
|
||||
YOUR TASK: Implement only Phase <N> from the plan:
|
||||
<paste the specific phase steps here>
|
||||
|
||||
Rules:
|
||||
- Follow the plan precisely.
|
||||
- Follow AGENTS.md coding conventions.
|
||||
- You are not alone in the codebase. Respect existing changes and do not revert unrelated work.
|
||||
- Do not modify .ai/ files except to update the Status section in plan.md.
|
||||
- When done, update plan.md Status section: change `- [ ] Phase <N>: ...` to `- [x] Phase <N>: ...`
|
||||
- Do not work on other phases.
|
||||
|
||||
When finished, report what you did, which files you changed, and any issues encountered.
|
||||
```
|
||||
|
||||
After each implementation phase:
|
||||
1. Use a narrow read or search to confirm the status line was updated.
|
||||
2. Verify the owned write set and touched files with a small diff summary such as `git diff --name-only`.
|
||||
3. If more phases remain, run the next implementation phase.
|
||||
4. If all phases are done, proceed to build verification.
|
||||
|
||||
## Phase 5: Build Verification
|
||||
|
||||
Only run this phase if the task modified project source code.
|
||||
|
||||
Prefer running the build in the main session because it is critical-path work. If you delegate it, use a worker subagent and wait immediately for the result.
|
||||
|
||||
```text
|
||||
You are a build verification agent.
|
||||
|
||||
Read these files:
|
||||
- .ai/<PROJECT>/<LETTER>/context.md
|
||||
- .ai/<PROJECT>/<LETTER>/plan.md
|
||||
|
||||
The implementation is complete. Your job is to build the project and fix any build errors that block the planned work.
|
||||
|
||||
Steps:
|
||||
1. Run (from repository root): cmake --build ./out --config Debug --target Telegram
|
||||
2. If the build succeeds, update plan.md: change `- [ ] Build verification` to `- [x] Build verification`
|
||||
3. If the build fails:
|
||||
a. Read the error messages carefully
|
||||
b. Read the relevant source files
|
||||
c. Fix the errors in accordance with the plan and AGENTS.md conventions
|
||||
d. Rebuild and repeat until the build passes
|
||||
e. Update plan.md status when done
|
||||
|
||||
Rules:
|
||||
- Only fix build errors. Do not refactor or improve code beyond what is needed for a passing build.
|
||||
- Follow AGENTS.md conventions.
|
||||
- If build fails with file-locked errors (C1041, LNK1104, "cannot open output file", or similar access-denied lock issues), stop and report the lock. Do not retry.
|
||||
- You are not alone in the codebase. Respect existing changes and do not revert unrelated work.
|
||||
|
||||
When finished, report the build result and which files, if any, you changed.
|
||||
```
|
||||
|
||||
## Phase 6: Code Review Loop
|
||||
|
||||
After build verification passes, run up to 3 review-fix iterations. Set iteration counter `R = 1`.
|
||||
|
||||
Review loop:
|
||||
|
||||
```text
|
||||
LOOP:
|
||||
1. Run review phase 6a with iteration R.
|
||||
2. Read review<R>.md verdict:
|
||||
- "APPROVED" -> go to FINISH
|
||||
- "NEEDS_CHANGES" -> run fix phase 6b
|
||||
3. After fix work completes and build passes:
|
||||
R = R + 1
|
||||
If R > 3 -> go to FINISH
|
||||
Otherwise -> go to step 1
|
||||
|
||||
FINISH:
|
||||
- Update plan.md: change `- [ ] Code review` to `- [x] Code review`
|
||||
- Proceed to Phase 7 on Windows, otherwise proceed to Completion
|
||||
```
|
||||
|
||||
### Step 6a: Code Review
|
||||
|
||||
```text
|
||||
You are a code review agent for Telegram Desktop (C++ / Qt).
|
||||
|
||||
Read these files:
|
||||
- .ai/<PROJECT>/<LETTER>/context.md
|
||||
- .ai/<PROJECT>/<LETTER>/plan.md
|
||||
- REVIEW.md
|
||||
- If R > 1, also read .ai/<PROJECT>/<LETTER>/review<R-1>.md
|
||||
|
||||
Then run `git diff` to see the current uncommitted changes for this task.
|
||||
|
||||
Read the modified source files in full to understand the changes in context.
|
||||
|
||||
Perform a focused code review using these criteria, in order:
|
||||
|
||||
1. Correctness and safety: Obvious logic errors, missing null checks at API boundaries, potential crashes, use-after-free, dangling references, race conditions.
|
||||
2. Dead code: Added or left-behind code that is never used within the scope of the changes.
|
||||
3. Redundant changes: Diff hunks that have no functional effect.
|
||||
4. Code duplication: Repeated logic that should be shared.
|
||||
5. Wrong placement: Code added to a module where it does not logically belong.
|
||||
6. Function decomposition: Whether an extracted helper would clearly improve readability.
|
||||
7. Module structure: Only in exceptional cases where a large new chunk of code clearly belongs elsewhere.
|
||||
8. Style compliance: REVIEW.md rules and AGENTS.md conventions.
|
||||
|
||||
Important guidelines:
|
||||
- Review only the changes made, not pre-existing code outside the scope of the task.
|
||||
- Be pragmatic. Each suggestion should have a clear, concrete benefit.
|
||||
- Do not suggest comments, docstrings, or over-engineering.
|
||||
|
||||
Write your review to: .ai/<PROJECT>/<LETTER>/review<R>.md
|
||||
|
||||
The review document should contain:
|
||||
|
||||
## Code Review - Iteration <R>
|
||||
|
||||
## Summary
|
||||
<1-2 sentence overall assessment>
|
||||
|
||||
## Verdict: <APPROVED or NEEDS_CHANGES>
|
||||
|
||||
If the verdict is NEEDS_CHANGES, continue with:
|
||||
|
||||
## Changes Required
|
||||
|
||||
### <Issue 1 title>
|
||||
- Category: <dead code | duplication | wrong placement | function decomposition | module structure | style | correctness>
|
||||
- File(s): <file paths>
|
||||
- Problem: <clear description>
|
||||
- Fix: <specific description of what to change>
|
||||
|
||||
Keep the list focused. Prioritize the most impactful issues.
|
||||
|
||||
When finished, report your verdict clearly as: APPROVED or NEEDS_CHANGES.
|
||||
```
|
||||
|
||||
### Step 6b: Review Fix
|
||||
|
||||
```text
|
||||
You are a review fix agent. You implement improvements identified during code review.
|
||||
|
||||
Read these files:
|
||||
- .ai/<PROJECT>/<LETTER>/context.md
|
||||
- .ai/<PROJECT>/<LETTER>/plan.md
|
||||
- .ai/<PROJECT>/<LETTER>/review<R>.md
|
||||
|
||||
Then read the source files mentioned in the review.
|
||||
|
||||
YOUR TASK: Implement all changes listed in review<R>.md.
|
||||
|
||||
Rules:
|
||||
- Implement exactly the review changes, nothing more.
|
||||
- Follow AGENTS.md coding conventions.
|
||||
- You are not alone in the codebase. Respect existing changes and do not revert unrelated work.
|
||||
- Do not modify .ai/ files except where the review process explicitly requires it.
|
||||
|
||||
After all changes are made:
|
||||
1. Build (from repository root): cmake --build ./out --config Debug --target Telegram
|
||||
2. If the build fails, fix build errors and rebuild until it passes.
|
||||
3. If build fails with file-locked errors (C1041, LNK1104, "cannot open output file", or similar access-denied lock issues), stop and report the lock. Do not retry.
|
||||
|
||||
When finished, report what changes were made and which files you touched.
|
||||
```
|
||||
|
||||
## Phase 7: Windows Text Normalization
|
||||
|
||||
Run this phase only on Windows hosts and only after the review loop has finished.
|
||||
|
||||
Use the current task's result logs as the source of truth for what Codex touched. Do not sweep the whole repo and do not rewrite unrelated files from a dirty worktree.
|
||||
|
||||
```text
|
||||
You are performing the final Windows-only text normalization phase for task-think.
|
||||
|
||||
Read these files:
|
||||
- .ai/<PROJECT>/<LETTER>/plan.md
|
||||
- .ai/<PROJECT>/<LETTER>/logs/phase-4*.result.md
|
||||
- .ai/<PROJECT>/<LETTER>/logs/phase-5*.result.md
|
||||
- .ai/<PROJECT>/<LETTER>/logs/phase-6*.result.md
|
||||
|
||||
Your job:
|
||||
- Collect the union of repo file paths listed under "Touched files" in those result logs.
|
||||
- Keep only files inside the repository that currently exist and are textual project files: source, headers, build/config files, localization files, style files, and similar text assets.
|
||||
- Exclude `.ai/`, `out/`, binary files, and unrelated user files that were not touched by Codex in this task.
|
||||
- Rewrite each kept file so all line endings are CRLF.
|
||||
- If a kept file is UTF-8 or ASCII text, write it back as UTF-8 without BOM. Never add a UTF-8 BOM to source/config/project text files.
|
||||
- Preserve file content otherwise. Preserve whether the file ended with a trailing newline.
|
||||
|
||||
Rules:
|
||||
- Run this phase in the main session on Windows.
|
||||
- Do not modify files outside the touched-file set for the current task.
|
||||
- Do not rewrite binary files.
|
||||
- When scripting this phase, do not use writer APIs or defaults that emit UTF-8 with BOM.
|
||||
- If a file cannot be normalized safely, record it as a failure instead of silently skipping it.
|
||||
|
||||
When finished:
|
||||
1. Write `.ai/<PROJECT>/<LETTER>/logs/phase-7-line-endings.result.md`
|
||||
2. Include:
|
||||
- whether the phase completed
|
||||
- which files were normalized
|
||||
- which files were skipped and why
|
||||
- whether any UTF-8 BOMs were removed or verified absent
|
||||
- any failures that need to be mentioned in the final summary
|
||||
```
|
||||
|
||||
## Completion
|
||||
|
||||
When all phases, including build verification, code review, and Windows line ending normalization when applicable, are done:
|
||||
1. Read the final `plan.md` and report the summary to the user.
|
||||
2. Show which files were modified or created.
|
||||
3. Note any issues encountered during implementation.
|
||||
4. Summarize the code review iterations: how many rounds, what was found and fixed, or whether it was approved on the first pass.
|
||||
5. On Windows, mention the text-normalization result briefly: which project files were normalized, whether any BOMs were removed, or whether nothing needed changes.
|
||||
6. Calculate and display the total elapsed time since `$START_TIME` (format as `Xh Ym Zs`, omitting zero components).
|
||||
7. Remind the user of the project name so they can request follow-up tasks within the same project.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If any phase fails or gets stuck, follow the timeout and retry rules above. Do not close an agent solely because the final artifact is missing while its progress file is still advancing. For Phase 1, Phase 2, Phase 3, Phase 4, and Phase 6, do not rerun locally after delegated retries fail; ask the user instead.
|
||||
- If `context.md` or `plan.md` is not written properly by a phase, rerun that phase in a fresh subagent with more specific instructions.
|
||||
- If build errors persist after the build phase's attempts, report the remaining errors to the user.
|
||||
- If a review-fix phase introduces new build errors that it cannot resolve, report to the user.
|
||||
|
||||
## Prompt Delivery And Logs
|
||||
|
||||
For each phase:
|
||||
1. Write the full prompt to `.ai/<PROJECT>/<LETTER>/logs/phase-<name>.prompt.md`
|
||||
2. Delegate by sending that prompt text to a fresh subagent, or use it as a same-session checklist only for the designated main-session phases or when delegation was unavailable from the start
|
||||
3. For delegated phases, expect a matching `.ai/<PROJECT>/<LETTER>/logs/phase-<name>.progress.md` heartbeat while work is in flight
|
||||
4. Save a concise completion note to `.ai/<PROJECT>/<LETTER>/logs/phase-<name>.result.md`
|
||||
|
||||
For review iterations, include the iteration in the file name, for example:
|
||||
- `phase-6a-review-1.prompt.md`
|
||||
- `phase-6a-review-1.result.md`
|
||||
- `phase-6b-fix-1.prompt.md`
|
||||
- `phase-6b-fix-1.result.md`
|
||||
|
||||
## Subagent Pattern
|
||||
|
||||
Use this pattern conceptually for delegated phases:
|
||||
|
||||
1. Write the phase prompt file.
|
||||
2. Spawn a fresh subagent with the phase prompt, usually with `fork_context: false`.
|
||||
3. Require the agent to create the matching progress file early and refresh it sparingly: at natural milestones when possible, otherwise only after a longer quiet stretch such as roughly 5-10 minutes.
|
||||
4. Wait in 5-minute intervals when the next step is blocked on that phase, checking both the final artifact and the progress file on timeout.
|
||||
5. When the phase looks close to finishing, switch to 1-2 minute waits.
|
||||
6. Prefer filesystem mtime checks on the progress file first. If its mtime moved or the heartbeat counter increased, keep waiting; do not treat that as a stall.
|
||||
7. If neither the artifact nor the progress file moves, send one short follow-up to the same agent, then retry once with a fresh subagent before involving the user.
|
||||
8. Validate the expected artifact or code changes with small shell summaries and the completion checks above.
|
||||
9. Write the result log from the validated outcome and the compact reply block.
|
||||
|
||||
Do not replace this pattern with shell-launched `codex exec`.
|
||||
@@ -0,0 +1,145 @@
|
||||
---
|
||||
name: task-think
|
||||
description: Orchestrate a multi-phase implementation workflow for this repository with artifact files under .ai/<project-name>/<letter>/ using Codex subagents instead of shell-spawned child processes. Use when the user wants one prompt to drive context gathering, planning, plan assessment, implementation, build verification, and review with persistent artifacts, clear phase handoffs, and a thin parent thread. Prefer spawn_agent/send_input/wait_agent, keep heavy pre-build work delegated when possible, and avoid pulling timed-out phases back into the main session.
|
||||
---
|
||||
|
||||
# Task Pipeline
|
||||
|
||||
Run a full implementation workflow with repository artifacts and clear phase boundaries.
|
||||
|
||||
## Inputs
|
||||
|
||||
Collect:
|
||||
- task description
|
||||
- optional project name (if missing, derive a short kebab-case name)
|
||||
- optional constraints (files, architecture, risk tolerance)
|
||||
- optional screenshot paths
|
||||
|
||||
If screenshots are attached in UI but not present as files, write a brief textual summary into the task artifacts before spawning fresh subagents so later phases can read the requirements without inheriting the whole parent thread.
|
||||
|
||||
## Overview
|
||||
|
||||
The workflow is organized around projects. Each project lives in `.ai/<project-name>/` and can contain multiple sequential tasks (labeled `a`, `b`, `c`, ... `z`).
|
||||
|
||||
Project structure:
|
||||
```text
|
||||
.ai/<project-name>/
|
||||
about.md # Single source of truth for the entire project
|
||||
a/ # First task
|
||||
context.md # Gathered codebase context for this task
|
||||
plan.md # Implementation plan
|
||||
review1.md # Code review documents (up to 3 iterations)
|
||||
review2.md
|
||||
review3.md
|
||||
logs/
|
||||
phase-*.prompt.md
|
||||
phase-*.progress.md
|
||||
phase-*.result.md
|
||||
b/ # Follow-up task
|
||||
context.md
|
||||
plan.md
|
||||
review1.md
|
||||
logs/
|
||||
...
|
||||
c/ # Another follow-up task
|
||||
...
|
||||
```
|
||||
|
||||
- `about.md` is the project-level blueprint: a single comprehensive document describing what this project does and how it works, written as if everything is already fully implemented. It contains no temporal state ("current state", "pending changes", "not yet implemented"). It is rewritten, not appended to, each time a new task starts, incorporating the new task's changes as if they were always part of the design.
|
||||
- Each task folder (`a/`, `b/`, ...) contains self-contained files for that task. The task's `context.md` carries all task-specific information: what specifically needs to change, the delta from the current codebase, gathered file references, and code patterns. Planning, implementation, and review phases should rely on the current task folder.
|
||||
|
||||
## Artifacts
|
||||
|
||||
Create and maintain:
|
||||
- `.ai/<project-name>/about.md`
|
||||
- `.ai/<project-name>/<letter>/context.md`
|
||||
- `.ai/<project-name>/<letter>/plan.md`
|
||||
- `.ai/<project-name>/<letter>/review<R>.md` (up to 3 review iterations)
|
||||
- `.ai/<project-name>/<letter>/logs/phase-<name>.prompt.md`
|
||||
- `.ai/<project-name>/<letter>/logs/phase-<name>.progress.md` for delegated phases
|
||||
- `.ai/<project-name>/<letter>/logs/phase-<name>.result.md`
|
||||
|
||||
Each `phase-<name>.result.md` should capture a concise outcome summary: whether the phase completed, which files it touched, and any follow-up notes or blockers.
|
||||
Each delegated `phase-<name>.progress.md` should act as a heartbeat: a tiny monotonic counter plus current step, files being read or edited, concrete findings so far, and the next checkpoint. It is not a final artifact; it exists so the parent can distinguish active research from a truly stuck subagent without rereading large context.
|
||||
|
||||
## Phases
|
||||
|
||||
Run these phases sequentially:
|
||||
|
||||
1. Phase 0: Setup - Record start time, detect follow-up vs new project, create directories.
|
||||
2. Phase 1: Context Gathering - Read codebase, write `about.md` and `context.md`. Use Phase 1F for follow-up tasks.
|
||||
3. Phase 2: Planning - Read context, write detailed `plan.md` with numbered steps grouped into phases.
|
||||
4. Phase 3: Plan Assessment - Review and refine the plan for correctness, completeness, code quality, and phase sizing.
|
||||
5. Phase 4: Implementation - Execute one implementation unit per plan phase.
|
||||
6. Phase 5: Build Verification - Build the project, fix any build errors. Skip if no source code was modified.
|
||||
7. Phase 6: Code Review Loop - Run review and fix iterations until approved or the iteration limit is reached.
|
||||
8. Phase 7: Windows Text Normalization - On Windows only, after review passes and before the final summary, normalize LF to CRLF for the text source/config files Codex edited in this task and ensure rewritten UTF-8 project files are saved without BOM.
|
||||
|
||||
Use the phase prompt templates in `PROMPTS.md`.
|
||||
|
||||
## Execution Mode
|
||||
|
||||
Use Codex subagents as the primary orchestration mechanism.
|
||||
|
||||
- When delegation is available, Phase 1, Phase 2, Phase 3, each Phase 4 implementation unit, and each Phase 6 review or review-fix pass must run in fresh subagents. Do not rerun those phases in the main session midstream just because a wait timed out or an artifact is missing.
|
||||
- Run Phase 7 in the main session on Windows because it depends on the final local file state and the exact touched-file set for the current task.
|
||||
- When any same-session helper rewrites Windows project text files, preserve CRLF and write UTF-8 without BOM. Avoid writer APIs or defaults that silently inject a UTF-8 BOM.
|
||||
- The main session may read `context.md` once after Phase 1 and `plan.md` once after Phase 3. After that, prefer narrow shell checks, file existence checks, and status-line reads instead of rereading full documents or diffs.
|
||||
- Prefer `worker` for phases that write files. Use `explorer` only for narrow read-only questions that unblock your next local step.
|
||||
- Keep `fork_context` off by default. Pass the phase prompt and explicit file paths instead of the whole thread unless the phase truly needs prior conversational context or thread-only attachments.
|
||||
- When the platform supports it, request `model: gpt-5.4` and `reasoning_effort: xhigh` for spawned phase agents. If overrides are unavailable, inherit the current session settings.
|
||||
- Write the exact phase prompt to the matching `logs/phase-<name>.prompt.md` file before you delegate. Use the same prompt file as a checklist if you later need to fall back to same-session execution.
|
||||
- For delegated phases, require an early `logs/phase-<name>.progress.md` heartbeat before deep work. The subagent should create or update it early, keep it tiny, and refresh it sparingly: preferably at natural milestones, and otherwise only after a longer quiet stretch such as roughly 5-10 minutes.
|
||||
- In every delegated prompt, require a compact final reply with only status, artifact paths, touched files, and blocker or `none`. Detailed reasoning belongs in `.ai/` artifacts, not in the chat reply.
|
||||
- After a subagent finishes, verify that the expected artifacts or code changes exist, then write a short result log in `logs/phase-<name>.result.md`.
|
||||
- For delegated phases, use `wait_agent` with a 5-minute timeout by default while a phase is still clearly in progress. Successful completion may wake earlier, so this does not add latency to finished phases.
|
||||
- When a phase looks close to completion — for example the final artifact has appeared, a build is in its final pass, or the agent said it is wrapping up — switch to 1-2 minute waits until it lands.
|
||||
- A timeout is not a failure; it only means no final status arrived yet. Do not treat short waits as stall detection for research-heavy phases.
|
||||
- On timeout, inspect the expected artifact, the phase progress file mtime, and the worktree for movement. Prefer mtime checks first; only reread the progress file when you need detail.
|
||||
- If the progress file mtime moved or its heartbeat counter increased since the previous check, treat that as active progress and wait again.
|
||||
- If no usable final artifact exists yet but the progress file is appearing or advancing, keep the same subagent alive. Progress-file movement does not count toward the retry limit.
|
||||
- If no usable final artifact exists yet and neither the expected artifact nor the progress file has moved since the previous blocked check, send one short follow-up asking the same subagent to refresh the progress file, finish the artifact, and return the compact status block, then wait again.
|
||||
- Only if the same subagent still shows no meaningful movement in either the expected artifact or the progress file after two full default waits and one follow-up should you close it and rerun that phase in a fresh subagent.
|
||||
- Use `wait_agent` only when the next step is blocked on the result. While the delegated phase runs, do small non-overlapping local tasks such as validating directory structure or preparing the next prompt file.
|
||||
- Build verification is critical-path work. Prefer running the build in the main session, and only delegate a bounded build-fix phase when there is a concrete reason.
|
||||
- If subagents are unavailable in the current environment, or current policy does not allow delegation from the start, run the phase in the main session using the same prompt files. Otherwise, do not switch a pre-build phase to same-session midstream. Never fall back to shell-spawned `codex exec` child processes from this skill.
|
||||
|
||||
## Verification Rules
|
||||
|
||||
- If build or test commands fail due to file locks or access-denied outputs (C1041, LNK1104), stop and ask the user to close locking processes before retrying.
|
||||
- Treat a delegated phase as complete only when the required artifact or status update exists on disk and matches the phase goals; do not rely on the chat reply alone.
|
||||
- Never claim completion without:
|
||||
- implemented code changes present
|
||||
- build attempt results recorded
|
||||
- review pass documented with any follow-up fixes
|
||||
- on Windows, if the task edited project source/config text files, a CRLF / no-BOM normalization pass recorded after review
|
||||
|
||||
## Completion Criteria
|
||||
|
||||
Mark complete only when:
|
||||
- All plan phases are done
|
||||
- Build verification is recorded
|
||||
- Review issues are addressed or explicitly deferred with rationale
|
||||
- On Windows, Codex-edited project source/config text files have been normalized to CRLF, any UTF-8 rewrites were saved without BOM, and the result is logged
|
||||
- Display total elapsed time since start (format: `Xh Ym Zs`, omitting zero components)
|
||||
- Remind the user of the project name so they can request follow-up tasks within the same project
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If any phase fails, times out, or gets stuck, follow the retry ladder from Execution Mode. Do not close an agent solely because the final artifact is missing while its progress file is still moving. After two delegated attempts remain blocked with no meaningful progress, report the issue to the user. Do not absorb the phase into the main session before build unless delegation was unavailable from the start.
|
||||
- If `context.md` or `plan.md` is not written properly by a phase, rerun that phase in a fresh subagent with more specific instructions. Do not repair it locally before build unless delegation was unavailable from the start.
|
||||
- If build errors persist after the build phase's attempts, report the remaining errors to the user.
|
||||
- If a review-fix phase introduces new build errors that it cannot resolve, report to the user.
|
||||
- If Phase 7 cannot safely normalize a touched file on Windows or remove an introduced UTF-8 BOM from a touched project text file, record the failure in the result log and report it in the final summary instead of silently skipping it.
|
||||
|
||||
## User Invocation
|
||||
|
||||
Use plain language with the skill name in the request, for example:
|
||||
|
||||
`Use local task-think skill with subagents: make sure FileLoadTask::process does not create or read QPixmap on background threads; use QImage with ARGB32_Premultiplied instead.`
|
||||
|
||||
For follow-up tasks on an existing project:
|
||||
|
||||
`Use local task-think skill with subagents: my-project also handle the case where the file is already cached`
|
||||
|
||||
If screenshots are relevant, include file paths in the same prompt when possible.
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignOperands: false
|
||||
AlignTrailingComments: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: true
|
||||
AfterControlStatement: false
|
||||
AfterEnum: true
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: true
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: true
|
||||
BeforeWhile: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBraces: Custom
|
||||
ColumnLimit: 120
|
||||
IncludeCategories:
|
||||
- Regex: '^<.*'
|
||||
Priority: 1
|
||||
- Regex: '^".*'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IndentCaseLabels: true
|
||||
IndentWidth: 4
|
||||
InsertNewlineAtEOF: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 2
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInAngles: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
TabWidth: 4
|
||||
UseTab: Always
|
||||
...
|
||||
@@ -0,0 +1,306 @@
|
||||
---
|
||||
description: Generate an SVG icon from a design mockup using vectosolve vectorization
|
||||
allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Agent, AskUserQuestion, TodoWrite, mcp__vectosolve__vectorize
|
||||
---
|
||||
|
||||
# Icon - SVG Icon Generation from Design Mockup
|
||||
|
||||
You generate production-quality SVG icons for Telegram Desktop by vectorizing design mockup screenshots using the vectosolve MCP service, then post-processing the result to match the Telegram icon format.
|
||||
|
||||
**Arguments:** `$ARGUMENTS` = "$ARGUMENTS"
|
||||
|
||||
If `$ARGUMENTS` is empty, ask the user to describe the icon they want and paste a cropped screenshot of it.
|
||||
|
||||
## Overview
|
||||
|
||||
The workflow takes a cropped screenshot of an icon from a design mockup (grabbed from the clipboard), vectorizes it via the vectosolve MCP, then post-processes the SVG (recolor to white-on-transparent, restructure to minimal format, set 24x24 output size).
|
||||
|
||||
Working directory: `.ai/icon_{name}/` with iterations labeled by letter (`a/`, `b/`, ...), each containing `source.png`. Output SVGs are in the icon root: `a.svg`, `b.svg`, etc.
|
||||
|
||||
Follow-ups are supported: `/icon {icon_name} <description>` continues from where the previous run left off.
|
||||
|
||||
## Phase 0: Setup
|
||||
|
||||
**Record the current time** (using `date` or equivalent) as `$START_TIME`.
|
||||
|
||||
### Step 0a: Clipboard grab (MUST be the VERY FIRST action)
|
||||
|
||||
If there is an image attached to the user's message:
|
||||
|
||||
1. Generate a random 8-character hex string for `HASH` (use `openssl rand -hex 4` or similar).
|
||||
2. **IMMEDIATELY** — before any other processing — run this Bash command to save the clipboard image:
|
||||
```bash
|
||||
HASH=$(openssl rand -hex 4) && if [[ "$OSTYPE" == darwin* ]]; then bash .claude/grab_clipboard.sh ".ai/icon_${HASH}.png"; else powershell -ExecutionPolicy Bypass -File .claude/grab_clipboard.ps1 ".ai/icon_${HASH}.png"; fi
|
||||
```
|
||||
On macOS `.claude/grab_clipboard.sh` is used; on Windows `.claude/grab_clipboard.ps1`. Both grab the current clipboard image and save it to the specified path.
|
||||
|
||||
3. If the command fails (exit 1 / no image on clipboard):
|
||||
- Tell the user: **"Clipboard doesn't contain an image. Please copy the icon area first, then retry."** (On macOS: Cmd+Ctrl+Shift+4 to snip to clipboard; on Windows: Win+Shift+S.)
|
||||
- **STOP IMMEDIATELY. Do NOT continue.** You cannot use the image pasted in the conversation — it exists only as pixels in the chat, not as a file you can send to vectosolve. The clipboard grab is the ONLY way to get the image to disk. Do not attempt any workaround.
|
||||
|
||||
4. Read back the saved `.ai/icon_HASH.png` using the Read tool.
|
||||
5. Compare it visually with the image pasted in the conversation. They should depict the same thing.
|
||||
- If they look **completely different**: delete `.ai/icon_HASH.png` and fail:
|
||||
> "The clipboard image doesn't match what you pasted. Please re-copy and retry."
|
||||
- If they look the same (or close enough): proceed. Store the temp path.
|
||||
|
||||
If NO image is attached to the message, skip this step entirely.
|
||||
|
||||
### Step 0b: Fail-fast — verify vectosolve MCP
|
||||
|
||||
Check that the `mcp__vectosolve__vectorize` tool is available by looking at your available tools list. If it is NOT available, fail immediately with:
|
||||
|
||||
> vectosolve MCP is not configured. Set it up with:
|
||||
> ```
|
||||
> claude mcp add vectosolve --scope user -e VECTOSOLVE_API_KEY=vs_xxx -- npx @vectosolve/mcp
|
||||
> ```
|
||||
> Then restart Claude Code.
|
||||
|
||||
### Step 0c: Follow-up detection
|
||||
|
||||
Extract the first word/token from `$ARGUMENTS` (everything before the first space or newline). Call it `FIRST_TOKEN`.
|
||||
|
||||
Run these TWO commands using the Bash tool, **IN PARALLEL**:
|
||||
1. `ls .ai/` — to see all existing icon project names
|
||||
2. `ls .ai/icon_{FIRST_TOKEN}/context.md` — to check if this specific icon project exists
|
||||
|
||||
**Evaluate the results:**
|
||||
- If command 2 **succeeds** (context.md exists): this is a **follow-up**. The icon name is `FIRST_TOKEN`. The follow-up description is everything in `$ARGUMENTS` after `FIRST_TOKEN`.
|
||||
- If command 2 **fails** (not found): this is a **new icon**. The full `$ARGUMENTS` is the icon description.
|
||||
|
||||
### Step 0d: New icon setup
|
||||
|
||||
1. Parse `$ARGUMENTS` to determine:
|
||||
- **Icon description**: what the icon should depict
|
||||
- **Icon type**: default is `menu` (24x24 menu/button icon). User may specify otherwise.
|
||||
- **Target subfolder**: `menu/` by default, or another subfolder if specified.
|
||||
|
||||
2. Choose an icon file name:
|
||||
- Lowercase letters and underscores only — **NO hyphens**
|
||||
- Match existing naming conventions (check `Telegram/Resources/icons/{subfolder}/`)
|
||||
- Must NOT conflict with existing icons
|
||||
- Must NOT collide with existing `.ai/icon_{name}/` directories
|
||||
|
||||
3. Create `.ai/icon_{name}/` and `.ai/icon_{name}/a/`.
|
||||
|
||||
4. Write `.ai/icon_{name}/context.md` with:
|
||||
```
|
||||
## Icon: {icon_name}
|
||||
Type: {menu/other}
|
||||
Target: Telegram/Resources/icons/{subfolder}/{icon_name}.svg
|
||||
|
||||
## Original Request
|
||||
{full $ARGUMENTS text}
|
||||
|
||||
## Follow-ups
|
||||
(none yet)
|
||||
```
|
||||
|
||||
5. Set `LETTER` to `a`.
|
||||
|
||||
### Step 0e: Follow-up setup
|
||||
|
||||
1. Read `.ai/icon_{name}/context.md` to get the icon type, subfolder, and full history.
|
||||
2. Find the latest existing letter folder in `.ai/icon_{name}/` (highest letter).
|
||||
3. Set `LETTER` to the next letter after the latest.
|
||||
4. Create `.ai/icon_{name}/{LETTER}/`.
|
||||
5. Update `.ai/icon_{name}/context.md` — append the follow-up description to the `## Follow-ups` section:
|
||||
```
|
||||
### Follow-up (starting at letter {LETTER})
|
||||
{follow-up description}
|
||||
```
|
||||
|
||||
### Step 0f: Place source image
|
||||
|
||||
If a clipboard image was grabbed in Step 0a:
|
||||
1. Copy (or move) `.ai/icon_HASH.png` → `.ai/icon_{name}/source.png` (overwrite if exists — this is always the latest source).
|
||||
2. Copy it to `.ai/icon_{name}/{LETTER}/source.png` (archive per-iteration source).
|
||||
3. Delete the temp `.ai/icon_HASH.png` if it was copied (not moved).
|
||||
|
||||
If NO image was grabbed:
|
||||
- **New icon with no image**: Ask the user to provide a screenshot. STOP.
|
||||
- **Follow-up with no image**: The existing `source.png` in the icon root carries forward. Copy it to `.ai/icon_{name}/{LETTER}/source.png`. If no source.png exists at all, ask the user for an image.
|
||||
|
||||
### Step 0g: Verify renderer
|
||||
|
||||
Locate the render tool (`codegen_style` with `--render-svg` mode):
|
||||
|
||||
```bash
|
||||
if [[ "$OSTYPE" == darwin* ]]; then
|
||||
ls out/Telegram/codegen/codegen/style/Debug/codegen_style
|
||||
else
|
||||
ls out/Telegram/codegen/codegen/style/Debug/codegen_style.exe
|
||||
fi
|
||||
```
|
||||
|
||||
If missing, build it: `cmake --build out --config Debug --target codegen_style`
|
||||
|
||||
Test on a known good SVG (use the appropriate binary path for the OS):
|
||||
```bash
|
||||
CODEGEN=$(if [[ "$OSTYPE" == darwin* ]]; then echo out/Telegram/codegen/codegen/style/Debug/codegen_style; else echo out/Telegram/codegen/codegen/style/Debug/codegen_style.exe; fi)
|
||||
$CODEGEN --render-svg Telegram/Resources/icons/menu/tag_add.svg .ai/icon_{name}/test_render.png 512
|
||||
```
|
||||
|
||||
If works → delete test render, set `RENDER_AVAILABLE = true`. If fails → `RENDER_AVAILABLE = false`.
|
||||
|
||||
## Phase 1: Vectorize & Post-process
|
||||
|
||||
### Step 1a: Call vectosolve
|
||||
|
||||
Use the `mcp__vectosolve__vectorize` tool with `file_path` set to the **absolute path** of `.ai/icon_{name}/{LETTER}/source.png`.
|
||||
|
||||
**If this fails, STOP IMMEDIATELY.** Do NOT try to generate the SVG manually or by any other means. Report the error to the user and let them fix the issue (bad API key, no credits, network error, etc.).
|
||||
|
||||
Save the returned SVG content to `.ai/icon_{name}/{LETTER}/raw_vectosolve.svg`.
|
||||
|
||||
The MCP tool calls the vectosolve API ($0.20/call). The API key is stored in `~/.claude.json` MCP config (never in the repository).
|
||||
|
||||
### Step 1b: Post-process the SVG
|
||||
|
||||
The vectosolve SVG will have colors from the mockup, arbitrary dimensions, and possibly a non-square aspect ratio from a non-square screenshot crop. Post-processing fixes this by adjusting the **viewBox** — leave path coordinates untouched.
|
||||
|
||||
**Do NOT transform path coordinates.** Vectosolve's paths are correct — the only thing wrong is the framing. All geometry adjustments are done by manipulating the `viewBox` and the `width`/`height` attributes.
|
||||
|
||||
#### Sub-step 1: Read the request and determine parameters
|
||||
|
||||
Before touching the SVG, determine these from the user's request and context.md:
|
||||
|
||||
1. **Output size** (`OUT_W × OUT_H`): default is `24px × 24px` for menu icons. The user may request different dimensions (e.g., 36×36, 48×48, or non-square). Always check the request.
|
||||
2. **Content padding**: default is ~2px equivalent on each side at the output scale (so content fills roughly (OUT_W-4) × (OUT_H-4)). The user may request different padding or edge-to-edge.
|
||||
3. **Centering**: default is centered both horizontally and vertically. The user may request specific alignment (e.g., "align to bottom").
|
||||
|
||||
#### Sub-step 2: Parse the raw SVG
|
||||
|
||||
1. Extract the `viewBox`: `viewBox="VB_X VB_Y VB_W VB_H"` (typically `0 0 W H`).
|
||||
2. Identify ALL paths. Classify each:
|
||||
- **Background**: a rect or path spanning the full viewBox (first path that's a simple rectangle matching the viewBox bounds). **Remove it entirely.**
|
||||
- **Content**: the actual icon shapes. **Keep these, paths unchanged.**
|
||||
3. If paths have `transform="translate(TX,TY)"` attributes, that's fine — keep them as-is. The viewBox framing will work regardless.
|
||||
|
||||
#### Sub-step 3: Compute the content bounding box
|
||||
|
||||
Estimate the bounding box of the content paths (after removing the background). You can either:
|
||||
- Eyeball it from the path coordinates (look at first/last M commands and extremes of curves)
|
||||
- Or for precision, write a quick script to parse the paths and find min/max X/Y
|
||||
|
||||
Call the result: `CX_MIN, CY_MIN, CX_MAX, CY_MAX`. Content dimensions: `CW = CX_MAX - CX_MIN`, `CH = CY_MAX - CY_MIN`.
|
||||
|
||||
#### Sub-step 4: Compute the new viewBox
|
||||
|
||||
The viewBox determines what part of the SVG coordinate space maps to the output rectangle. By expanding the viewBox beyond the content bounds, we add padding. By making the viewBox aspect ratio match the output aspect ratio, we prevent stretching.
|
||||
|
||||
1. **Output aspect ratio**: `OUT_AR = OUT_W / OUT_H` (for 24×24 this is 1.0).
|
||||
2. **Padding in SVG coordinates**: we want ~2px padding at output scale. The scale factor is `OUT_W / VB_CONTENT_W` approximately, so padding in SVG coords = `2 * (CW / (OUT_W - 4))` (or similar — the exact formula depends on which dimension is dominant). Simpler approach: aim for content to occupy ~83% of the viewBox (≈ 20/24), so:
|
||||
- `PADDED_W = CW / 0.83`
|
||||
- `PADDED_H = CH / 0.83`
|
||||
3. **Match output aspect ratio**: the viewBox aspect ratio must equal `OUT_AR` to avoid stretching.
|
||||
- If `PADDED_W / PADDED_H > OUT_AR`: width is dominant → `VB_W = PADDED_W`, `VB_H = VB_W / OUT_AR`
|
||||
- If `PADDED_W / PADDED_H < OUT_AR`: height is dominant → `VB_H = PADDED_H`, `VB_W = VB_H * OUT_AR`
|
||||
- If equal: `VB_W = PADDED_W`, `VB_H = PADDED_H`
|
||||
4. **Center the content** in the new viewBox:
|
||||
- `VB_X = CX_MIN - (VB_W - CW) / 2`
|
||||
- `VB_Y = CY_MIN - (VB_H - CH) / 2`
|
||||
- (Adjust if the user requested non-centered alignment)
|
||||
|
||||
The new viewBox is: `viewBox="VB_X VB_Y VB_W VB_H"`.
|
||||
|
||||
#### Sub-step 5: Recolor to white-on-transparent
|
||||
|
||||
- Replace ALL `fill` color values (anything that isn't `none`) with `#FFFFFF`.
|
||||
- Remove ALL `stroke` and `stroke-width` attributes entirely.
|
||||
- Remove `opacity` attributes if present.
|
||||
|
||||
#### Sub-step 6: Determine path composition
|
||||
|
||||
Look at the icon's visual structure and decide how paths should combine:
|
||||
- **Outlined shape** (e.g., circle outline with something inside): combine outer + inner cutout into one `<path>` with `fill-rule="evenodd"`.
|
||||
- **Separate distinct parts** (e.g., magnifying glass + checkmark): keep as separate `<path>` elements.
|
||||
- **Filled shape with cutout** (e.g., filled circle with checkmark punched out): combine into one path with `fill-rule="evenodd"`.
|
||||
|
||||
#### Sub-step 7: Assemble final SVG
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="{OUT_W}px" height="{OUT_H}px" viewBox="{VB_X} {VB_Y} {VB_W} {VB_H}" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="..." fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
```
|
||||
|
||||
- `width`/`height` = the output size from the request (default `24px`/`24px`).
|
||||
- `viewBox` = the computed viewBox from Sub-step 4. The SVG renderer maps this coordinate region to the output size.
|
||||
- Path `d` attributes are **unchanged** from vectosolve output (just background removed, colors replaced).
|
||||
- No `<title>`, `id`, `xmlns:xlink`, `version`, `class`, `style`, XML comments, `<metadata>`, or `preserveAspectRatio`.
|
||||
- No `<circle>`, `<rect>`, `<line>` — only `<path>`.
|
||||
|
||||
Write the final SVG to `.ai/icon_{name}/{LETTER}.svg`.
|
||||
|
||||
### Step 1c: Render
|
||||
|
||||
If `RENDER_AVAILABLE`:
|
||||
```bash
|
||||
$CODEGEN --render-svg ".ai/icon_{name}/{LETTER}.svg" ".ai/icon_{name}/render_{LETTER}.png" 512
|
||||
```
|
||||
|
||||
Read the render to visually verify the result.
|
||||
|
||||
## Phase 2: Review
|
||||
|
||||
After rendering, assess the result:
|
||||
|
||||
1. **Recognizable?** The icon should be clearly identifiable as the intended symbol.
|
||||
2. **Scale reasonable?** Should fill the space appropriately with ~2-3px padding.
|
||||
3. **Clean lines?** No broken paths, artifacts, or unwanted elements.
|
||||
4. **Correct colors?** All white on transparent (no leftover colors from the mockup).
|
||||
|
||||
If the result looks good → proceed to Phase 3 (Output).
|
||||
|
||||
If there are fixable issues (stray element, missed color, etc.) → fix the SVG directly, re-render, and re-check.
|
||||
|
||||
If the result is poor (vectosolve couldn't handle the input well) → report to the user and suggest:
|
||||
- Trying a cleaner/larger crop of the icon
|
||||
- Providing a different screenshot
|
||||
- Following up: `/icon {icon_name} <description of what to change>`
|
||||
|
||||
## Phase 3: Output
|
||||
|
||||
1. Read the `Target:` line from `.ai/icon_{name}/context.md` to get the output path.
|
||||
|
||||
2. Copy the final SVG to that target path (e.g., `Telegram/Resources/icons/menu/{icon_name}.svg`).
|
||||
|
||||
3. Update `.ai/icon_{name}/context.md` — append to the end:
|
||||
```
|
||||
## Latest Output
|
||||
Letter: {LETTER}
|
||||
Written to: {target_path}
|
||||
```
|
||||
|
||||
4. Report to the user:
|
||||
- Final icon file path
|
||||
- Number of vectosolve calls made (cost at $0.20/call)
|
||||
- Suggest verifying visually
|
||||
- Working directory `.ai/icon_{name}/` has all iterations
|
||||
- Elapsed time since `$START_TIME` (format `Xm Ys`)
|
||||
- Follow-up: `/icon {icon_name} <description of what to change>`
|
||||
|
||||
## Text-only Follow-ups (no new image)
|
||||
|
||||
When a follow-up has no attached image, the user wants to refine the existing SVG based on text feedback. In this case:
|
||||
|
||||
1. Skip Phase 1 (no vectosolve call needed).
|
||||
2. Read the latest SVG (`.ai/icon_{name}/{prev_letter}.svg`).
|
||||
3. Read the latest render if available.
|
||||
4. Apply the user's requested changes by editing the SVG directly.
|
||||
5. Save as `.ai/icon_{name}/{LETTER}.svg`.
|
||||
6. Render, review, and output as normal (Phases 1c → 3).
|
||||
|
||||
If the changes are too complex for manual SVG editing, suggest the user provide a new screenshot instead.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If clipboard grab fails → tell user to re-copy and retry.
|
||||
- If vectosolve returns an error → report it and suggest a different/cleaner screenshot.
|
||||
- If vectosolve returns SVG that can't be parsed → save raw output for debugging, report to user.
|
||||
- If the render helper fails → set `RENDER_AVAILABLE = false`, continue with SVG-only review.
|
||||
- If post-processing produces a broken SVG → fall back to the raw vectosolve output and do lighter cleanup.
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
description: Plan and create a repetitive task automation (prompt.md + tasks.json pair)
|
||||
allowed-tools: Read, Write, Edit, Glob, Grep, Bash(mkdir:*), Bash(ls:*), AskUserQuestion
|
||||
---
|
||||
|
||||
# Task Planner - Create Automated Task Workflows
|
||||
|
||||
You are setting up a new **repetitive task automation** for Claude Code. The goal is to create a folder in `.ai/<featurename>/` containing:
|
||||
- `prompt.md` - Detailed instructions for the autonomous agent
|
||||
- `tasks.json` - List of tasks with completion tracking
|
||||
|
||||
This pair can then be executed via `.claude/iterate.ps1 <featurename>`.
|
||||
|
||||
## Your Workflow
|
||||
|
||||
### 1. Understand the Goal
|
||||
|
||||
First, understand what the user wants to automate. Ask clarifying questions using AskUserQuestion if needed:
|
||||
- What is the overall goal/feature being implemented?
|
||||
- What are the individual tasks involved?
|
||||
- Are there dependencies between tasks?
|
||||
- What files/areas of the codebase are involved?
|
||||
- Are there any reference examples or patterns to follow?
|
||||
|
||||
### 2. Choose a Feature Name
|
||||
|
||||
The `<featurename>` should be:
|
||||
- Short (1-2 words, lowercase, hyphen-separated)
|
||||
- Easy to type on command line
|
||||
- Descriptive of the work being done
|
||||
- Not already used in `.ai/`
|
||||
|
||||
Check existing folders:
|
||||
```bash
|
||||
ls .ai/
|
||||
```
|
||||
|
||||
Suggest a name to the user or let them specify one directly via $ARGUMENTS.
|
||||
|
||||
|
||||
### 3. Create the Folder and Files
|
||||
|
||||
Create `.ai/<featurename>/`:
|
||||
|
||||
**prompt.md** should include:
|
||||
- Overview of what we're doing
|
||||
- Architecture/context needed
|
||||
- Step-by-step instructions for each task type
|
||||
- Code patterns and examples
|
||||
- Build/test commands
|
||||
- Commit message format (see below)
|
||||
|
||||
### Commit Message Guidelines
|
||||
|
||||
All prompts should specify commit message length requirements:
|
||||
- **Soft limit**: ~50 characters (ideal length for first line)
|
||||
- **Hard limit**: 76 characters (must not exceed)
|
||||
|
||||
Example instruction for prompt.md:
|
||||
```
|
||||
## Commit Format
|
||||
|
||||
First line: Short summary ending with a dot (aim for ~50 chars, max 76 chars)
|
||||
|
||||
<Optional body with details, also ending with a dot.>
|
||||
|
||||
IMPORTANT: Never try to commit files in .ai/
|
||||
```
|
||||
|
||||
**tasks.json** format:
|
||||
```json
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"id": "task-id",
|
||||
"title": "Short task title",
|
||||
"description": "Detailed description of what to do",
|
||||
"started": false,
|
||||
"completed": false,
|
||||
"dependencies": ["other-task-id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Iterate with the User
|
||||
|
||||
After creating initial files, the user may want to:
|
||||
- Add more tasks to tasks.json
|
||||
- Refine the prompt with more details
|
||||
- Add examples or patterns
|
||||
- Clarify instructions
|
||||
|
||||
Keep refining until the user is satisfied.
|
||||
|
||||
## Arguments
|
||||
|
||||
If `$ARGUMENTS` is provided, it's the feature name to use:
|
||||
- `$ARGUMENTS` = "$ARGUMENTS"
|
||||
|
||||
If empty, you'll need to determine/suggest a name based on the discussion.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Settings Migration
|
||||
```
|
||||
/taskplanner settings-upgrade
|
||||
```
|
||||
Creates `.ai/settings-upgrade/` with prompt and tasks for migrating settings sections.
|
||||
|
||||
### Example 2: Open-ended
|
||||
```
|
||||
/taskplanner
|
||||
```
|
||||
Starts a conversation to understand what needs to be automated, then creates the appropriate folder.
|
||||
|
||||
## Starting Point
|
||||
|
||||
Let's begin! Please describe:
|
||||
1. What repetitive coding task do you want to automate?
|
||||
2. What is the end goal?
|
||||
3. Do you have initial tasks in mind, or should we discover them together?
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
description: Learn from corrections — examine staged vs unstaged diffs and optionally distill insights into AGENTS.md or REVIEW.md
|
||||
allowed-tools: Read, Edit, Bash(git diff:*), Bash(git status:*), Bash(git log:*), Bash(ls:*), AskUserQuestion
|
||||
---
|
||||
|
||||
# Reflect — Learn from Corrections
|
||||
|
||||
You are a reflection agent. Your job is to examine the difference between what an AI agent produced (staged changes) and what the user corrected (unstaged changes), and determine whether any **general, reusable insight** can be extracted and added to the project's coding guidelines.
|
||||
|
||||
**CRITICAL: Use extended thinking ultrathink for your analysis. This requires deep, careful reasoning.**
|
||||
|
||||
## Arguments
|
||||
|
||||
`$ARGUMENTS` = "$ARGUMENTS"
|
||||
|
||||
If `$ARGUMENTS` is provided, it is a task name (project name from the `/task` workflow). This means the agent was working within `.ai/<task-name>/` and you should read the task context for deeper understanding of what the agent was trying to do.
|
||||
|
||||
If `$ARGUMENTS` is empty, skip the task context step — just work from the diffs alone.
|
||||
|
||||
## Context
|
||||
|
||||
The workflow is:
|
||||
1. An AI agent implemented something and its changes were staged (`git add`).
|
||||
2. The user reviewed and corrected the agent's work. These corrections are unstaged.
|
||||
3. You are now invoked to reflect on what went wrong and whether it reveals a pattern.
|
||||
|
||||
## Step 1: Gather the Diffs and Task Context
|
||||
|
||||
Run these commands in parallel:
|
||||
|
||||
```bash
|
||||
git diff --cached # What the agent wrote (staged)
|
||||
git diff # What the user corrected (unstaged, on top of staged)
|
||||
git status # Which files are involved
|
||||
```
|
||||
|
||||
If either diff is empty, tell the user and stop. Both diffs must be non-empty for reflection to be meaningful.
|
||||
|
||||
### Task context (only if `$ARGUMENTS` is non-empty)
|
||||
|
||||
The task name is `$ARGUMENTS`. Read the task's project context:
|
||||
|
||||
1. Read `.ai/$ARGUMENTS/about.md` — the project-level description of what this feature does.
|
||||
2. Find the latest task iteration folder: list `.ai/$ARGUMENTS/` and pick the folder with the highest letter (`a`, `b`, `c`, ...).
|
||||
3. Read `.ai/$ARGUMENTS/<latest-letter>/context.md` — the detailed implementation context the agent was working from.
|
||||
|
||||
This helps you distinguish between:
|
||||
- **Task-specific mistakes** — the agent misunderstood this particular feature's requirements or made a wrong choice within the specific problem. These are NOT documentation-worthy.
|
||||
- **General convention mistakes** — the agent did something that violates a pattern the codebase follows broadly, regardless of which feature is being implemented. These ARE potentially documentation-worthy.
|
||||
|
||||
Having the task context makes this distinction much sharper. Without it, you might mistake a task-specific correction for a general pattern or vice versa.
|
||||
|
||||
## Step 2: Read the Current Guidelines
|
||||
|
||||
Read both files:
|
||||
- `AGENTS.md` — development guidelines: build system, coding style, API usage patterns, UI styling, localization, rpl, architectural conventions, "how to do things"
|
||||
- `REVIEW.md` — mechanical style and formatting rules: brace placement, operator position, type checks, variable initialization, call formatting
|
||||
|
||||
Read them carefully. You need to know exactly what's already documented to avoid duplicates and detect contradictions.
|
||||
|
||||
## Step 3: Analyze the Corrections
|
||||
|
||||
Now think deeply. For each correction the user made, ask yourself:
|
||||
|
||||
1. **What did the agent do wrong?** Understand the specific mistake.
|
||||
2. **Why was it wrong?** Identify the underlying principle.
|
||||
3. **Is this already covered by AGENTS.md or REVIEW.md?** Check carefully:
|
||||
- If the existing rule's scope, title, and examples **clearly cover** this exact scenario and the agent just ignored it — that's not a documentation problem. Skip it.
|
||||
- If the existing rule **technically applies** but its scope is too narrow, its examples don't illustrate this usage pattern, or its wording would reasonably lead an agent to think it doesn't apply here — **the rule needs improvement**. Treat this as a potential insight (broaden the scope, add examples, adjust wording). A rule that agents repeatedly violate is an ineffective rule.
|
||||
4. **Is this specific to this particular task, or is it general?** Most corrections are task-specific ("wrong variable here", "this should call that function instead"). These are NOT documentation-worthy. Only patterns that would apply across many different tasks are worth capturing.
|
||||
5. **Would documenting this actually help a future agent?** Some things are too context-dependent or too obvious to be useful as a written rule. Be honest about this.
|
||||
|
||||
## Step 4: Decision
|
||||
|
||||
After analysis, you MUST reach one of these conclusions:
|
||||
|
||||
### Conclusion A: No actionable insight
|
||||
|
||||
The corrections are purely task-specific, or the existing documentation clearly and specifically covers the exact scenario and the agent simply ignored it. Say what the corrections were and why no doc changes are needed. Then **stop**.
|
||||
|
||||
### Conclusion B: New insight found
|
||||
|
||||
You can articulate a **concise, general rule** that:
|
||||
- Applies broadly (not just to this one task)
|
||||
- Is not already documented
|
||||
- Would genuinely help a future agent avoid the same class of mistake
|
||||
- Can be expressed in a few sentences with a clear code example
|
||||
|
||||
If you have a new insight, proceed to Step 5.
|
||||
|
||||
### Conclusion C: Existing rule needs improvement
|
||||
|
||||
A rule already exists in AGENTS.md or REVIEW.md, but its **scope is too narrow**, its **examples don't cover** the pattern the agent encountered, or its **wording** would reasonably lead an agent to think the rule doesn't apply. The agent's mistake is evidence the rule isn't effective.
|
||||
|
||||
This is NOT the same as Conclusion A. The test: would a careful agent, reading the existing rule, clearly know it applies to this specific situation? If no — the rule needs to be broadened, its examples expanded, or its title/scope adjusted. Proceed to Step 5.
|
||||
|
||||
**Common signs of an ineffective rule:**
|
||||
- The rule's title or scope restricts it to a context narrower than the actual principle (e.g., "in localization calls" when the pattern applies generally)
|
||||
- The examples only show one usage pattern, and the agent encountered a different one
|
||||
- The wording describes *what* to use but not *when* — so agents only apply it in situations that look like the examples
|
||||
|
||||
## Step 5: Categorize and Check for Contradictions
|
||||
|
||||
### Where does it belong?
|
||||
|
||||
- **REVIEW.md** — if it's a mechanical/style rule: formatting, naming, syntax preferences, call structure, brace/operator placement, type usage patterns. Rules that can be checked by looking at code locally without understanding the broader feature.
|
||||
- **AGENTS.md** — if it's an architectural/behavioral guideline: how to use APIs, where to place code, design patterns, build conventions, module organization, reactive patterns (rpl), localization usage, style system usage. Rules that require understanding the broader context.
|
||||
|
||||
### Does it contradict existing content?
|
||||
|
||||
Read the target file again carefully. Check if:
|
||||
1. The new insight **contradicts** an existing rule — if so, do NOT just append or just remove. Instead, use AskUserQuestion to present both the existing rule and the new insight to the user, explain the contradiction, and ask how to reconcile them.
|
||||
2. The new insight **overlaps** with an existing rule — if so, consider whether the existing rule should be extended/refined rather than adding a separate entry.
|
||||
3. The new insight is **complementary** — it adds something new without conflicting. This is the simplest case.
|
||||
|
||||
## Step 6: Propose the Change
|
||||
|
||||
**Do NOT silently edit the files.** First, present your proposed change to the user:
|
||||
|
||||
- Quote the exact text you want to add or modify
|
||||
- Explain which file and where in the file
|
||||
- Explain why this is general enough to document
|
||||
- If modifying existing text, show the before and after
|
||||
|
||||
Use AskUserQuestion to get the user's approval before making any edit.
|
||||
|
||||
Only after the user approves, apply the edit using the Edit tool.
|
||||
|
||||
## Rules
|
||||
|
||||
- **Keep docs lean and high-signal.** Don't add vague or overly specific rules. But don't default to inaction either — if the user had to manually fix something that a better-worded rule would have prevented, improving that rule is high-signal work.
|
||||
- **Never dump corrections verbatim.** The goal is distilled principles, not a changelog of mistakes.
|
||||
- **One insight per reflection, maximum.** If you think you see multiple insights, pick the strongest one. You can always run `/reflect` again next time.
|
||||
- **Keep the same style.** Match the formatting, tone, and level of detail of the target file. REVIEW.md uses specific before/after code examples. AGENTS.md uses explanatory sections with code snippets.
|
||||
- **Don't add "don't do X" rules.** Frame rules positively: "do Y" is better than "don't do X." Show the right way, not just the wrong way.
|
||||
- **No meta-commentary.** Don't add notes like "Added after reflection on..." — the rule should read as if it was always there.
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
description: Prepare changelog, set version, and commit a new release
|
||||
allowed-tools: Read, Bash, Edit, Grep, AskUserQuestion
|
||||
---
|
||||
|
||||
# Release — Changelog, Set Version, Commit
|
||||
|
||||
Full release flow: generate changelog entry, run `set_version`, and commit.
|
||||
|
||||
**Arguments:** `$ARGUMENTS` = "$ARGUMENTS"
|
||||
|
||||
Parse `$ARGUMENTS` for two optional parts (in any order):
|
||||
- A **version number** like `6.7` or `6.7.0` — if provided, use it as the new version exactly.
|
||||
- The word **"beta"** — if present, mark the release as beta.
|
||||
|
||||
If no version number is given, auto-increment the patch component (see step 2).
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Check git status is clean
|
||||
|
||||
Run `git status --porcelain`. If there are any uncommitted changes, **stop** and ask the user to commit or discard them before proceeding. Do not continue until status is clean.
|
||||
|
||||
### 2. Read the current changelog
|
||||
|
||||
Read `changelog.txt` from the repository root. Note the **latest version number** on the first line (e.g. `6.6.3 beta (12.03.26)`). Parse its major.minor.patch components.
|
||||
|
||||
### 3. Determine the new version number
|
||||
|
||||
**Important:** version numbers are shared across the beta and stable tracks — the sequence advances through both. The same `major.minor.patch` cannot be released as both beta and stable. A beta release "uses up" that number; the next stable must bump to a new patch.
|
||||
|
||||
- **If a version was provided in arguments**, use it directly (append `.0` if only major.minor was given). If the latest changelog entry already used this exact number (regardless of beta/stable), warn the user — they likely want a bumped patch.
|
||||
- **If no version was provided**, auto-increment from the latest changelog version:
|
||||
- If it was a beta, and the new release is **not** beta, bump the patch component by 1 (do **not** reuse the beta's number for stable).
|
||||
- If the new release is beta and the latest was also beta with the same major.minor, bump patch.
|
||||
- Otherwise bump the patch component by 1.
|
||||
- Present the chosen version to the user and ask for confirmation before proceeding. If the user suggests a different version, use that instead.
|
||||
|
||||
### 4. Fetch tags and determine the last release tag
|
||||
|
||||
Run `git fetch origin --tags` first to ensure all tags from the public repository are available locally. Then run `git tag --sort=-v:refname` and find the most recent `v*` tag. This is the baseline for the diff.
|
||||
|
||||
### 5. Collect commits
|
||||
|
||||
Run `git log <last-tag>..HEAD --oneline` to get all commits since the last release.
|
||||
|
||||
### 6. Write the changelog entry
|
||||
|
||||
Analyze every commit message. Group them mentally into features, improvements, and bug fixes. Then produce **brief, user-facing bullet points** following these rules:
|
||||
|
||||
- **Style:** Match the existing changelog tone exactly — short, imperative sentences starting with a verb (Fix, Add, Allow, Show, Improve, Support…). Keep the trailing periods (the existing changelog uses them).
|
||||
- **Brevity:** Each bullet should be one short sentence, around 80 characters when possible. No implementation details. No commit hashes.
|
||||
- **Selection:** Only include changes that matter to end users. Skip CI, build infra, submodule bumps, code style, refactors, and intermediate WIP commits. Collapse many related commits (e.g. a dozen image-editor commits) into one or two bullets.
|
||||
- **Ordering:** Features first, then improvements, then bug fixes.
|
||||
- **Quantity:** Aim for 4-12 bullets total depending on the amount of changes.
|
||||
|
||||
### 7. Format and insert into changelog.txt
|
||||
|
||||
Use this exact format (date is today in DD.MM.YY):
|
||||
|
||||
```
|
||||
<version> [beta ](DD.MM.YY)
|
||||
|
||||
- Bullet one.
|
||||
- Bullet two.
|
||||
```
|
||||
|
||||
Prepend the new entry at the very top of `changelog.txt`, separated by a blank line from the previous first entry. Use the Edit tool.
|
||||
|
||||
**Never delete or edit existing entries**, even if the new stable entry merges bullets from prior beta(s). Prior beta blocks remain as historical record for beta-track users.
|
||||
|
||||
**For stable releases spanning prior beta(s):** the entry should cover everything since the last **stable** release (not just since the last beta), including noteworthy beta-shipped features, trimmed as needed to stay within 4–12 bullets.
|
||||
|
||||
### 8. Wait for approval
|
||||
|
||||
After writing the entry to `changelog.txt` (step 7), tell the user the changelog has been updated and ask them to review it in the IDE. They can edit it directly and tell you to continue, or tell you what to change in chat. Do **not** print the full entry in chat — the file itself is the review surface.
|
||||
|
||||
**Do NOT proceed until the user explicitly approves.**
|
||||
|
||||
### 9. Run set_version
|
||||
|
||||
Once approved, run the `set_version` script from the repository root. On Windows:
|
||||
|
||||
```
|
||||
.\Telegram\build\set_version.bat <version_arg>
|
||||
```
|
||||
|
||||
Where `<version_arg>` is formatted as the `set_version` script expects:
|
||||
- Stable: `6.7.0` or `6.7`
|
||||
- Beta: `6.7.0.beta`
|
||||
|
||||
Verify the script exits successfully (exit code 0). If it fails, show the error and stop.
|
||||
|
||||
### 10. Commit
|
||||
|
||||
Stage all changes and create a commit. The commit message format:
|
||||
|
||||
**First line:**
|
||||
- For stable: `Version <major>.<minor>.` if patch is 0, otherwise `Version <major>.<minor>.<patch>.`
|
||||
- For beta: `Beta version <major>.<minor>.<patch>.`
|
||||
|
||||
**Then an empty line, then the changelog bullets.** Copy bullet lines from the changelog as-is. Only wrap lines that exceed 72 characters; shorter lines must stay on a single line. When wrapping is needed, break at logically correct places (between words/phrases) and indent continuation lines with two spaces.
|
||||
|
||||
Example commit message:
|
||||
```
|
||||
Beta version 6.6.3.
|
||||
|
||||
- Drawing tools in image editor
|
||||
(brush, marker, eraser, arrow).
|
||||
- Draw-to-reply button in media viewer.
|
||||
- Trim recorded voice messages.
|
||||
- Fix reorder freeze in chats list.
|
||||
```
|
||||
|
||||
Use a HEREDOC to pass the message to `git commit -a`.
|
||||
|
||||
### 11. Done
|
||||
|
||||
Run `git log -1` to show the resulting commit and confirm success.
|
||||
@@ -0,0 +1,506 @@
|
||||
---
|
||||
description: Implement a feature or fix using multi-agent workflow with fresh context at each phase
|
||||
allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task, AskUserQuestion, TodoWrite
|
||||
---
|
||||
|
||||
# Task - Multi-Agent Implementation Workflow
|
||||
|
||||
You orchestrate a multi-phase implementation workflow that uses fresh agent spawns to work within context window limits on a large codebase.
|
||||
|
||||
**Arguments:** `$ARGUMENTS` = "$ARGUMENTS"
|
||||
|
||||
If `$ARGUMENTS` is provided, it's the task description. If empty, ask the user what they want implemented.
|
||||
|
||||
## Overview
|
||||
|
||||
The workflow is organized around **projects**. Each project lives in `.ai/<project-name>/` and can contain multiple sequential **tasks** (labeled `a`, `b`, `c`, ... `z`).
|
||||
|
||||
Project structure:
|
||||
```
|
||||
.ai/<project-name>/
|
||||
about.md # Single source of truth for the entire project
|
||||
a/ # First task
|
||||
context.md # Gathered codebase context for this task
|
||||
plan.md # Implementation plan
|
||||
review1.md # Code review documents (up to 3)
|
||||
review2.md
|
||||
review3.md
|
||||
b/ # Follow-up task
|
||||
context.md
|
||||
plan.md
|
||||
review1.md
|
||||
c/ # Another follow-up task
|
||||
...
|
||||
```
|
||||
|
||||
- `about.md` is the project-level blueprint — a single comprehensive document describing what this project does and how it works, written as if everything is already fully implemented. It contains no temporal state ("current state", "pending changes", "not yet implemented"). It is **rewritten** (not appended to) each time a new task starts, incorporating the new task's changes as if they were always part of the design.
|
||||
- Each task folder (`a/`, `b/`, ...) contains self-contained files for that task. The task's `context.md` carries all task-specific information: what specifically needs to change, the delta from the current codebase, gathered file references and code patterns. Planning, implementation, and review agents only read the current task's folder.
|
||||
|
||||
## Phase 0: Setup
|
||||
|
||||
**Record the current time now** (using `Get-Date` in PowerShell or equivalent) and store it as `$START_TIME`. You will use this at the end to display total elapsed time.
|
||||
|
||||
⚠️ **CRITICAL: Follow-up detection MUST happen FIRST, before anything else.**
|
||||
|
||||
### Step 0a: Follow-up detection (MANDATORY — do this BEFORE understanding the task)
|
||||
|
||||
Extract the first word/token from `$ARGUMENTS` (everything before the first space or newline). Call it `FIRST_TOKEN`.
|
||||
|
||||
Then run these TWO commands using the Bash tool, IN PARALLEL, right now:
|
||||
1. `ls .ai/` — to see all existing project names
|
||||
2. `ls .ai/<FIRST_TOKEN>/about.md` — to check if this specific project exists
|
||||
|
||||
**Evaluate the results:**
|
||||
- If command 2 **succeeds** (the file exists): this is a **follow-up task**. The project name is `FIRST_TOKEN`. The task description is everything in `$ARGUMENTS` AFTER `FIRST_TOKEN` (strip leading whitespace).
|
||||
- If command 2 **fails** (file not found): this is a **new project**. The full `$ARGUMENTS` is the task description.
|
||||
|
||||
**Do NOT proceed to step 0b until you have run these commands and determined follow-up vs new.**
|
||||
|
||||
### Step 0b: Project setup
|
||||
|
||||
**For new projects:**
|
||||
- Using the list from command 1, pick a unique short name (1-2 lowercase words, hyphen-separated) that doesn't collide with existing projects.
|
||||
- Create `.ai/<project-name>/` and `.ai/<project-name>/a/`.
|
||||
- Set current task letter = `a`.
|
||||
|
||||
**For follow-up tasks:**
|
||||
- Scan `.ai/<project-name>/` for existing task folders (`a/`, `b/`, ...). Find the latest one (highest letter).
|
||||
- The previous task letter = that highest letter.
|
||||
- The new task letter = next letter in sequence.
|
||||
- Create `.ai/<project-name>/<new-letter>/`.
|
||||
|
||||
Then proceed to Phase 1 (Context Gathering) in both cases. Follow-up tasks do NOT skip context gathering — they go through a modified version of it.
|
||||
|
||||
## Phase 1: Context Gathering
|
||||
|
||||
### For New Projects (task letter = `a`)
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt structure:
|
||||
|
||||
```
|
||||
You are a context-gathering agent for a large C++ codebase (Telegram Desktop).
|
||||
|
||||
TASK: <paste the user's task description here>
|
||||
|
||||
YOUR JOB: Read AGENTS.md, inspect the codebase, find ALL files and code relevant to this task, and write two documents.
|
||||
|
||||
Steps:
|
||||
1. Read AGENTS.md for project conventions and build instructions.
|
||||
2. Search the codebase for files, classes, functions, and patterns related to the task.
|
||||
3. Read all potentially relevant files. Be thorough - read more rather than less.
|
||||
4. For each relevant file, note:
|
||||
- File path
|
||||
- Relevant line ranges
|
||||
- What the code does and how it relates to the task
|
||||
- Key data structures, function signatures, patterns used
|
||||
5. Look for similar existing features that could serve as a reference implementation.
|
||||
6. Check api.tl if the task involves Telegram API.
|
||||
7. Check .style files if the task involves UI.
|
||||
8. Check lang.strings if the task involves user-visible text.
|
||||
|
||||
Write TWO files:
|
||||
|
||||
### File 1: .ai/<project-name>/about.md
|
||||
|
||||
NOTE: This file is NOT used by any agent in the current task. It exists solely as a starting point for a FUTURE follow-up task's context gatherer. No planning, implementation, or review agent will ever read it. Only the context-gathering agent of the next follow-up task reads about.md (together with the latest context.md) to produce a fresh context.md for that next task.
|
||||
|
||||
Write it as if the project is already fully implemented and working. It should contain:
|
||||
- **Project**: What this project does (feature description, goals, scope)
|
||||
- **Architecture**: High-level architectural decisions, which modules are involved, how they interact
|
||||
- **Key Design Decisions**: Important choices made about the approach
|
||||
- **Relevant Codebase Areas**: Which parts of the codebase this project touches, key types and APIs involved
|
||||
|
||||
Do NOT include temporal state like "Current State", "Pending Changes", "Not yet implemented", "TODO", or any other framing that distinguishes between "done" and "not done". Describe the project as a complete, coherent whole — as if everything is already working. This is a project overview, not a status tracker. Task-specific work belongs exclusively in context.md.
|
||||
|
||||
### File 2: .ai/<project-name>/a/context.md
|
||||
|
||||
This is the task-specific implementation context. This is the PRIMARY document — all downstream agents (planning, implementation, review) will read ONLY this file. It must be completely self-contained. It should contain:
|
||||
- **Task Description**: The full task restated clearly
|
||||
- **Relevant Files**: Every file path with line ranges and descriptions of what's there
|
||||
- **Key Code Patterns**: How similar things are done in the codebase (with code snippets)
|
||||
- **Data Structures**: Relevant types, structs, classes
|
||||
- **API Methods**: Any TL schema methods involved (copied from api.tl)
|
||||
- **UI Styles**: Any relevant style definitions
|
||||
- **Localization**: Any relevant string keys
|
||||
- **Build Info**: Build command and any special notes
|
||||
- **Reference Implementations**: Similar features that can serve as templates
|
||||
|
||||
Be extremely thorough. Another agent with NO prior context will read this file and must be able to understand everything needed to implement the task.
|
||||
```
|
||||
|
||||
After this agent completes, read both `about.md` and `a/context.md` to verify they were written properly.
|
||||
|
||||
### For Follow-up Tasks (task letter = `b`, `c`, ...)
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt structure:
|
||||
|
||||
```
|
||||
You are a context-gathering agent for a follow-up task on an existing project in a large C++ codebase (Telegram Desktop).
|
||||
|
||||
NEW TASK: <paste the follow-up task description here>
|
||||
|
||||
YOUR JOB: Read the existing project state, gather any additional context needed, and produce fresh documents for the new task.
|
||||
|
||||
Steps:
|
||||
1. Read AGENTS.md for project conventions and build instructions.
|
||||
2. Read .ai/<project-name>/about.md — this is the project-level blueprint describing everything done so far.
|
||||
3. Read .ai/<project-name>/<previous-letter>/context.md — this is the previous task's gathered context.
|
||||
4. Understand what has already been implemented by reading the actual source files referenced in about.md and the previous context.
|
||||
5. Based on the NEW TASK description, search the codebase for any ADDITIONAL files, classes, functions, and patterns that are relevant to the new task but not already covered.
|
||||
6. Read all newly relevant files thoroughly.
|
||||
|
||||
Write TWO files:
|
||||
|
||||
### File 1: .ai/<project-name>/about.md (REWRITE)
|
||||
|
||||
NOTE: This file is NOT used by any agent in the current task. It exists solely as a starting point for a FUTURE follow-up task's context gatherer. No planning, implementation, or review agent will ever read it. You are rewriting it now so that the next follow-up has an accurate project overview to start from.
|
||||
|
||||
REWRITE this file (not append). The new about.md must be a single coherent document that describes the project as if everything — including this new task's changes — is already fully implemented and working.
|
||||
|
||||
It should incorporate:
|
||||
- Everything from the old about.md that is still accurate and relevant
|
||||
- The new task's functionality described as part of the project (not as "changes to make")
|
||||
- Any changed design decisions or architectural updates from the new task requirements
|
||||
|
||||
It should NOT contain:
|
||||
- Any temporal state: "Current State", "Pending Changes", "TODO", "Not yet implemented"
|
||||
- History of how requirements changed between tasks
|
||||
- References to "the old approach" vs "the new approach"
|
||||
- Task-by-task changelog or timeline
|
||||
- Any distinction between "what was done before" and "what this task adds"
|
||||
- Information that contradicts the new task requirements (if the new task changes direction, the about.md should reflect the NEW direction as if it was always the plan)
|
||||
|
||||
Think of about.md as "the complete description of what this project does and how it works." Someone reading it should understand the full project as a finished product, without knowing it went through multiple tasks.
|
||||
|
||||
### File 2: .ai/<project-name>/<new-letter>/context.md
|
||||
|
||||
This is the PRIMARY document — all downstream agents (planning, implementation, review) will read ONLY this file. It must be completely self-contained. about.md will NOT be available to them.
|
||||
|
||||
It should contain:
|
||||
- **Task Description**: The new task restated clearly, with enough project background (from about.md and previous context.md) that an implementation agent can understand it without reading any other .ai/ files
|
||||
- **Relevant Files**: Every file path with line ranges relevant to THIS task (including files modified by previous tasks and any newly relevant files)
|
||||
- **Key Code Patterns**: How similar things are done in the codebase
|
||||
- **Data Structures**: Relevant types, structs, classes
|
||||
- **API Methods**: Any TL schema methods involved
|
||||
- **UI Styles**: Any relevant style definitions
|
||||
- **Localization**: Any relevant string keys
|
||||
- **Build Info**: Build command and any special notes
|
||||
- **Reference Implementations**: Similar features that can serve as templates
|
||||
|
||||
Be extremely thorough. Another agent with NO prior context will read ONLY this file and must be able to understand everything needed to implement the new task. Do NOT assume the reader has seen about.md or any previous task files. The context.md is the single source of truth for all downstream agents — it must include all relevant project background, not just the delta.
|
||||
```
|
||||
|
||||
After this agent completes, read both `about.md` and `<new-letter>/context.md` to verify they were written properly.
|
||||
|
||||
## Phase 2: Planning
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt structure:
|
||||
|
||||
```
|
||||
You are a planning agent. You must create a detailed implementation plan.
|
||||
|
||||
Read these files:
|
||||
- .ai/<project-name>/<letter>/context.md - Contains all gathered context for this task
|
||||
- Then read the specific source files referenced in context.md to understand the code deeply.
|
||||
|
||||
Think carefully about the implementation approach.
|
||||
|
||||
Create a detailed plan in: .ai/<project-name>/<letter>/plan.md
|
||||
|
||||
The plan.md should contain:
|
||||
|
||||
## Task
|
||||
<one-line summary>
|
||||
|
||||
## Approach
|
||||
<high-level description of the implementation approach>
|
||||
|
||||
## Files to Modify
|
||||
<list of files that will be created or modified>
|
||||
|
||||
## Files to Create
|
||||
<list of new files, if any>
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
Each step must be specific enough that an agent can execute it without ambiguity:
|
||||
- Exact file paths
|
||||
- Exact function names
|
||||
- What code to add/modify/remove
|
||||
- Where exactly in the file (after which function, in which class, etc.)
|
||||
|
||||
Number every step. Group steps into phases if there are more than ~8 steps.
|
||||
|
||||
### Phase 1: <name>
|
||||
1. <specific step>
|
||||
2. <specific step>
|
||||
...
|
||||
|
||||
### Phase 2: <name> (if needed)
|
||||
...
|
||||
|
||||
## Build Verification
|
||||
- Build command to run
|
||||
- Expected outcome
|
||||
|
||||
## Status
|
||||
- [ ] Phase 1: <name>
|
||||
- [ ] Phase 2: <name> (if applicable)
|
||||
- [ ] Build verification
|
||||
- [ ] Code review
|
||||
```
|
||||
|
||||
After this agent completes, read `plan.md` to verify it was written properly.
|
||||
|
||||
## Phase 3: Plan Assessment
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt structure:
|
||||
|
||||
```
|
||||
You are a plan assessment agent. Review and refine an implementation plan.
|
||||
|
||||
Read these files:
|
||||
- .ai/<project-name>/<letter>/context.md
|
||||
- .ai/<project-name>/<letter>/plan.md
|
||||
- Then read the actual source files referenced to verify the plan makes sense.
|
||||
|
||||
Carefully assess the plan:
|
||||
|
||||
1. **Correctness**: Are the file paths and line references accurate? Does the plan reference real functions and types?
|
||||
2. **Completeness**: Are there missing steps? Edge cases not handled?
|
||||
3. **Code quality**: Will the plan minimize code duplication? Does it follow existing codebase patterns from AGENTS.md?
|
||||
4. **Design**: Could the approach be improved? Are there better patterns already used in the codebase?
|
||||
5. **Phase sizing**: Each phase should be implementable by a single agent in one session. If a phase has more than ~8-10 substantive code changes, split it further.
|
||||
|
||||
Update plan.md with your refinements. Keep the same structure but:
|
||||
- Fix any inaccuracies
|
||||
- Add missing steps
|
||||
- Improve the approach if you found better patterns
|
||||
- Ensure phases are properly sized for single-agent execution
|
||||
- Add a line at the top of the Status section: `Phases: <N>` indicating how many implementation phases there are
|
||||
- Add `Assessed: yes` at the bottom of the file
|
||||
|
||||
If the plan is small enough for a single agent (roughly <=8 steps), mark it as a single phase.
|
||||
```
|
||||
|
||||
After this agent completes, read `plan.md` to verify it was assessed.
|
||||
|
||||
## Phase 4: Implementation
|
||||
|
||||
Now read `plan.md` yourself to understand the phases.
|
||||
|
||||
For each phase in the plan that is not yet marked as done, spawn an implementation agent (Task tool, subagent_type=`general-purpose`):
|
||||
|
||||
```
|
||||
You are an implementation agent working on phase <N> of an implementation plan.
|
||||
|
||||
Read these files first:
|
||||
- .ai/<project-name>/<letter>/context.md - Full codebase context
|
||||
- .ai/<project-name>/<letter>/plan.md - Implementation plan
|
||||
|
||||
Then read the source files you'll be modifying.
|
||||
|
||||
YOUR TASK: Implement ONLY Phase <N> from the plan:
|
||||
<paste the specific phase steps here>
|
||||
|
||||
Rules:
|
||||
- Follow the plan precisely
|
||||
- Follow AGENTS.md coding conventions (no comments except complex algorithms, use auto, empty line before closing brace, etc.)
|
||||
- Do NOT modify .ai/ files except to update the Status section in plan.md
|
||||
- When done, update plan.md Status section: change `- [ ] Phase <N>: ...` to `- [x] Phase <N>: ...`
|
||||
- Do NOT work on other phases
|
||||
|
||||
When finished, report what you did and any issues encountered.
|
||||
```
|
||||
|
||||
After each implementation agent returns:
|
||||
1. Read `plan.md` to check the status was updated.
|
||||
2. If more phases remain, spawn the next implementation agent.
|
||||
3. If all phases are done, proceed to build verification.
|
||||
|
||||
## Phase 5: Build Verification
|
||||
|
||||
Only run this phase if the task involved modifying project source code (not just docs or config).
|
||||
|
||||
Spawn a build verification agent (Task tool, subagent_type=`general-purpose`):
|
||||
|
||||
```
|
||||
You are a build verification agent.
|
||||
|
||||
Read these files:
|
||||
- .ai/<project-name>/<letter>/context.md
|
||||
- .ai/<project-name>/<letter>/plan.md
|
||||
|
||||
The implementation is complete. Your job is to build the project and fix any build errors.
|
||||
|
||||
Steps:
|
||||
1. Run (from repository root): cmake --build ./out --config Debug --target Telegram
|
||||
2. If the build succeeds, update plan.md: change `- [ ] Build verification` to `- [x] Build verification`
|
||||
3. If the build fails:
|
||||
a. Read the error messages carefully
|
||||
b. Read the relevant source files
|
||||
c. Fix the errors in accordance with the plan and AGENTS.md conventions
|
||||
d. Rebuild and repeat until the build passes
|
||||
e. Update plan.md status when done
|
||||
|
||||
Rules:
|
||||
- Only fix build errors, do not refactor or improve code
|
||||
- Follow AGENTS.md conventions
|
||||
- If build fails with file-locked errors (C1041, LNK1104), STOP and report - do not retry
|
||||
|
||||
When finished, report the build result.
|
||||
```
|
||||
|
||||
After the build agent returns, read `plan.md` to confirm the final status. Then proceed to Phase 6.
|
||||
|
||||
## Phase 6: Code Review Loop
|
||||
|
||||
After build verification passes, run up to 3 review-fix iterations to improve code quality. Set iteration counter `R = 1`.
|
||||
|
||||
### Review Loop
|
||||
|
||||
```
|
||||
LOOP:
|
||||
1. Spawn review agent (Step 6a) with iteration R
|
||||
2. Read review<R>.md verdict:
|
||||
- "APPROVED" → go to FINISH
|
||||
- Has improvement suggestions → spawn fix agent (Step 6b)
|
||||
3. After fix agent completes and build passes:
|
||||
R = R + 1
|
||||
If R > 3 → go to FINISH (stop iterating, accept current state)
|
||||
Otherwise → go to step 1
|
||||
|
||||
FINISH:
|
||||
- Update plan.md: change `- [ ] Code review` to `- [x] Code review`
|
||||
- Proceed to Completion
|
||||
```
|
||||
|
||||
### Step 6a: Code Review Agent
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`):
|
||||
|
||||
```
|
||||
You are a code review agent for Telegram Desktop (C++ / Qt).
|
||||
|
||||
Read these files:
|
||||
- .ai/<project-name>/<letter>/context.md - Codebase context
|
||||
- .ai/<project-name>/<letter>/plan.md - Implementation plan
|
||||
- REVIEW.md - Style and formatting rules to enforce
|
||||
<if R > 1, also read:>
|
||||
- .ai/<project-name>/<letter>/review<R-1>.md - Previous review (to see what was already addressed)
|
||||
|
||||
Then run `git diff` to see all uncommitted changes made by the implementation. Implementation agents do not commit, so `git diff` shows exactly the current feature's changes.
|
||||
|
||||
Then read the modified source files in full to understand changes in context.
|
||||
|
||||
Perform a thorough code review.
|
||||
|
||||
REVIEW CRITERIA (in order of importance):
|
||||
|
||||
1. **Correctness and safety**: Obvious logic errors, missing null checks at API boundaries, potential crashes, use-after-free, dangling references, race conditions. This is the highest priority — bugs and safety issues must be caught first. Do NOT nitpick internal code that relies on framework guarantees.
|
||||
|
||||
2. **Dead code**: Any code added or left behind that is never called or used, within the scope of the changes. Unused variables, unreachable branches, leftover scaffolding.
|
||||
|
||||
3. **Redundant changes**: Changes in the diff that have no functional effect — moving declarations or code blocks to a different location without reason, reformatting untouched code, reordering includes or fields with no purpose. Every line in the diff should serve the feature. If a file appears in `git diff` but contains only no-op rearrangements, flag it for revert.
|
||||
|
||||
4. **Code duplication**: Unnecessary repetition of logic that should be shared. Look for near-identical blocks that differ only in minor details and could be unified.
|
||||
|
||||
5. **Wrong placement**: Code added to a module where it doesn't logically belong. If another existing module is a clearly better fit for the new code, flag it. Consider the existing module boundaries and responsibilities visible in context.md.
|
||||
|
||||
6. **Function decomposition**: For longer functions (roughly 50+ lines), consider whether a logical sub-task could be cleanly extracted into a separate function. This is NOT a hard rule — a 100-line function that flows naturally and isn't easily divisible is perfectly fine. But sometimes even a 20-line function contains a clear isolated subtask that reads better as two 10-line functions. The key is to think about it each time: does extracting improve readability and reduce cognitive load, or does it just scatter logic across call sites for no real benefit? Only suggest extraction when there's a genuinely self-contained piece of logic with a clear name and purpose.
|
||||
|
||||
7. **Module structure**: Only in exceptional cases — if a large amount of newly added code (hundreds of lines) is logically distinct from the rest of its host module, suggest extracting it into a new module. But do NOT suggest new modules lightly: every module adds significant build overhead due to PCH and heavy template usage. Only suggest this when the new code is both large enough AND logically separated enough to justify it. At the same time, don't let modules grow into multi-thousand-line monoliths either.
|
||||
|
||||
8. **Style compliance**: Verify adherence to REVIEW.md rules (empty line before closing brace, operators at start of continuation lines, minimize type checks with direct cast instead of is+as, no if-with-initializer when simpler alternatives exist) and AGENTS.md conventions (no unnecessary comments, `auto` usage, no hardcoded sizes — must use .style definitions), etc.
|
||||
|
||||
IMPORTANT GUIDELINES:
|
||||
- Review ONLY the changes made, not pre-existing code in the repository.
|
||||
- Be pragmatic. Don't suggest changes for the sake of it. Each suggestion should have a clear, concrete benefit.
|
||||
- Don't suggest adding comments, docstrings, or type annotations — the codebase style avoids these.
|
||||
- Don't suggest error handling for impossible scenarios or over-engineering.
|
||||
|
||||
Write your review to: .ai/<project-name>/<letter>/review<R>.md
|
||||
|
||||
The review document should contain:
|
||||
|
||||
## Code Review - Iteration <R>
|
||||
|
||||
## Summary
|
||||
<1-2 sentence overall assessment>
|
||||
|
||||
## Verdict: <APPROVED or NEEDS_CHANGES>
|
||||
|
||||
<If APPROVED, stop here. Everything looks good.>
|
||||
|
||||
<If NEEDS_CHANGES, continue with:>
|
||||
|
||||
## Changes Required
|
||||
|
||||
### <Issue 1 title>
|
||||
- **Category**: <dead code | duplication | wrong placement | function decomposition | module structure | style | correctness>
|
||||
- **File(s)**: <file paths>
|
||||
- **Problem**: <clear description of what's wrong>
|
||||
- **Fix**: <specific description of what to change>
|
||||
|
||||
### <Issue 2 title>
|
||||
...
|
||||
|
||||
Keep the list focused. Only include issues that genuinely improve the code. If you find yourself listing more than ~5-6 issues, prioritize the most impactful ones.
|
||||
|
||||
When finished, report your verdict clearly as: APPROVED or NEEDS_CHANGES.
|
||||
```
|
||||
|
||||
After the review agent returns, read `review<R>.md`. If the verdict is APPROVED, proceed to Completion. If NEEDS_CHANGES, spawn the fix agent.
|
||||
|
||||
### Step 6b: Review Fix Agent
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`):
|
||||
|
||||
```
|
||||
You are a review fix agent. You implement improvements identified during code review.
|
||||
|
||||
Read these files:
|
||||
- .ai/<project-name>/<letter>/context.md - Codebase context
|
||||
- .ai/<project-name>/<letter>/plan.md - Original implementation plan
|
||||
- .ai/<project-name>/<letter>/review<R>.md - Code review with required changes
|
||||
|
||||
Then read the source files mentioned in the review.
|
||||
|
||||
YOUR TASK: Implement ALL changes listed in review<R>.md.
|
||||
|
||||
For each issue in the review:
|
||||
1. Read the relevant source file(s).
|
||||
2. Make the specified change.
|
||||
3. Verify the change makes sense in context.
|
||||
|
||||
After all changes are made:
|
||||
1. Build (from repository root): cmake --build ./out --config Debug --target Telegram
|
||||
2. If the build fails, fix build errors and rebuild until it passes.
|
||||
3. If build fails with file-locked errors (C1041, LNK1104), STOP and report - do not retry.
|
||||
|
||||
Rules:
|
||||
- Implement exactly the changes from the review, nothing more.
|
||||
- Follow AGENTS.md coding conventions.
|
||||
- Do NOT modify .ai/ files.
|
||||
|
||||
When finished, report what changes were made.
|
||||
```
|
||||
|
||||
After the fix agent returns, increment R and loop back to Step 6a (unless R > 3, in which case proceed to Completion).
|
||||
|
||||
## Completion
|
||||
|
||||
When all phases including build verification and code review are done:
|
||||
1. Read the final `plan.md` and report the summary to the user.
|
||||
2. Show which files were modified/created.
|
||||
3. Note any issues encountered during implementation.
|
||||
4. Summarize code review iterations: how many rounds, what was found and fixed, or if it was approved on first pass.
|
||||
5. Calculate and display the total elapsed time since `$START_TIME` (format as `Xh Ym Zs`, omitting zero components — e.g. `12m 34s` or `1h 5m 12s`).
|
||||
6. Remind the user of the project name so they can use `/task <project-name> <follow-up description>` for follow-up changes.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If any agent fails or gets stuck, report the issue to the user and ask how to proceed.
|
||||
- If context.md or plan.md is not written properly by an agent, re-spawn that agent with more specific instructions.
|
||||
- If build errors persist after the build agent's attempts, report the remaining errors to the user.
|
||||
- If a review fix agent introduces new build errors that it cannot resolve, report to the user.
|
||||
@@ -0,0 +1,656 @@
|
||||
---
|
||||
description: Implement a feature using multi-agent workflow, then iteratively test and fix it in-app
|
||||
allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task, AskUserQuestion, TodoWrite
|
||||
---
|
||||
|
||||
# WithTest - Multi-Agent Implementation + Testing Workflow
|
||||
|
||||
You orchestrate a multi-phase implementation workflow followed by an iterative testing/fixing loop. This is an extended version of `/task` that adds in-app programmatic testing after the build succeeds.
|
||||
|
||||
**Arguments:** `$ARGUMENTS` = "$ARGUMENTS"
|
||||
|
||||
If `$ARGUMENTS` is provided, it's the task description. If empty, ask the user what they want implemented.
|
||||
|
||||
## Overview
|
||||
|
||||
The workflow produces `.ai/<feature-name>/` containing:
|
||||
- `context.md` - Gathered codebase context relevant to the task
|
||||
- `plan.md` - Detailed implementation plan with phases and status
|
||||
- `testN.md` - Test plan for iteration N
|
||||
- `resultN.md` - Test result report for iteration N
|
||||
- `planN.md` - Fix plan for iteration N (if implementation bugs found)
|
||||
- `screenshots/` - Screenshots captured during test runs
|
||||
|
||||
Two major stages:
|
||||
1. **Implementation** (Phases 0-5) - same as `/task`
|
||||
2. **Testing Loop** (Phase 6) - iterative test-plan → test-do → test-run → test-check cycle
|
||||
|
||||
---
|
||||
|
||||
## STAGE 1: IMPLEMENTATION (Phases 0-5)
|
||||
|
||||
These phases are identical to the `/task` workflow.
|
||||
|
||||
### Phase 0: Setup
|
||||
|
||||
1. Understand the task from `$ARGUMENTS` or ask the user.
|
||||
2. **Follow-up detection:** Check if `$ARGUMENTS` starts with a task name (the first word/token before any whitespace or newline). Look for `.ai/<that-name>/` directory:
|
||||
- If `.ai/<that-name>/` exists AND contains both `context.md` and `plan.md`, this is a **follow-up task**. Read both files. The rest of `$ARGUMENTS` (after the task name) is the follow-up task description describing what additional changes are needed.
|
||||
- If no matching directory exists, this is a **new task** - proceed normally.
|
||||
3. For new tasks: check existing folders in `.ai/` to pick a unique short name (1-2 lowercase words, hyphen-separated) and create `.ai/<feature-name>/`.
|
||||
4. For follow-up tasks: the folder already exists, skip creation.
|
||||
|
||||
### Follow-up Task Flow
|
||||
|
||||
When a follow-up task is detected (existing `.ai/<name>/` with `context.md` and `plan.md`):
|
||||
|
||||
1. Skip Phase 1 (Context Gathering) - context already exists.
|
||||
2. Skip Phase 2 (Planning) - original plan already exists.
|
||||
3. Go directly to **Phase 2F (Follow-up Planning)** instead of Phase 3.
|
||||
|
||||
**Phase 2F: Follow-up Planning**
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt:
|
||||
|
||||
```
|
||||
You are a planning agent for a follow-up task on an existing implementation.
|
||||
|
||||
Read these files:
|
||||
- .ai/<feature-name>/context.md - Previously gathered codebase context
|
||||
- .ai/<feature-name>/plan.md - Previous implementation plan (already completed)
|
||||
|
||||
Then read the source files referenced in context.md and plan.md to understand what was already implemented.
|
||||
|
||||
FOLLOW-UP TASK: <paste the follow-up task description here>
|
||||
|
||||
The previous plan was already implemented and tested. Now there are follow-up changes needed.
|
||||
|
||||
YOUR JOB:
|
||||
1. Understand what was already done from plan.md (look at the completed phases).
|
||||
2. Read the actual source files to see the current state of the code.
|
||||
3. If context.md needs updates for the follow-up task (new files relevant, new patterns needed), update it with additional sections marked "## Follow-up Context (iteration 2)" or similar.
|
||||
4. Create a NEW follow-up plan. Update plan.md by:
|
||||
- Keep the existing content as history (do NOT delete it)
|
||||
- Add a new section at the end:
|
||||
|
||||
---
|
||||
## Follow-up Task
|
||||
<description>
|
||||
|
||||
## Follow-up Approach
|
||||
<high-level description>
|
||||
|
||||
## Follow-up Files to Modify
|
||||
<list>
|
||||
|
||||
## Follow-up Implementation Steps
|
||||
|
||||
### Phase F1: <name>
|
||||
1. <specific step>
|
||||
2. ...
|
||||
|
||||
### Phase F2: <name> (if needed)
|
||||
...
|
||||
|
||||
## Follow-up Status
|
||||
Phases: <N>
|
||||
- [ ] Phase F1: <name>
|
||||
- [ ] Phase F2: <name> (if applicable)
|
||||
- [ ] Build verification
|
||||
- [ ] Testing
|
||||
Assessed: yes
|
||||
|
||||
Reason carefully. The follow-up plan should be self-contained enough that an implementation agent can execute it by reading context.md and the updated plan.md.
|
||||
```
|
||||
|
||||
After this agent completes, read `plan.md` to verify the follow-up plan was written. Then proceed to Phase 4 (Implementation), using the follow-up phases (F1, F2, etc.) instead of the original phases. After implementation and build verification, proceed to Stage 2 (Testing Loop) as normal.
|
||||
|
||||
### New Task Flow
|
||||
|
||||
When this is a new task (no existing folder), proceed with Phases 1-5 as described below.
|
||||
|
||||
### Phase 1: Context Gathering
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt structure:
|
||||
|
||||
```
|
||||
You are a context-gathering agent for a large C++ codebase (Telegram Desktop).
|
||||
|
||||
TASK: <paste the user's task description here>
|
||||
|
||||
YOUR JOB: Read CLAUDE.md, inspect the codebase, find ALL files and code relevant to this task, and write a comprehensive context document.
|
||||
|
||||
Steps:
|
||||
1. Read CLAUDE.md for project conventions and build instructions.
|
||||
2. Search the codebase for files, classes, functions, and patterns related to the task.
|
||||
3. Read all potentially relevant files. Be thorough - read more rather than less.
|
||||
4. For each relevant file, note:
|
||||
- File path
|
||||
- Relevant line ranges
|
||||
- What the code does and how it relates to the task
|
||||
- Key data structures, function signatures, patterns used
|
||||
5. Look for similar existing features that could serve as a reference implementation.
|
||||
6. Check api.tl if the task involves Telegram API.
|
||||
7. Check .style files if the task involves UI.
|
||||
8. Check lang.strings if the task involves user-visible text.
|
||||
|
||||
Write your findings to: .ai/<feature-name>/context.md
|
||||
|
||||
The context.md should contain:
|
||||
- **Task Description**: The full task restated clearly
|
||||
- **Relevant Files**: Every file path with line ranges and descriptions of what's there
|
||||
- **Key Code Patterns**: How similar things are done in the codebase (with code snippets)
|
||||
- **Data Structures**: Relevant types, structs, classes
|
||||
- **API Methods**: Any TL schema methods involved (copied from api.tl)
|
||||
- **UI Styles**: Any relevant style definitions
|
||||
- **Localization**: Any relevant string keys
|
||||
- **Build Info**: Build command and any special notes
|
||||
- **Reference Implementations**: Similar features that can serve as templates
|
||||
|
||||
Be extremely thorough. Another agent with NO prior context will read this file and must be able to understand everything needed to implement the task.
|
||||
```
|
||||
|
||||
After this agent completes, read `context.md` to verify it was written properly.
|
||||
|
||||
### Phase 2: Planning
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt structure:
|
||||
|
||||
```
|
||||
You are a planning agent. You must create a detailed implementation plan.
|
||||
|
||||
Read these files:
|
||||
- .ai/<feature-name>/context.md - Contains all gathered context
|
||||
- Then read the specific source files referenced in context.md to understand the code deeply.
|
||||
|
||||
Think carefully about the implementation approach.
|
||||
|
||||
Create a detailed plan in: .ai/<feature-name>/plan.md
|
||||
|
||||
The plan.md should contain:
|
||||
|
||||
## Task
|
||||
<one-line summary>
|
||||
|
||||
## Approach
|
||||
<high-level description of the implementation approach>
|
||||
|
||||
## Files to Modify
|
||||
<list of files that will be created or modified>
|
||||
|
||||
## Files to Create
|
||||
<list of new files, if any>
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
Each step must be specific enough that an agent can execute it without ambiguity:
|
||||
- Exact file paths
|
||||
- Exact function names
|
||||
- What code to add/modify/remove
|
||||
- Where exactly in the file (after which function, in which class, etc.)
|
||||
|
||||
Number every step. Group steps into phases if there are more than ~8 steps.
|
||||
|
||||
### Phase 1: <name>
|
||||
1. <specific step>
|
||||
2. <specific step>
|
||||
...
|
||||
|
||||
### Phase 2: <name> (if needed)
|
||||
...
|
||||
|
||||
## Build Verification
|
||||
- Build command to run
|
||||
- Expected outcome
|
||||
|
||||
## Status
|
||||
- [ ] Phase 1: <name>
|
||||
- [ ] Phase 2: <name> (if applicable)
|
||||
- [ ] Build verification
|
||||
- [ ] Testing
|
||||
```
|
||||
|
||||
After this agent completes, read `plan.md` to verify it was written properly.
|
||||
|
||||
### Phase 3: Plan Assessment
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`) with this prompt structure:
|
||||
|
||||
```
|
||||
You are a plan assessment agent. Review and refine an implementation plan.
|
||||
|
||||
Read these files:
|
||||
- .ai/<feature-name>/context.md
|
||||
- .ai/<feature-name>/plan.md
|
||||
- Then read the actual source files referenced to verify the plan makes sense.
|
||||
|
||||
Carefully assess the plan:
|
||||
|
||||
1. **Correctness**: Are the file paths and line references accurate? Does the plan reference real functions and types?
|
||||
2. **Completeness**: Are there missing steps? Edge cases not handled?
|
||||
3. **Code quality**: Will the plan minimize code duplication? Does it follow existing codebase patterns from CLAUDE.md?
|
||||
4. **Design**: Could the approach be improved? Are there better patterns already used in the codebase?
|
||||
5. **Phase sizing**: Each phase should be implementable by a single agent in one session. If a phase has more than ~8-10 substantive code changes, split it further.
|
||||
|
||||
Update plan.md with your refinements. Keep the same structure but:
|
||||
- Fix any inaccuracies
|
||||
- Add missing steps
|
||||
- Improve the approach if you found better patterns
|
||||
- Ensure phases are properly sized for single-agent execution
|
||||
- Add a line at the top of the Status section: `Phases: <N>` indicating how many implementation phases there are
|
||||
- Add `Assessed: yes` at the bottom of the file
|
||||
|
||||
If the plan is small enough for a single agent (roughly <=8 steps), mark it as a single phase.
|
||||
```
|
||||
|
||||
After this agent completes, read `plan.md` to verify it was assessed.
|
||||
|
||||
### Phase 4: Implementation
|
||||
|
||||
Now read `plan.md` yourself to understand the phases.
|
||||
|
||||
For each phase in the plan that is not yet marked as done, spawn an implementation agent (Task tool, subagent_type=`general-purpose`):
|
||||
|
||||
```
|
||||
You are an implementation agent working on phase <N> of an implementation plan.
|
||||
|
||||
Read these files first:
|
||||
- .ai/<feature-name>/context.md - Full codebase context
|
||||
- .ai/<feature-name>/plan.md - Implementation plan
|
||||
|
||||
Then read the source files you'll be modifying.
|
||||
|
||||
YOUR TASK: Implement ONLY Phase <N> from the plan:
|
||||
<paste the specific phase steps here>
|
||||
|
||||
Rules:
|
||||
- Follow the plan precisely
|
||||
- Follow CLAUDE.md coding conventions (no comments except complex algorithms, use auto, empty line before closing brace, etc.)
|
||||
- Do NOT modify .ai/ files except to update the Status section in plan.md
|
||||
- When done, update plan.md Status section: change `- [ ] Phase <N>: ...` to `- [x] Phase <N>: ...`
|
||||
- Do NOT work on other phases
|
||||
|
||||
When finished, report what you did and any issues encountered.
|
||||
```
|
||||
|
||||
After each implementation agent returns:
|
||||
1. Read `plan.md` to check the status was updated.
|
||||
2. If more phases remain, spawn the next implementation agent.
|
||||
3. If all phases are done, proceed to build verification.
|
||||
|
||||
### Phase 5: Build Verification
|
||||
|
||||
Spawn a build verification agent (Task tool, subagent_type=`general-purpose`):
|
||||
|
||||
```
|
||||
You are a build verification agent.
|
||||
|
||||
Read these files:
|
||||
- .ai/<feature-name>/context.md
|
||||
- .ai/<feature-name>/plan.md
|
||||
|
||||
The implementation is complete. Your job is to build the project and fix any build errors.
|
||||
|
||||
Steps:
|
||||
1. Run: cmake --build "c:\Telegram\tdesktop\out" --config Debug --target Telegram
|
||||
2. If the build succeeds, update plan.md: change `- [ ] Build verification` to `- [x] Build verification`
|
||||
3. If the build fails:
|
||||
a. Read the error messages carefully
|
||||
b. Read the relevant source files
|
||||
c. Fix the errors in accordance with the plan and CLAUDE.md conventions
|
||||
d. Rebuild and repeat until the build passes
|
||||
e. Update plan.md status when done
|
||||
|
||||
Rules:
|
||||
- Only fix build errors, do not refactor or improve code
|
||||
- Follow CLAUDE.md conventions
|
||||
- If build fails with file-locked errors (C1041, LNK1104), STOP and report - do not retry
|
||||
|
||||
When finished, report the build result.
|
||||
```
|
||||
|
||||
After the build agent returns, read `plan.md` to confirm build verification passed. If it did, proceed to Stage 2.
|
||||
|
||||
---
|
||||
|
||||
## STAGE 2: TESTING LOOP (Phase 6)
|
||||
|
||||
This stage iteratively tests the implementation in-app and fixes issues. It maintains an iteration counter `N` starting at 1.
|
||||
|
||||
**Key concept:** Since the project has tight coupling and no unit test infrastructure, we test by injecting `#ifdef _DEBUG` blocks into the app code that perform actions, write to `log.txt`, save screenshots, and call `Core::Quit()` when done. An agent then runs the app and observes the output.
|
||||
|
||||
### Git Submodule Awareness
|
||||
|
||||
Before ANY git operation (commit, stash, stash pop), the agent must:
|
||||
1. Run `git submodule status` to check for modified submodules.
|
||||
2. If submodules have changes, commit/stash those submodules FIRST, individually:
|
||||
```
|
||||
cd <submodule-path> && git add -A && git commit -m "[wip-N] test changes" && cd <repo-root>
|
||||
```
|
||||
or for stash:
|
||||
```
|
||||
cd <submodule-path> && git stash && cd <repo-root>
|
||||
```
|
||||
3. Then operate on the main repo.
|
||||
|
||||
### Step 6a: Test Plan (test-plan agent)
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`):
|
||||
|
||||
```
|
||||
You are a test-planning agent for Telegram Desktop (C++ / Qt).
|
||||
|
||||
Read these files:
|
||||
- .ai/<feature-name>/context.md
|
||||
- .ai/<feature-name>/plan.md
|
||||
<if N > 1, also include:>
|
||||
- .ai/<feature-name>/result<N-1>.md - Previous test result
|
||||
<if a planN.md triggered this iteration:>
|
||||
- .ai/<feature-name>/plan<trigger>.md - Fix plan that was just implemented
|
||||
|
||||
CURRENT ITERATION: <N>
|
||||
|
||||
YOUR TASKS:
|
||||
|
||||
1. **Commit current implementation changes.**
|
||||
- Run `git submodule status` to check for modified submodules.
|
||||
- If any submodules are dirty, go into each one and commit:
|
||||
`cd <submodule> && git add -A && git commit -m "[wip-<N>]" && cd <repo-root>`
|
||||
- Then in main repo: `git add -A && git commit -m "[wip-<N>]"`
|
||||
- Do NOT add files in .ai/ to the commit.
|
||||
|
||||
2. <If N > 1> **Restore previous test code.**
|
||||
- Run `git submodule status` and `git stash list` in any dirty submodules to check for stashed test code.
|
||||
- Pop submodule stashes first: `cd <submodule> && git stash pop && cd <repo-root>`
|
||||
- Then pop main repo stash: `git stash pop`
|
||||
- Read the previous test<N-1>.md to understand what was tested before.
|
||||
- Decide: reuse/modify existing test code or start fresh.
|
||||
|
||||
3. **Plan the test code.**
|
||||
Carefully design test code that will verify the implementation works correctly.
|
||||
|
||||
The test code must:
|
||||
- Be wrapped in `#ifdef _DEBUG` blocks so it only runs in Debug builds
|
||||
- Be injected at appropriate points in the app lifecycle (e.g., after main window shows, after chats load, etc.)
|
||||
- Write progress and results to a log file. Use a dedicated path like:
|
||||
`QFile logFile("c:/Telegram/tdesktop/.ai/<feature-name>/test_log.txt");`
|
||||
Open with `QIODevice::Append | QIODevice::Text`, write with QTextStream, and flush after every write.
|
||||
- Save screenshots where visual verification is needed:
|
||||
`widget->grab().save("c:/Telegram/tdesktop/.ai/<feature-name>/screenshots/<name>.png");`
|
||||
Log each screenshot save: `"SCREENSHOT: <full-path>"`
|
||||
- Use `QTimer::singleShot(...)` or deferred calls to schedule test steps after UI events settle
|
||||
- Call `Core::Quit()` when all test steps complete, so the app exits cleanly
|
||||
- Log `"TEST_COMPLETE"` right before `Core::Quit()` so the test-run agent knows testing finished
|
||||
- Log `"TEST_STEP: <description>"` before each major step for progress tracking
|
||||
- Log `"TEST_RESULT: PASS: <what>"` or `"TEST_RESULT: FAIL: <what> - <details>"` for each check
|
||||
|
||||
Consider what needs testing:
|
||||
- Does the new UI appear correctly?
|
||||
- Do interactions work (clicks, navigation)?
|
||||
- Does data flow correctly?
|
||||
- Are there edge cases to verify?
|
||||
|
||||
4. **Write the test plan** to `.ai/<feature-name>/test<N>.md` containing:
|
||||
|
||||
## Test Iteration <N>
|
||||
## What We're Testing
|
||||
<description of what this test verifies>
|
||||
|
||||
## Test Steps
|
||||
1. <step>: what we do, what we expect, how we verify
|
||||
2. ...
|
||||
|
||||
## Code Injection Points
|
||||
- File: <path>, Location: <where in file>, Purpose: <what this block does>
|
||||
- ...
|
||||
|
||||
## Expected Log Output
|
||||
<example of what test_log.txt should contain if everything works>
|
||||
|
||||
## Expected Screenshots
|
||||
- <name>.png: should show <description>
|
||||
- ...
|
||||
|
||||
## Success Criteria
|
||||
- <criterion 1>
|
||||
- <criterion 2>
|
||||
- ...
|
||||
|
||||
When finished, report what test plan was created.
|
||||
```
|
||||
|
||||
### Step 6b: Test Implementation (test-do agent)
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`):
|
||||
|
||||
```
|
||||
You are a test implementation agent for Telegram Desktop (C++ / Qt).
|
||||
|
||||
Read these files:
|
||||
- .ai/<feature-name>/context.md
|
||||
- .ai/<feature-name>/plan.md
|
||||
- .ai/<feature-name>/test<N>.md - The test plan to implement
|
||||
|
||||
YOUR TASK: Implement the test code described in test<N>.md.
|
||||
|
||||
Rules:
|
||||
- ALL test code MUST be inside `#ifdef _DEBUG` blocks
|
||||
- Place test code at the injection points specified in the test plan
|
||||
- Make sure the screenshots folder exists: create `.ai/<feature-name>/screenshots/` directory
|
||||
- Delete any old test_log.txt before the test starts (in code, at the first test step)
|
||||
- Use QTimer::singleShot for delayed operations to let the UI settle
|
||||
- Flush log writes immediately (don't buffer)
|
||||
- End with logging "TEST_COMPLETE" and calling Core::Quit()
|
||||
- Follow CLAUDE.md coding conventions
|
||||
- Make sure the code compiles: run `cmake --build "c:\Telegram\tdesktop\out" --config Debug --target Telegram`
|
||||
- If build fails, fix errors and rebuild until it passes
|
||||
- If build fails with file-locked errors (C1041, LNK1104), STOP and report
|
||||
|
||||
When finished, report what test code was added and where.
|
||||
```
|
||||
|
||||
### Step 6c: Test Run (test-run agent)
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`):
|
||||
|
||||
```
|
||||
You are a test execution agent. You run the Telegram Desktop app and observe test output.
|
||||
|
||||
Read these files:
|
||||
- .ai/<feature-name>/test<N>.md - The test plan (so you know what to expect)
|
||||
|
||||
YOUR TASK: Run the built app and monitor test execution.
|
||||
|
||||
Steps:
|
||||
|
||||
1. **Prepare.**
|
||||
- Delete old test_log.txt if it exists: `del "c:\Telegram\tdesktop\docs\ai\work\<feature-name>\test_log.txt" 2>nul`
|
||||
- Ensure screenshots folder exists: `mkdir "c:\Telegram\tdesktop\docs\ai\work\<feature-name>\screenshots" 2>nul`
|
||||
|
||||
2. **Launch the app.**
|
||||
- Run in background: `start "" "c:\Telegram\tdesktop\out\Debug\Telegram.exe"`
|
||||
- Note the time of launch.
|
||||
|
||||
3. **Monitor test_log.txt in a polling loop.**
|
||||
- Every 5 seconds, read the log file to check for new output.
|
||||
- When you see `"SCREENSHOT: <path>"`, read the screenshot image file to visually verify it.
|
||||
- Track which TEST_STEP entries appear.
|
||||
- Track TEST_RESULT entries (PASS/FAIL).
|
||||
|
||||
4. **Detect completion or failure.**
|
||||
- **Success**: Log contains `"TEST_COMPLETE"` - the app should exit on its own shortly after.
|
||||
- **Crash**: The process disappears before `"TEST_COMPLETE"`. Check for crash dumps or error dialogs.
|
||||
- **Hang/Timeout**: If no new log output for 120 seconds and no `"TEST_COMPLETE"`, kill the process:
|
||||
`taskkill /IM Telegram.exe /F`
|
||||
- **No log at all**: If no test_log.txt appears within 60 seconds of launch, kill the process.
|
||||
|
||||
5. **After the process exits (or is killed), wait 5 seconds, then:**
|
||||
- Read the full final test_log.txt
|
||||
- Read all screenshot files saved during the test
|
||||
- Check for any leftover Telegram.exe processes: `tasklist /FI "IMAGENAME eq Telegram.exe"` and kill if needed
|
||||
|
||||
6. **Write the result report** to `.ai/<feature-name>/result<N>.md`:
|
||||
|
||||
## Test Result - Iteration <N>
|
||||
## Outcome: <PASS / FAIL / CRASH / TIMEOUT>
|
||||
|
||||
## Log Output
|
||||
<full contents of test_log.txt, or note that it was empty/missing>
|
||||
|
||||
## Screenshot Analysis
|
||||
- <name>.png: <description of what you see, whether it matches expectations from test<N>.md>
|
||||
- ...
|
||||
|
||||
## Test Results Summary
|
||||
- PASS: <list>
|
||||
- FAIL: <list>
|
||||
|
||||
## Issues Found
|
||||
<any problems observed, unexpected behavior, etc.>
|
||||
|
||||
## Raw Details
|
||||
<process exit code if available, timing information, any stderr output>
|
||||
|
||||
When finished, report the test outcome.
|
||||
```
|
||||
|
||||
After the test-run agent returns, read `result<N>.md`.
|
||||
|
||||
### Step 6d: Test Assessment (test-check agent)
|
||||
|
||||
Spawn an agent (Task tool, subagent_type=`general-purpose`):
|
||||
|
||||
```
|
||||
You are a test assessment agent. You analyze test results and decide next steps.
|
||||
|
||||
Read these files:
|
||||
- .ai/<feature-name>/context.md
|
||||
- .ai/<feature-name>/plan.md
|
||||
- .ai/<feature-name>/test<N>.md
|
||||
- .ai/<feature-name>/result<N>.md
|
||||
<if N > 1, also read previous test/result pairs for history>
|
||||
|
||||
Carefully analyze the test results.
|
||||
|
||||
DECIDE one of three outcomes:
|
||||
|
||||
### Outcome A: ALL TESTS PASS
|
||||
If all test results are PASS and screenshots look correct:
|
||||
1. Write to result<N>.md (append): `\n## Verdict: PASS`
|
||||
2. Report "ALL_TESTS_PASS" so the orchestrator knows to finish.
|
||||
|
||||
### Outcome B: TEST CODE NEEDS CHANGES
|
||||
If the test itself was flawed (wrong assertions, bad timing, insufficient waits, screenshot taken too early, wrong injection point, etc.) but the implementation seems correct:
|
||||
1. Describe what's wrong with the test and what to change.
|
||||
2. Make the changes directly to the test code in the source files.
|
||||
3. Rebuild: `cmake --build "c:\Telegram\tdesktop\out" --config Debug --target Telegram`
|
||||
4. If build fails with file-locked errors (C1041, LNK1104), STOP and report.
|
||||
5. Write the updated test description to `.ai/<feature-name>/test<N+1>.md` explaining what changed and why.
|
||||
6. Report "TEST_NEEDS_RERUN" so the orchestrator goes back to step 6c.
|
||||
|
||||
### Outcome C: IMPLEMENTATION HAS BUGS
|
||||
If the test results indicate actual bugs in the implementation (not test issues):
|
||||
1. Analyze what's wrong with the implementation.
|
||||
2. Write a fix plan to `.ai/<feature-name>/plan<N>.md`:
|
||||
|
||||
## Fix Plan - Iteration <N>
|
||||
## Problem
|
||||
<what the test revealed>
|
||||
|
||||
## Root Cause
|
||||
<analysis of why the implementation is wrong>
|
||||
|
||||
## Fix Steps
|
||||
1. <specific fix with file path, location, what to change>
|
||||
2. ...
|
||||
|
||||
3. Stash the test code (it will be restored later):
|
||||
- Run `git submodule status` and stash dirty submodules first:
|
||||
`cd <submodule> && git stash && cd <repo-root>`
|
||||
- Then: `git stash`
|
||||
4. Report "IMPLEMENTATION_NEEDS_FIX" so the orchestrator goes to re-implementation.
|
||||
|
||||
When finished, report your verdict clearly as one of: ALL_TESTS_PASS, TEST_NEEDS_RERUN, IMPLEMENTATION_NEEDS_FIX.
|
||||
```
|
||||
|
||||
### Orchestrator Loop Logic
|
||||
|
||||
After Phase 5 (build verification) succeeds, you (the orchestrator) run the testing loop:
|
||||
|
||||
```
|
||||
Set N = 1
|
||||
|
||||
LOOP:
|
||||
1. Spawn test-plan agent (Step 6a) with iteration N
|
||||
2. Spawn test-do agent (Step 6b) with iteration N
|
||||
3. Spawn test-run agent (Step 6c) with iteration N
|
||||
4. Spawn test-check agent (Step 6d) with iteration N
|
||||
5. Read the verdict:
|
||||
- "ALL_TESTS_PASS" → go to FINISH
|
||||
- "TEST_NEEDS_RERUN" →
|
||||
N = N + 1
|
||||
go to step 3 (skip 6a and 6b, test code was already updated by test-check)
|
||||
- "IMPLEMENTATION_NEEDS_FIX" →
|
||||
Spawn implementation fix agent (see below)
|
||||
N = N + 1
|
||||
go to step 1 (full restart: new commit, stash pop test code, etc.)
|
||||
6. Safety: if N > 5, stop and report to user - too many iterations.
|
||||
|
||||
FINISH:
|
||||
- Stash or revert all test code (#ifdef _DEBUG blocks):
|
||||
- git submodule status, stash submodules if dirty
|
||||
- git stash (to save test code separately, user may want it later)
|
||||
- Update plan.md: change `- [ ] Testing` to `- [x] Testing`
|
||||
- Report to user
|
||||
```
|
||||
|
||||
### Implementation Fix Agent
|
||||
|
||||
When test-check reports IMPLEMENTATION_NEEDS_FIX, spawn this agent:
|
||||
|
||||
```
|
||||
You are an implementation fix agent.
|
||||
|
||||
Read these files:
|
||||
- .ai/<feature-name>/context.md
|
||||
- .ai/<feature-name>/plan.md
|
||||
- .ai/<feature-name>/plan<N>.md - The fix plan from test assessment
|
||||
|
||||
Then read the source files mentioned in the fix plan.
|
||||
|
||||
YOUR TASK: Implement the fixes described in plan<N>.md.
|
||||
|
||||
Steps:
|
||||
1. Read and understand the fix plan.
|
||||
2. Make the specified code changes.
|
||||
3. Build: `cmake --build "c:\Telegram\tdesktop\out" --config Debug --target Telegram`
|
||||
4. Fix any build errors.
|
||||
5. If build fails with file-locked errors (C1041, LNK1104), STOP and report.
|
||||
|
||||
Rules:
|
||||
- Only make changes specified in the fix plan
|
||||
- Follow CLAUDE.md conventions
|
||||
- Do NOT touch test code or .ai/ files (except plan.md status if relevant)
|
||||
|
||||
When finished, report what was fixed.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Completion
|
||||
|
||||
When the testing loop finishes (ALL_TESTS_PASS or user stops it):
|
||||
1. Read the final `plan.md` and report full summary to the user.
|
||||
2. List all files modified/created by the implementation.
|
||||
3. Summarize test iterations: how many rounds, what was found and fixed.
|
||||
4. Note that test code is stashed (available via `git stash pop` if needed).
|
||||
5. Note any remaining concerns.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If any agent fails or gets stuck, report the issue to the user and ask how to proceed.
|
||||
- If context.md or plan.md is not written properly by an agent, re-spawn that agent with more specific instructions.
|
||||
- If build errors persist after agent attempts, report remaining errors to the user.
|
||||
- If the testing loop exceeds 5 iterations, stop and report - something fundamental may be wrong.
|
||||
- If the app crashes repeatedly, report to user - may need manual investigation.
|
||||
- If file-locked build errors occur at ANY point, stop immediately and ask user to close Telegram.exe.
|
||||
@@ -0,0 +1,11 @@
|
||||
param([string]$outPath)
|
||||
Add-Type -AssemblyName System.Windows.Forms
|
||||
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
||||
if ($img) {
|
||||
$img.Save($outPath, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
Write-Host "Saved to $outPath"
|
||||
exit 0
|
||||
} else {
|
||||
Write-Host "No image on clipboard"
|
||||
exit 1
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# Grab clipboard image on macOS and save as PNG.
|
||||
outPath="$1"
|
||||
if [ -z "$outPath" ]; then
|
||||
echo "Usage: grab_clipboard.sh <output.png>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
osascript -e '
|
||||
set theFile to POSIX file "'"$outPath"'"
|
||||
try
|
||||
set theImage to the clipboard as «class PNGf»
|
||||
on error
|
||||
return "no image"
|
||||
end try
|
||||
set fh to open for access theFile with write permission
|
||||
write theImage to fh
|
||||
close access fh
|
||||
return "ok"
|
||||
' 2>/dev/null | grep -q "ok"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Saved to $outPath"
|
||||
exit 0
|
||||
else
|
||||
echo "No image on clipboard"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,343 @@
|
||||
#!/usr/bin/env pwsh
|
||||
# Iterative Task Runner
|
||||
# Runs Claude Code in a loop to complete tasks from a taskplanner-created folder
|
||||
#
|
||||
# Usage: .\docs\ai\iterate.ps1 <featurename> [-MaxIterations N] [-Interactive] [-DryRun] [-SingleCommit] [-NoCommit]
|
||||
#
|
||||
# Arguments:
|
||||
# featurename Name of the folder in .ai/ containing prompt.md and tasks.json
|
||||
# -MaxIterations Maximum iterations before stopping (default: 50)
|
||||
# -Interactive Pause between iterations for user confirmation (default: auto/no pause)
|
||||
# -DryRun Show what would be executed without running
|
||||
# -SingleCommit Don't commit after each task, commit all changes at the end
|
||||
# -NoCommit Don't commit at all (no per-task commits, no final commit)
|
||||
|
||||
param(
|
||||
[Parameter(Position=0, Mandatory=$true)]
|
||||
[string]$FeatureName,
|
||||
|
||||
[int]$MaxIterations = 50,
|
||||
[switch]$Interactive,
|
||||
[switch]$DryRun,
|
||||
[switch]$SingleCommit,
|
||||
[switch]$NoCommit
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$RepoRoot = Resolve-Path (Join-Path $ScriptDir "..\..")
|
||||
$WorkDir = Join-Path $ScriptDir "work\$FeatureName"
|
||||
$PromptMd = Join-Path $WorkDir "prompt.md"
|
||||
$TasksJson = Join-Path $WorkDir "tasks.json"
|
||||
|
||||
$BuildOutputDir = Join-Path $RepoRoot "out\Debug"
|
||||
$TelegramExe = Join-Path $BuildOutputDir "Telegram.exe"
|
||||
$TelegramPdb = Join-Path $BuildOutputDir "Telegram.pdb"
|
||||
|
||||
function Format-Duration {
|
||||
param([int]$Seconds)
|
||||
|
||||
if ($Seconds -lt 60) {
|
||||
return "${Seconds}s"
|
||||
} elseif ($Seconds -lt 3600) {
|
||||
$min = [math]::Floor($Seconds / 60)
|
||||
$sec = $Seconds % 60
|
||||
return "${min}m ${sec}s"
|
||||
} else {
|
||||
$hr = [math]::Floor($Seconds / 3600)
|
||||
$min = [math]::Floor(($Seconds % 3600) / 60)
|
||||
$sec = $Seconds % 60
|
||||
return "${hr}h ${min}m ${sec}s"
|
||||
}
|
||||
}
|
||||
|
||||
function Test-BuildFilesUnlocked {
|
||||
$filesToCheck = @($TelegramExe, $TelegramPdb)
|
||||
|
||||
foreach ($file in $filesToCheck) {
|
||||
if (Test-Path $file) {
|
||||
try {
|
||||
Remove-Item $file -Force -ErrorAction Stop
|
||||
Write-Host "Removed: $file" -ForegroundColor DarkGray
|
||||
}
|
||||
catch {
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Red
|
||||
Write-Host " ERROR: Cannot delete build output" -ForegroundColor Red
|
||||
Write-Host " File is locked: $file" -ForegroundColor Red
|
||||
Write-Host "" -ForegroundColor Red
|
||||
Write-Host " Please close Telegram.exe and any" -ForegroundColor Red
|
||||
Write-Host " debugger, then try again." -ForegroundColor Red
|
||||
Write-Host "========================================" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
return $false
|
||||
}
|
||||
}
|
||||
}
|
||||
return $true
|
||||
}
|
||||
|
||||
function Show-ClaudeStream {
|
||||
param([string]$Line)
|
||||
|
||||
try {
|
||||
$obj = $Line | ConvertFrom-Json -ErrorAction Stop
|
||||
|
||||
switch ($obj.type) {
|
||||
"assistant" {
|
||||
if ($obj.message.content) {
|
||||
foreach ($block in $obj.message.content) {
|
||||
if ($block.type -eq "text") {
|
||||
Write-Host $block.text -ForegroundColor White
|
||||
}
|
||||
elseif ($block.type -eq "tool_use") {
|
||||
$summary = ""
|
||||
if ($block.input) {
|
||||
if ($block.input.file_path) {
|
||||
$summary = $block.input.file_path
|
||||
} elseif ($block.input.pattern) {
|
||||
$summary = $block.input.pattern
|
||||
} elseif ($block.input.command) {
|
||||
$cmd = $block.input.command
|
||||
if ($cmd.Length -gt 60) { $cmd = $cmd.Substring(0, 60) + "..." }
|
||||
$summary = $cmd
|
||||
} else {
|
||||
$inputStr = $block.input | ConvertTo-Json -Compress -Depth 1
|
||||
if ($inputStr.Length -gt 60) { $inputStr = $inputStr.Substring(0, 60) + "..." }
|
||||
$summary = $inputStr
|
||||
}
|
||||
}
|
||||
Write-Host "[Tool: $($block.name)] $summary" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"user" {
|
||||
# Tool results - skip verbose output
|
||||
}
|
||||
"result" {
|
||||
Write-Host "`n--- Session Complete ---" -ForegroundColor Cyan
|
||||
if ($obj.cost_usd) {
|
||||
Write-Host "Cost: `$$($obj.cost_usd)" -ForegroundColor DarkCyan
|
||||
}
|
||||
}
|
||||
"system" {
|
||||
# System messages - skip
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
# Not valid JSON, skip
|
||||
}
|
||||
}
|
||||
|
||||
# Verify feature folder exists
|
||||
if (-not (Test-Path $WorkDir)) {
|
||||
Write-Error "Feature folder not found: $WorkDir`nRun '/taskplanner $FeatureName' first to create it."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Verify required files exist
|
||||
foreach ($file in @($PromptMd, $TasksJson)) {
|
||||
if (-not (Test-Path $file)) {
|
||||
Write-Error "Required file not found: $file"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if ($SingleCommit -or $NoCommit) {
|
||||
$AfterImplementation = @"
|
||||
- Mark the task completed in tasks.json ("completed": true)
|
||||
- If new tasks emerged, add them to tasks.json
|
||||
"@
|
||||
$CommitRule = "- Do NOT commit changes after task is done, just mark it as done in tasks.json. Commit will be done when all tasks are complete, separately."
|
||||
} else {
|
||||
$AfterImplementation = @"
|
||||
- Mark the task completed in tasks.json ("completed": true)
|
||||
- Commit your changes
|
||||
- If new tasks emerged, add them to tasks.json
|
||||
"@
|
||||
$CommitRule = ""
|
||||
}
|
||||
|
||||
$Prompt = @"
|
||||
You are an autonomous coding agent working on: $FeatureName
|
||||
|
||||
Read these files for context:
|
||||
- .ai/$FeatureName/prompt.md - Detailed instructions and architecture
|
||||
- .ai/$FeatureName/tasks.json - Task list with completion status
|
||||
|
||||
Do exactly ONE task per iteration.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Read tasks.json and find the most suitable task to implement (it can be first uncompleted task or it can be some task in the middle, if it is better suited to be implemented right now, respecting dependencies)
|
||||
2. Plan the implementation carefully
|
||||
3. Implement that ONE task only
|
||||
4. After successful implementation:
|
||||
$AfterImplementation
|
||||
|
||||
## Critical Rules
|
||||
|
||||
- Only mark a task complete if you verified the work is done (build passes, etc.)
|
||||
- If stuck, document the issue in the task's notes field and move on
|
||||
- Do ONE task per iteration, then stop
|
||||
- NEVER try to commit files in .ai/
|
||||
$CommitRule
|
||||
|
||||
## Completion Signal
|
||||
|
||||
If ALL tasks in tasks.json have "completed": true, output exactly:
|
||||
===ALL_TASKS_COMPLETE===
|
||||
"@
|
||||
|
||||
$CommitPrompt = @"
|
||||
You are an autonomous coding agent. All tasks for "$FeatureName" are now complete.
|
||||
|
||||
Your job: Create a single commit with all the changes.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Run git status to see all modified files
|
||||
2. Run git diff to review the changes
|
||||
3. Create a commit with a short summary (aim for ~50 chars, max 76 chars) describing what was implemented
|
||||
4. The commit message should describe the overall feature/fix, not list individual changes
|
||||
|
||||
## Critical Rules
|
||||
|
||||
- NEVER try to commit files in .ai/
|
||||
- Use a concise commit message that captures the essence of the work done
|
||||
"@
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host " Iterative Task Runner" -ForegroundColor Cyan
|
||||
Write-Host " Feature: $FeatureName" -ForegroundColor Cyan
|
||||
Write-Host " Max iterations: $MaxIterations" -ForegroundColor Cyan
|
||||
Write-Host " Mode: $(if ($Interactive) { 'Interactive' } else { 'Auto' })" -ForegroundColor Cyan
|
||||
Write-Host " Commit: $(if ($NoCommit) { 'None' } elseif ($SingleCommit) { 'Single (at end)' } else { 'Per task' })" -ForegroundColor Cyan
|
||||
Write-Host " Working directory: $RepoRoot" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
if ($DryRun) {
|
||||
Write-Host "[DRY RUN] Would execute with prompt:" -ForegroundColor Yellow
|
||||
Write-Host $Prompt
|
||||
Write-Host ""
|
||||
Write-Host "Feature folder: $WorkDir" -ForegroundColor Yellow
|
||||
Write-Host "Prompt file: $PromptMd" -ForegroundColor Yellow
|
||||
Write-Host "Tasks file: $TasksJson" -ForegroundColor Yellow
|
||||
exit 0
|
||||
}
|
||||
|
||||
Push-Location $RepoRoot
|
||||
|
||||
$ScriptStartTime = Get-Date
|
||||
$IterationTimes = @()
|
||||
|
||||
try {
|
||||
for ($i = 1; $i -le $MaxIterations; $i++) {
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Yellow
|
||||
Write-Host " Iteration $i of $MaxIterations" -ForegroundColor Yellow
|
||||
Write-Host "========================================" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
|
||||
if (-not (Test-BuildFilesUnlocked)) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
$IterationStartTime = Get-Date
|
||||
|
||||
claude --dangerously-skip-permissions --verbose -p $Prompt --output-format stream-json 2>&1 | ForEach-Object {
|
||||
Show-ClaudeStream $_
|
||||
}
|
||||
|
||||
$IterationEndTime = Get-Date
|
||||
$IterationDuration = [int]($IterationEndTime - $IterationStartTime).TotalSeconds
|
||||
$IterationTimes += $IterationDuration
|
||||
Write-Host "Iteration time: $(Format-Duration $IterationDuration)" -ForegroundColor DarkCyan
|
||||
|
||||
# Check task status after each run
|
||||
$tasks = Get-Content $TasksJson | ConvertFrom-Json
|
||||
$incomplete = @($tasks.tasks | Where-Object { -not $_.completed })
|
||||
$inProgress = @($tasks.tasks | Where-Object { $_.started -and -not $_.completed })
|
||||
|
||||
if ($incomplete.Count -eq 0) {
|
||||
if ($SingleCommit -and -not $NoCommit) {
|
||||
$i++
|
||||
if ($i -le $MaxIterations) {
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Yellow
|
||||
Write-Host " Final commit iteration" -ForegroundColor Yellow
|
||||
Write-Host "========================================" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
|
||||
$CommitStartTime = Get-Date
|
||||
|
||||
claude --dangerously-skip-permissions --verbose -p $CommitPrompt --output-format stream-json 2>&1 | ForEach-Object {
|
||||
Show-ClaudeStream $_
|
||||
}
|
||||
|
||||
$CommitEndTime = Get-Date
|
||||
$CommitDuration = [int]($CommitEndTime - $CommitStartTime).TotalSeconds
|
||||
$IterationTimes += $CommitDuration
|
||||
Write-Host "Commit time: $(Format-Duration $CommitDuration)" -ForegroundColor DarkCyan
|
||||
} else {
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Red
|
||||
Write-Host " Max iterations reached before commit" -ForegroundColor Red
|
||||
Write-Host " Run manually: git add . && git commit" -ForegroundColor Red
|
||||
Write-Host "========================================" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
$TotalTime = [int]((Get-Date) - $ScriptStartTime).TotalSeconds
|
||||
$AvgTime = if ($IterationTimes.Count -gt 0) { [int](($IterationTimes | Measure-Object -Sum).Sum / $IterationTimes.Count) } else { 0 }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Green
|
||||
Write-Host " ALL TASKS COMPLETE!" -ForegroundColor Green
|
||||
Write-Host " Feature: $FeatureName" -ForegroundColor Green
|
||||
Write-Host " Iterations: $($IterationTimes.Count)" -ForegroundColor Green
|
||||
Write-Host " Total time: $(Format-Duration $TotalTime)" -ForegroundColor Green
|
||||
Write-Host " Avg per iteration: $(Format-Duration $AvgTime)" -ForegroundColor Green
|
||||
Write-Host "========================================" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Remaining tasks: $($incomplete.Count)" -ForegroundColor Cyan
|
||||
if ($inProgress.Count -gt 0) {
|
||||
Write-Host "In progress: $($inProgress[0].title)" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
if ($Interactive) {
|
||||
Write-Host "Press Enter to continue, Ctrl+C to stop..." -ForegroundColor Cyan
|
||||
Read-Host
|
||||
} else {
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
}
|
||||
|
||||
$TotalTime = [int]((Get-Date) - $ScriptStartTime).TotalSeconds
|
||||
$AvgTime = if ($IterationTimes.Count -gt 0) { [int](($IterationTimes | Measure-Object -Sum).Sum / $IterationTimes.Count) } else { 0 }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Red
|
||||
Write-Host " Max iterations ($MaxIterations) reached" -ForegroundColor Red
|
||||
Write-Host " Check tasks.json for remaining tasks" -ForegroundColor Red
|
||||
Write-Host " Total time: $(Format-Duration $TotalTime)" -ForegroundColor Red
|
||||
Write-Host " Avg per iteration: $(Format-Duration $AvgTime)" -ForegroundColor Red
|
||||
Write-Host "========================================" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
exit 1
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
---
|
||||
description: For tasks requiring sending Telegram server API requests or working with generated API types.
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Telegram Desktop API Usage
|
||||
|
||||
## API Schema
|
||||
|
||||
The API definitions are described using [TL Language]\(https:/core.telegram.org/mtproto/TL) in two main schema files:
|
||||
|
||||
1. **`Telegram/SourceFiles/mtproto/scheme/mtproto.tl`**
|
||||
* Defines the core MTProto protocol types and methods used for basic communication, encryption, authorization, service messages, etc.
|
||||
* Some fundamental types and methods from this schema (like basic types, RPC calls, containers) are often implemented directly in the C++ MTProto core (`SourceFiles/mtproto/`) and may be skipped during the C++ code generation phase.
|
||||
* Other parts of `mtproto.tl` might still be processed by the code generator.
|
||||
|
||||
2. **`Telegram/SourceFiles/mtproto/scheme/api.tl`**
|
||||
* Defines the higher-level Telegram API layer, including all the methods and types related to chat functionality, user profiles, messages, channels, stickers, etc.
|
||||
* This is the primary schema used when making functional API requests within the application.
|
||||
|
||||
Both files use the same TL syntax to describe API methods (functions) and types (constructors).
|
||||
|
||||
## Code Generation
|
||||
|
||||
A custom code generation tool processes `api.tl` (and parts of `mtproto.tl`) to create corresponding C++ classes and types. These generated headers are typically included via the Precompiled Header (PCH) for the main `Telegram` project.
|
||||
|
||||
Generated types often follow the pattern `MTP[Type]` (e.g., `MTPUser`, `MTPMessage`) and methods correspond to functions within the `MTP` namespace or related classes (e.g., `MTPmessages_SendMessage`).
|
||||
|
||||
## Making API Requests
|
||||
|
||||
API requests are made using a standard pattern involving the `api()` object (providing access to the `MTP::Instance`), the generated `MTP...` request object, callback handlers for success (`.done()`) and failure (`.fail()`), and the `.send()` method.
|
||||
|
||||
Here's the general structure:
|
||||
|
||||
```cpp
|
||||
// Include necessary headers if not already in PCH
|
||||
|
||||
// Obtain the API instance (usually via api() or MTP::Instance::Get())
|
||||
api().request(MTPnamespace_MethodName(
|
||||
// Constructor arguments based on the api.tl definition for the method
|
||||
MTP_flags(flags_value), // Use MTP_flags if the method has flags
|
||||
MTP_inputPeer(peer), // Use MTP_... types for parameters
|
||||
MTP_string(messageText),
|
||||
MTP_long(randomId),
|
||||
// ... other arguments matching the TL definition
|
||||
MTP_vector<MTPMessageEntity>() // Example for a vector argument
|
||||
)).done([=]\(const MTPResponseType &result) {
|
||||
// Handle the successful response (result).
|
||||
// 'result' will be of the C++ type corresponding to the TL type
|
||||
// specified after the '=' in the api.tl method definition.
|
||||
// How to access data depends on whether the TL type has one or multiple constructors:
|
||||
|
||||
// 1. Multiple Constructors (e.g., User = User | UserEmpty):
|
||||
// Use .match() with lambdas for each constructor:
|
||||
result.match([&]\(const MTPDuser &data) {
|
||||
/* use data.vfirst_name().v, etc. */
|
||||
}, [&]\(const MTPDuserEmpty &data) {
|
||||
/* handle empty user */
|
||||
});
|
||||
|
||||
// Alternatively, check the type explicitly and use the constructor getter:
|
||||
if (result.type() == mtpc_user) {
|
||||
const auto &data = result.c_user(); // Asserts if type is not mtpc_user!
|
||||
// use data.vfirst_name().v
|
||||
} else if (result.type() == mtpc_userEmpty) {
|
||||
const auto &data = result.c_userEmpty();
|
||||
// handle empty user
|
||||
}
|
||||
|
||||
// 2. Single Constructor (e.g., Messages = messages { msgs: vector<Message> }):
|
||||
// Use .match() with a single lambda:
|
||||
result.match([&]\(const MTPDmessages &data) { /* use data.vmessages().v */ });
|
||||
|
||||
// Or check the type explicitly and use the constructor getter:
|
||||
if (result.type() == mtpc_messages) {
|
||||
const auto &data = result.c_messages(); // Asserts if type is not mtpc_messages!
|
||||
// use data.vmessages().v
|
||||
}
|
||||
|
||||
// Or use the shortcut .data() for single-constructor types:
|
||||
const auto &data = result.data(); // Only works for single-constructor types!
|
||||
// use data.vmessages().v
|
||||
|
||||
}).fail([=]\(const MTP::Error &error) {
|
||||
// Handle the API error (error).
|
||||
// 'error' is an MTP::Error object containing the error code (error.type())
|
||||
// and description (error.description()). Check for specific error strings.
|
||||
if (error.type() == u"FLOOD_WAIT_X"_q) {
|
||||
// Handle flood wait
|
||||
} else {
|
||||
Ui::show(Box<InformBox>(Lang::Hard::ServerError())); // Example generic error handling
|
||||
}
|
||||
}).handleFloodErrors().send(); // handleFloodErrors() is common, then send()
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
|
||||
* Always refer to `Telegram/SourceFiles/mtproto/scheme/api.tl` for the correct method names, parameters (names and types), and response types.
|
||||
* Use the generated `MTP...` types/classes for request parameters (e.g., `MTP_int`, `MTP_string`, `MTP_bool`, `MTP_vector`, `MTPInputUser`, etc.) and response handling.
|
||||
* The `.done()` lambda receives the specific C++ `MTP...` type corresponding to the TL return type.
|
||||
* For types with **multiple constructors** (e.g., `User = User | UserEmpty`), use `result.match([&]\(const MTPDuser &d){ ... }, [&]\(const MTPDuserEmpty &d){ ... })` to handle each case, or check `result.type() == mtpc_user` / `mtpc_userEmpty` and call the specific `result.c_user()` / `result.c_userEmpty()` getter (which asserts on type mismatch).
|
||||
* For types with a **single constructor** (e.g., `Messages = messages{...}`), you can use `result.match([&]\(const MTPDmessages &d){ ... })` with one lambda, or check `type()` and call `c_messages()`, or use the shortcut `result.data()` to access the fields directly.
|
||||
* The `.fail()` lambda receives an `MTP::Error` object. Check `error.type()` against known error strings (often defined as constants or using `u"..."_q` literals).
|
||||
* Directly construct the `MTPnamespace_MethodName(...)` object inside `request()`.
|
||||
* Include `.handleFloodErrors()` before `.send()` for standard flood wait handling.
|
||||
@@ -1,164 +0,0 @@
|
||||
---
|
||||
description: For tasks requiring changing or adding user facing phrases and text parts.
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Telegram Desktop Localization
|
||||
|
||||
## Coding Style Note
|
||||
|
||||
**Use `auto`:** In the actual codebase, variable types are almost always deduced using `auto` (or `const auto`, `const auto &`) rather than being written out explicitly. Examples in this guide may use explicit types for clarity, but prefer `auto` in practice.
|
||||
|
||||
```cpp
|
||||
// Prefer this:
|
||||
auto currentTitle = tr::lng_settings_title(tr::now);
|
||||
auto nameProducer = GetNameProducer(); // Returns rpl::producer<...>
|
||||
|
||||
// Instead of this:
|
||||
QString currentTitle = tr::lng_settings_title(tr::now);
|
||||
rpl::producer<QString> nameProducer = GetNameProducer();
|
||||
```
|
||||
|
||||
## String Resource File
|
||||
|
||||
Base user-facing English strings are defined in the `lang.strings` file:
|
||||
|
||||
`Telegram/Resources/langs/lang.strings`
|
||||
|
||||
This file uses a key-value format with named placeholders:
|
||||
|
||||
```
|
||||
"lng_settings_title" = "Settings";
|
||||
"lng_confirm_delete_item" = "Are you sure you want to delete {item_name}?";
|
||||
"lng_files_selected" = "{count} files selected"; // Simple count example (see Pluralization)
|
||||
```
|
||||
|
||||
Placeholders are enclosed in curly braces, e.g., `{name}`, `{user}`. A special placeholder `{count}` is used for pluralization rules.
|
||||
|
||||
### Pluralization
|
||||
|
||||
For keys that depend on a number (using the `{count}` placeholder), English typically requires two forms: singular and plural. These are defined in `lang.strings` using `#one` and `#other` suffixes:
|
||||
|
||||
```
|
||||
"lng_files_selected#one" = "{count} file selected";
|
||||
"lng_files_selected#other" = "{count} files selected";
|
||||
```
|
||||
|
||||
While only `#one` and `#other` are defined in the base `lang.strings`, the code generation process creates C++ accessors for all six CLDR plural categories (`#zero`, `#one`, `#two`, `#few`, `#many`, `#other`) to support languages with more complex pluralization rules.
|
||||
|
||||
## Translation Process
|
||||
|
||||
While `lang.strings` provides the base English text and the keys, the actual translations are managed via Telegram's translations platform (translations.telegram.org) and loaded dynamically at runtime from the API. The keys from `lang.strings` (including the `#one`/`#other` variants) are used on the platform.
|
||||
|
||||
## Code Generation
|
||||
|
||||
A code generation tool processes `lang.strings` to create C++ structures and accessors within the `tr` namespace. These allow type-safe access to strings and handling of placeholders and pluralization. Generated keys typically follow the pattern `tr::lng_key_name`.
|
||||
|
||||
## String Usage in Code
|
||||
|
||||
Strings are accessed in C++ code using the generated objects within the `tr::` namespace. There are two main ways to use them: reactively (returning an `rpl::producer`) or immediately (returning the current value).
|
||||
|
||||
### 1. Reactive Usage (rpl::producer)
|
||||
|
||||
Calling a generated string function directly returns a reactive producer, typically `rpl::producer<QString>`. This producer automatically updates its value whenever the application language changes.
|
||||
|
||||
```cpp
|
||||
// Key: "settings_title" = "Settings";
|
||||
auto titleProducer = tr::lng_settings_title(); // Type: rpl::producer<QString>
|
||||
|
||||
// Key: "confirm_delete_item" = "Are you sure you want to delete {item_name}?";
|
||||
auto itemNameProducer = /* ... */; // Type: rpl::producer<QString>
|
||||
auto confirmationProducer = tr::lng_confirm_delete_item( // Type: rpl::producer<QString>
|
||||
tr::now, // NOTE: tr::now is NOT passed here for reactive result
|
||||
lt_item_name,
|
||||
std::move(itemNameProducer)); // Placeholder producers should be moved
|
||||
```
|
||||
|
||||
### 2. Immediate Usage (Current Value)
|
||||
|
||||
Passing `tr::now` as the first argument retrieves the string's current value in the active language (typically as a `QString`).
|
||||
|
||||
```cpp
|
||||
// Key: "settings_title" = "Settings";
|
||||
auto currentTitle = tr::lng_settings_title(tr::now); // Type: QString
|
||||
|
||||
// Key: "confirm_delete_item" = "Are you sure you want to delete {item_name}?";
|
||||
const auto currentItemName = QString("My Document"); // Type: QString
|
||||
auto currentConfirmation = tr::lng_confirm_delete_item( // Type: QString
|
||||
tr::now, // Pass tr::now for immediate value
|
||||
lt_item_name, currentItemName); // Placeholder value is a direct QString (or convertible)
|
||||
```
|
||||
|
||||
### 3. Placeholders (`{tag}`)
|
||||
|
||||
Placeholders like `{item_name}` are replaced by providing arguments after `tr::now` (for immediate) or as the initial arguments (for reactive). A corresponding `lt_tag_name` constant is passed before the value.
|
||||
|
||||
* **Immediate:** Pass the direct value (e.g., `QString`, `int`).
|
||||
* **Reactive:** Pass an `rpl::producer` of the corresponding type (e.g., `rpl::producer<QString>`). Remember to `std::move` the producer or use `rpl::duplicate` if you need to reuse the original producer afterwards.
|
||||
|
||||
### 4. Pluralization (`{count}`)
|
||||
|
||||
Keys using `{count}` require a numeric value for the `lt_count` placeholder. The correct plural form (`#zero`, `#one`, ..., `#other`) is automatically selected based on this value and the current language rules.
|
||||
|
||||
* **Immediate (`tr::now`):** Pass a `float64` or `int` (which is auto-converted to `float64`).
|
||||
```cpp
|
||||
int count = 1;
|
||||
auto filesText = tr::lng_files_selected(tr::now, lt_count, count); // Type: QString
|
||||
count = 5;
|
||||
filesText = tr::lng_files_selected(tr::now, lt_count, count); // Uses "files_selected#other"
|
||||
```
|
||||
|
||||
* **Reactive:** Pass an `rpl::producer<float64>`. Use the `tr::to_count()` helper to convert an `rpl::producer<int>` or wrap a single value.
|
||||
```cpp
|
||||
// From an existing int producer:
|
||||
auto countProducer = /* ... */; // Type: rpl::producer<int>
|
||||
auto filesTextProducer = tr::lng_files_selected( // Type: rpl::producer<QString>
|
||||
lt_count,
|
||||
countProducer | tr::to_count()); // Use tr::to_count() for conversion
|
||||
|
||||
// From a single int value wrapped reactively:
|
||||
int currentCount = 5;
|
||||
auto filesTextProducerSingle = tr::lng_files_selected( // Type: rpl::producer<QString>
|
||||
lt_count,
|
||||
rpl::single(currentCount) | tr::to_count());
|
||||
// Alternative for single values (less common): rpl::single(currentCount * 1.)
|
||||
```
|
||||
|
||||
### 5. Custom Projectors
|
||||
|
||||
An optional final argument can be a projector function (like `Ui::Text::Upper` or `Ui::Text::WithEntities`) to transform the output.
|
||||
|
||||
* If the projector returns `OutputType`, the string function returns `OutputType` (immediate) or `rpl::producer<OutputType>` (reactive).
|
||||
* Placeholder values must match the projector's *input* requirements. For `Ui::Text::WithEntities`, placeholders expect `TextWithEntities` (immediate) or `rpl::producer<TextWithEntities>` (reactive).
|
||||
|
||||
```cpp
|
||||
// Immediate with Ui::Text::WithEntities projector
|
||||
// Key: "user_posted_photo" = "{user} posted a photo";
|
||||
const auto userName = TextWithEntities{ /* ... */ }; // Type: TextWithEntities
|
||||
auto message = tr::lng_user_posted_photo( // Type: TextWithEntities
|
||||
tr::now,
|
||||
lt_user,
|
||||
userName, // Must be TextWithEntities
|
||||
Ui::Text::WithEntities); // Projector
|
||||
|
||||
// Reactive with Ui::Text::WithEntities projector
|
||||
auto userNameProducer = /* ... */; // Type: rpl::producer<TextWithEntities>
|
||||
auto messageProducer = tr::lng_user_posted_photo( // Type: rpl::producer<TextWithEntities>
|
||||
lt_user,
|
||||
std::move(userNameProducer), // Move placeholder producers
|
||||
Ui::Text::WithEntities); // Projector
|
||||
```
|
||||
|
||||
## Key Summary
|
||||
|
||||
* Keys are defined in `Resources/langs/lang.strings` using `{tag}` placeholders.
|
||||
* Plural keys use `{count}` and have `#one`/`#other` variants in `lang.strings`.
|
||||
* Access keys via `tr::lng_key_name(...)` in C++.
|
||||
* Call with `tr::now` as the first argument for the immediate `QString` (or projected type).
|
||||
* Call without `tr::now` for the reactive `rpl::producer<QString>` (or projected type).
|
||||
* Provide placeholder values (`lt_tag_name, value`) matching the usage (direct value for immediate, `rpl::producer` for reactive). Producers should typically be moved via `std::move`.
|
||||
* For `{count}`:
|
||||
* Immediate: Pass `int` or `float64`.
|
||||
* Reactive: Pass `rpl::producer<float64>`, typically by converting an `int` producer using `| tr::to_count()`.
|
||||
* Optional projector function as the last argument modifies the output type and required placeholder types.
|
||||
* Actual translations are loaded at runtime from the API.
|
||||
@@ -1,216 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# RPL (Reactive Programming Library) Guide
|
||||
|
||||
## Coding Style Note
|
||||
|
||||
**Use `auto`:** In the actual codebase, variable types are almost always deduced using `auto` (or `const auto`, `const auto &`) rather than being written out explicitly. Examples in this guide may use explicit types for clarity, but prefer `auto` in practice.
|
||||
|
||||
```cpp
|
||||
// Prefer this:
|
||||
auto intProducer = rpl::single(123);
|
||||
const auto &lifetime = existingLifetime;
|
||||
|
||||
// Instead of this:
|
||||
rpl::producer<int> intProducer = rpl::single(123);
|
||||
const rpl::lifetime &lifetime = existingLifetime;
|
||||
|
||||
// Sometimes needed if deduction is ambiguous or needs help:
|
||||
auto user = std::make_shared<UserData>();
|
||||
auto data = QByteArray::fromHex("...");
|
||||
```
|
||||
|
||||
## Introduction
|
||||
|
||||
RPL is the reactive programming library used in this project, residing in the `rpl::` namespace. It allows handling asynchronous streams of data over time.
|
||||
|
||||
The core concept is the `rpl::producer`, which represents a stream of values that can be generated over a certain lifetime.
|
||||
|
||||
## Producers: `rpl::producer<Type, Error = no_error>`
|
||||
|
||||
The fundamental building block is `rpl::producer<Type, Error>`. It produces values of `Type` and can optionally signal an error of type `Error`. By default, `Error` is `rpl::no_error`, indicating that the producer does not explicitly handle error signaling through this mechanism.
|
||||
|
||||
```cpp
|
||||
// A producer that emits integers.
|
||||
auto intProducer = /* ... */; // Type: rpl::producer<int>
|
||||
|
||||
// A producer that emits strings and can potentially emit a CustomError.
|
||||
auto stringProducerWithError = /* ... */; // Type: rpl::producer<QString, CustomError>
|
||||
```
|
||||
|
||||
Producers are typically lazy; they don't start emitting values until someone subscribes to them.
|
||||
|
||||
## Lifetime Management: `rpl::lifetime`
|
||||
|
||||
Reactive pipelines have a limited duration, managed by `rpl::lifetime`. An `rpl::lifetime` object essentially holds a collection of cleanup callbacks. When the lifetime ends (either explicitly destroyed or goes out of scope), these callbacks are executed, tearing down the associated pipeline and freeing resources.
|
||||
|
||||
```cpp
|
||||
rpl::lifetime myLifetime;
|
||||
// ... later ...
|
||||
// myLifetime is destroyed, cleanup happens.
|
||||
|
||||
// Or, pass a lifetime instance to manage a pipeline's duration.
|
||||
rpl::lifetime &parentLifetime = /* ... get lifetime from context ... */;
|
||||
```
|
||||
|
||||
## Starting a Pipeline: `rpl::start_...`
|
||||
|
||||
To consume values from a producer, you start a pipeline using one of the `rpl::start_...` methods. These methods subscribe to the producer and execute callbacks for the events they handle.
|
||||
|
||||
The most common method is `rpl::on_next`:
|
||||
|
||||
```cpp
|
||||
auto counter = /* ... */; // Type: rpl::producer<int>
|
||||
rpl::lifetime lifetime;
|
||||
|
||||
// Counter is consumed here, use std::move if it's an l-value.
|
||||
std::move(
|
||||
counter
|
||||
) | rpl::on_next([=]\(int nextValue) {
|
||||
// Process the next integer value emitted by the producer.
|
||||
qDebug() << "Received: " << nextValue;
|
||||
}, lifetime); // Pass the lifetime to manage the subscription.
|
||||
// Note: `counter` is now in a moved-from state and likely invalid.
|
||||
|
||||
// If you need to start the same producer multiple times, duplicate it:
|
||||
// rpl::duplicate(counter) | rpl::on_next(...);
|
||||
|
||||
// If you DON'T pass a lifetime to a start_... method:
|
||||
auto counter2 = /* ... */; // Type: rpl::producer<int>
|
||||
rpl::lifetime subscriptionLifetime = std::move(
|
||||
counter2
|
||||
) | rpl::on_next([=]\(int nextValue) { /* ... */ });
|
||||
// The returned lifetime MUST be stored. If it's discarded immediately,
|
||||
// the subscription stops instantly.
|
||||
// `counter2` is also moved-from here.
|
||||
```
|
||||
|
||||
Other variants allow handling errors (`_error`) and completion (`_done`):
|
||||
|
||||
```cpp
|
||||
auto dataStream = /* ... */; // Type: rpl::producer<QString, Error>
|
||||
rpl::lifetime lifetime;
|
||||
|
||||
// Assuming dataStream might be used again, we duplicate it for the first start.
|
||||
// If it's the only use, std::move(dataStream) would be preferred.
|
||||
rpl::duplicate(
|
||||
dataStream
|
||||
) | rpl::on_error([=]\(Error &&error) {
|
||||
// Handle the error signaled by the producer.
|
||||
qDebug() << "Error: " << error.text();
|
||||
}, lifetime);
|
||||
|
||||
// Using dataStream again, perhaps duplicated again or moved if last use.
|
||||
rpl::duplicate(
|
||||
dataStream
|
||||
) | rpl::on_done([=] {
|
||||
// Execute when the producer signals it's finished emitting values.
|
||||
qDebug() << "Stream finished.";
|
||||
}, lifetime);
|
||||
|
||||
// Last use of dataStream, so we move it.
|
||||
std::move(
|
||||
dataStream
|
||||
) | rpl::on_next_error_done(
|
||||
[=]\(QString &&value) { /* handle next value */ },
|
||||
[=]\(Error &&error) { /* handle error */ },
|
||||
[=] { /* handle done */ },
|
||||
lifetime);
|
||||
```
|
||||
|
||||
## Transforming Producers
|
||||
|
||||
RPL provides functions to create new producers by transforming existing ones:
|
||||
|
||||
* `rpl::map`: Transforms each value emitted by a producer.
|
||||
```cpp
|
||||
auto ints = /* ... */; // Type: rpl::producer<int>
|
||||
// The pipe operator often handles the move implicitly for chained transformations.
|
||||
auto strings = std::move(
|
||||
ints // Explicit move is safer
|
||||
) | rpl::map([](int value) {
|
||||
return QString::number(value * 2);
|
||||
}); // Emits strings like "0", "2", "4", ...
|
||||
```
|
||||
|
||||
* `rpl::filter`: Emits only the values from a producer that satisfy a condition.
|
||||
```cpp
|
||||
auto ints = /* ... */; // Type: rpl::producer<int>
|
||||
auto evenInts = std::move(
|
||||
ints // Explicit move is safer
|
||||
) | rpl::filter([](int value) {
|
||||
return (value % 2 == 0);
|
||||
}); // Emits only even numbers.
|
||||
```
|
||||
|
||||
## Combining Producers
|
||||
|
||||
You can combine multiple producers into one:
|
||||
|
||||
* `rpl::combine`: Combines the latest values from multiple producers whenever *any* of them emits a new value. Requires all producers to have emitted at least one value initially.
|
||||
While it produces a `std::tuple`, subsequent operators like `map`, `filter`, and `start_with_next` can automatically unpack this tuple into separate lambda arguments.
|
||||
```cpp
|
||||
auto countProducer = rpl::single(1); // Type: rpl::producer<int>
|
||||
auto textProducer = rpl::single(u"hello"_q); // Type: rpl::producer<QString>
|
||||
rpl::lifetime lifetime;
|
||||
|
||||
// rpl::combine takes producers by const-ref internally and duplicates,
|
||||
// so move/duplicate is usually not strictly needed here unless you
|
||||
// want to signal intent or manage the lifetime of p1/p2 explicitly.
|
||||
auto combined = rpl::combine(
|
||||
countProducer, // or rpl::duplicate(countProducer)
|
||||
textProducer // or rpl::duplicate(textProducer)
|
||||
);
|
||||
|
||||
// Starting the combined producer consumes it.
|
||||
// The lambda receives unpacked arguments, not the tuple itself.
|
||||
std::move(
|
||||
combined
|
||||
) | rpl::on_next([=]\(int count, const QString &text) {
|
||||
// No need for std::get<0>(latest), etc.
|
||||
qDebug() << "Combined: Count=" << count << ", Text=" << text;
|
||||
}, lifetime);
|
||||
|
||||
// This also works with map, filter, etc.
|
||||
std::move(
|
||||
combined
|
||||
) | rpl::filter([=]\(int count, const QString &text) {
|
||||
return count > 0 && !text.isEmpty();
|
||||
}) | rpl::map([=]\(int count, const QString &text) {
|
||||
return text.repeated(count);
|
||||
}) | rpl::on_next([=]\(const QString &result) {
|
||||
qDebug() << "Mapped & Filtered: " << result;
|
||||
}, lifetime);
|
||||
```
|
||||
|
||||
* `rpl::merge`: Merges the output of multiple producers of the *same type* into a single producer. It emits a value whenever *any* of the source producers emits a value.
|
||||
```cpp
|
||||
auto sourceA = /* ... */; // Type: rpl::producer<QString>
|
||||
auto sourceB = /* ... */; // Type: rpl::producer<QString>
|
||||
|
||||
// rpl::merge also duplicates internally.
|
||||
auto merged = rpl::merge(sourceA, sourceB);
|
||||
|
||||
// Starting the merged producer consumes it.
|
||||
std::move(
|
||||
merged
|
||||
) | rpl::on_next([=]\(QString &&value) {
|
||||
// Receives values from either sourceA or sourceB as they arrive.
|
||||
qDebug() << "Merged value: " << value;
|
||||
}, lifetime);
|
||||
```
|
||||
|
||||
## Key Concepts Summary
|
||||
|
||||
* Use `rpl::producer<Type, Error>` to represent streams of values.
|
||||
* Manage subscription duration using `rpl::lifetime`.
|
||||
* Pass `rpl::lifetime` to `rpl::start_...` methods.
|
||||
* If `rpl::lifetime` is not passed, **store the returned lifetime** to keep the subscription active.
|
||||
* Use operators like `| rpl::map`, `| rpl::filter` to transform streams.
|
||||
* Use `rpl::combine` or `rpl::merge` to combine streams.
|
||||
* When starting a chain (`std::move(producer) | rpl::map(...)`), explicitly move the initial producer.
|
||||
* These functions typically duplicate their input producers internally.
|
||||
* Starting a pipeline consumes the producer; use `
|
||||
@@ -1,154 +0,0 @@
|
||||
---
|
||||
description: For tasks requiring working with user facing UI components.
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Telegram Desktop UI Styling
|
||||
|
||||
## Style Definition Files
|
||||
|
||||
UI element styles (colors, fonts, paddings, margins, icons, etc.) are defined in `.style` files using a custom syntax. These files are located alongside the C++ source files they correspond to within specific UI component directories (e.g., `Telegram/SourceFiles/ui/chat/chat.style`).
|
||||
|
||||
Definitions from other `.style` files can be included using the `using` directive at the top of the file:
|
||||
```style
|
||||
using "ui/basic.style";
|
||||
using "ui/widgets/widgets.style";
|
||||
```
|
||||
|
||||
The central definition of named colors happens in `Telegram/SourceFiles/ui/colors.palette`. This file allows for theme generation and loading colors from various sources.
|
||||
|
||||
### Syntax Overview
|
||||
|
||||
1. **Built-in Types:** The syntax recognizes several base types inferred from the value assigned:
|
||||
* `int`: Integer numbers (e.g., `lineHeight: 20;`)
|
||||
* `bool`: Boolean values (e.g., `useShadow: true;`)
|
||||
* `pixels`: Pixel values, ending with `px` (e.g., `borderWidth: 1px;`). Generated as `int` in C++.
|
||||
* `color`: Named colors defined in `colors.palette` (e.g., `background: windowBg;`)
|
||||
* `icon`: Defined inline using a specific syntax (see below). Generates `style::icon`.
|
||||
* `margins`: Four pixel values for margins or padding. Requires `margins(top, right, bottom, left)` syntax (e.g., `margin: margins(10px, 5px, 10px, 5px);` or `padding: margins(8px, 8px, 8px, 8px);`). Generates `style::margins` (an alias for `QMargins`).
|
||||
* `size`: Two pixel values for width and height (e.g., `iconSize: size(16px, 16px);`). Generates `style::size`.
|
||||
* `point`: Two pixel values for x and y coordinates (e.g., `textPos: point(5px, 2px);`). Generates `style::point`.
|
||||
* `align`: Alignment keywords (e.g., `textAlign: align(center);` or `iconAlign: align(left);`). Generates `style::align`.
|
||||
* `font`: Font definitions (e.g., `font: font(14px semibold);`). Generates `style::font`.
|
||||
* `double`: Floating point numbers (e.g., `disabledOpacity: 0.5;`)
|
||||
|
||||
*Note on Borders:* Borders are typically defined using multiple fields like `border: pixels;` (for width) and `borderFg: color;` (for color), rather than a single CSS-like property.
|
||||
|
||||
2. **Structure Definition:** You can define complex data structures directly within the `.style` file:
|
||||
```style
|
||||
MyButtonStyle { // Defines a structure named 'MyButtonStyle'
|
||||
textPadding: margins; // Field 'textPadding' expects margins type
|
||||
icon: icon; // Field 'icon' of type icon
|
||||
height: pixels; // Field 'height' of type pixels
|
||||
}
|
||||
```
|
||||
This generates a `struct MyButtonStyle { ... };` inside the `namespace style`. Fields will have corresponding C++ types (`style::margins`, `style::icon`, `int`).
|
||||
|
||||
3. **Variable Definition & Inheritance:** Variables are defined using `name: value;` or `groupName { ... }`. They can be of built-in types or custom structures. Structures can be initialized inline or inherit from existing variables.
|
||||
|
||||
**Icon Definition Syntax:** Icons are defined inline using the `icon{...}` syntax. The generator probes for `.svg` files or `.png` files (including `@2x`, `@3x` variants) based on the provided path stem.
|
||||
```style
|
||||
// Single-part icon definition:
|
||||
myIconSearch: icon{{ "gui/icons/search", iconColor }};
|
||||
// Multi-part icon definition (layers drawn bottom-up):
|
||||
myComplexIcon: icon{
|
||||
{ "gui/icons/background", iconBgColor },
|
||||
{ "gui/icons/foreground", iconFgColor }
|
||||
};
|
||||
// Icon with path modifiers (PNG only for flips, SVG only for size):
|
||||
myFlippedIcon: icon{{ "gui/icons/arrow-flip_horizontal", arrowColor }};
|
||||
myResizedIcon: icon{{ "gui/icons/logo-128x128", logoColor }}; // Forces 128x128 for SVG
|
||||
```
|
||||
|
||||
**Other Variable Examples:**
|
||||
```style
|
||||
// Simple variables
|
||||
buttonHeight: 30px;
|
||||
activeButtonColor: buttonBgActive; // Named color from colors.palette
|
||||
|
||||
// Variable of a custom structure type, initialized inline
|
||||
defaultButton: MyButtonStyle {
|
||||
textPadding: margins(10px, 15px, 10px, 15px); // Use margins(...) syntax
|
||||
icon: myIconSearch; // Assign the previously defined icon variable
|
||||
height: buttonHeight; // Reference another variable
|
||||
}
|
||||
|
||||
// Another variable inheriting from 'defaultButton' and overriding/adding fields
|
||||
primaryButton: MyButtonStyle(defaultButton) {
|
||||
icon: myComplexIcon; // Override icon with the multi-part one
|
||||
backgroundColor: activeButtonColor; // Add a field not in MyButtonStyle definition
|
||||
}
|
||||
|
||||
// Style group (often used for specific UI elements)
|
||||
chatInput { // Example using separate border properties and explicit padding
|
||||
border: 1px; // Border width
|
||||
borderFg: defaultInputFieldBorder; // Border color (named color)
|
||||
padding: margins(5px, 10px, 5px, 10px); // Use margins(...) syntax for padding field
|
||||
backgroundColor: defaultChatBg; // Background color
|
||||
}
|
||||
```
|
||||
|
||||
## Code Generation
|
||||
|
||||
A code generation tool processes these `.style` files and `colors.palette` to create C++ objects.
|
||||
- The `using` directives resolve dependencies between `.style` files.
|
||||
- Custom structure definitions (like `MyButtonStyle`) generate corresponding `struct MyButtonStyle { ... };` within the `namespace style`.
|
||||
- Style variables/groups (like `defaultButton`, `primaryButton`, `chatInput`) are generated as objects/structs within the `st` namespace (e.g., `st::defaultButton`, `st::primaryButton`, `st::chatInput`). These generated structs contain members corresponding to the fields defined in the `.style` file.
|
||||
- Color objects are generated into the `st` namespace as well, based on their names in `colors.palette` (e.g., `st::windowBg`, `st::buttonBgActive`).
|
||||
- The generated header files for styles are placed in the `Telegram/SourceFiles/styles/` directory with a `style_` prefix (e.g., `styles/style_widgets.h` for `ui/widgets/widgets.style`). You include them like `#include "styles/style_widgets.h"`.
|
||||
|
||||
Generated C++ types correspond to the `.style` types: `style::color`, `style::font`, `style::margins` (used for both `margin:` and `padding:` fields), `style::icon`, `style::size`, `style::point`, `style::align`, and `int` or `bool` for simple types.
|
||||
|
||||
## Style Usage in Code
|
||||
|
||||
Styles are applied in C++ code by referencing the generated `st::...` objects and their members.
|
||||
|
||||
```cpp
|
||||
// Example: Including the generated style header
|
||||
#include "styles/style_widgets.h" // For styles defined in ui/widgets/widgets.style
|
||||
|
||||
// ... inside some UI class code ...
|
||||
|
||||
// Accessing members of a generated style struct
|
||||
int height = st::primaryButton.height; // Accessing the 'height' field (pixels -> int)
|
||||
const style::icon &icon = st::primaryButton.icon; // Accessing the 'icon' field (st::myComplexIcon)
|
||||
style::margins padding = st::primaryButton.textPadding; // Accessing 'textPadding'
|
||||
style::color bgColor = st::primaryButton.backgroundColor; // Accessing the color (st::activeButtonColor)
|
||||
|
||||
// Applying styles (conceptual examples)
|
||||
myButton->setIcon(st::primaryButton.icon);
|
||||
myButton->setHeight(st::primaryButton.height);
|
||||
myButton->setPadding(st::primaryButton.textPadding);
|
||||
myButton->setBackgroundColor(st::primaryButton.backgroundColor);
|
||||
|
||||
// Using styles directly in painting
|
||||
void MyWidget::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
p.fillRect(rect(), st::chatInput.backgroundColor); // Use color from chatInput style
|
||||
|
||||
// Border painting requires width and color
|
||||
int borderWidth = st::chatInput.border; // Access border width (pixels -> int)
|
||||
style::color borderColor = st::chatInput.borderFg; // Access border color
|
||||
if (borderWidth > 0) {
|
||||
p.setPen(QPen(borderColor, borderWidth));
|
||||
// Adjust rect for pen width if needed before drawing
|
||||
p.drawRect(rect().adjusted(borderWidth / 2, borderWidth / 2, -borderWidth / 2, -borderWidth / 2));
|
||||
}
|
||||
|
||||
// Access padding (style::margins)
|
||||
style::margins inputPadding = st::chatInput.padding;
|
||||
// ... use inputPadding.top(), inputPadding.left() etc. for content layout ...
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
|
||||
* Styles are defined in `.style` files next to their corresponding C++ source files.
|
||||
* `using "path/to/other.style";` includes definitions from other style files.
|
||||
* Named colors are defined centrally in `ui/colors.palette`.
|
||||
* `.style` syntax supports built-in types (like `pixels`, `color`, `margins`, `point`, `size`, `align`, `font`, `double`), custom structure definitions (`Name { field: type; ... }`), variable definitions (`name: value;`), and inheritance (`child: Name(parent) { ... }`).
|
||||
* Values must match the expected type (e.g., fields declared as `margins` type, like `margin:` or `padding:`, require `margins(...)` syntax). Borders are typically set via separate `border: pixels;` and `borderFg: color;` fields.
|
||||
* Icons are defined inline using `name: icon{{ "path_stem", color }};` or `name: icon{ { "path1", c1 }, ... };` syntax, with optional path modifiers.
|
||||
* Code generation creates `struct` definitions in the `style` namespace for custom types and objects/structs in the `st` namespace for defined variables/groups.
|
||||
* Generated headers are in `styles/` with a `style_` prefix and must be included.
|
||||
* Access style properties via the generated `st::` objects (e.g., `st::primaryButton.height`, `st::chatInput.backgroundColor`).
|
||||
@@ -0,0 +1,328 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Convert changelog.txt to a static HTML page for GitHub Pages."""
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import html
|
||||
from pathlib import Path
|
||||
|
||||
MONTHS = [
|
||||
"", "January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December",
|
||||
]
|
||||
|
||||
VERSION_RE = re.compile(
|
||||
r"^(\d+\.\d+(?:\.\d+)?)\s*" # version number
|
||||
r"(?:(alpha|beta|dev|stable)\s*)?" # optional tag
|
||||
r"\((\d{2})\.(\d{2})\.(\d{2,4})\)$" # date (DD.MM.YY or DD.MM.YYYY)
|
||||
)
|
||||
|
||||
|
||||
def parse_date(day: str, month: str, year: str) -> tuple[str, str, str]:
|
||||
"""Return (sort_key, raw_display, full_display) from DD, MM, YY strings."""
|
||||
y = int(year)
|
||||
if y < 100:
|
||||
y += 2000
|
||||
m = int(month)
|
||||
d = int(day)
|
||||
sort_key = f"{y:04d}-{m:02d}-{d:02d}"
|
||||
raw_display = f"{day}.{month}.{year}"
|
||||
full_display = f"{d} {MONTHS[m]} {y}"
|
||||
return sort_key, raw_display, full_display
|
||||
|
||||
|
||||
def parse_changelog(text: str) -> list[dict]:
|
||||
entries = []
|
||||
current = None
|
||||
|
||||
for raw_line in text.splitlines():
|
||||
line = raw_line.rstrip()
|
||||
m = VERSION_RE.match(line)
|
||||
if m:
|
||||
if current:
|
||||
entries.append(current)
|
||||
version, tag, day, month, year = m.groups()
|
||||
sort_key, raw_date, full_date = parse_date(day, month, year)
|
||||
current = {
|
||||
"version": version,
|
||||
"tag": tag or "",
|
||||
"date": raw_date,
|
||||
"full_date": full_date,
|
||||
"sort_key": sort_key,
|
||||
"lines": [],
|
||||
}
|
||||
elif current is not None:
|
||||
# Skip blank lines at the start
|
||||
if not line and not current["lines"]:
|
||||
continue
|
||||
# Skip stray artifact lines
|
||||
if line.strip() in ("),", "),"):
|
||||
continue
|
||||
current["lines"].append(line)
|
||||
|
||||
if current:
|
||||
entries.append(current)
|
||||
|
||||
# Trim trailing blank lines from each entry
|
||||
for entry in entries:
|
||||
while entry["lines"] and not entry["lines"][-1]:
|
||||
entry["lines"].pop()
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def render_entry(entry: dict) -> str:
|
||||
version = html.escape(entry["version"])
|
||||
tag = entry["tag"]
|
||||
date = html.escape(entry["date"])
|
||||
anchor = f"v{version}"
|
||||
|
||||
tag_html = ""
|
||||
if tag and tag not in ("stable",):
|
||||
tag_html = f' {html.escape(tag)}'
|
||||
|
||||
parts = [
|
||||
f'<article class="entry" id="{anchor}">',
|
||||
f' <h2><a class="anchor" href="#{anchor}"></a>'
|
||||
f'{version}{tag_html}'
|
||||
f' <time>{date}</time></h2>',
|
||||
]
|
||||
|
||||
in_list = False
|
||||
for line in entry["lines"]:
|
||||
stripped = line.lstrip()
|
||||
if stripped.startswith("- ") or stripped.startswith("\u2014 "):
|
||||
# Bullet point (- or em dash)
|
||||
if not in_list:
|
||||
parts.append(" <ul>")
|
||||
in_list = True
|
||||
bullet_text = stripped[2:]
|
||||
parts.append(f" <li>{html.escape(bullet_text)}</li>")
|
||||
else:
|
||||
if in_list:
|
||||
parts.append(" </ul>")
|
||||
in_list = False
|
||||
if stripped:
|
||||
parts.append(f" <p>{html.escape(stripped)}</p>")
|
||||
|
||||
if in_list:
|
||||
parts.append(" </ul>")
|
||||
|
||||
parts.append("</article>")
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def build_html(entries: list[dict]) -> str:
|
||||
count = len(entries)
|
||||
first_date = entries[-1]["full_date"] if entries else ""
|
||||
latest_version = entries[0]["version"] if entries else ""
|
||||
|
||||
entries_html = "\n\n".join(render_entry(e) for e in entries)
|
||||
|
||||
return f"""<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Version history</title>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="icon32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="icon16.png">
|
||||
<style>
|
||||
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
|
||||
body {{
|
||||
font: 12px / 18px "Lucida Grande", "Lucida Sans Unicode", Arial,
|
||||
Helvetica, Verdana, sans-serif;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
}}
|
||||
header {{
|
||||
background: #1d98dc;
|
||||
color: #fff;
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
}}
|
||||
header h1 {{ font-size: 18px; font-weight: 700; }}
|
||||
header p {{ opacity: .85; margin-top: 4px; font-size: 12px; }}
|
||||
.container {{
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 15px;
|
||||
}}
|
||||
.search-box {{
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background: #fff;
|
||||
padding: 8px 0 12px;
|
||||
}}
|
||||
.search-box input {{
|
||||
width: 100%;
|
||||
padding: 6px 10px;
|
||||
font: 12px / 18px "Lucida Grande", "Lucida Sans Unicode", Arial,
|
||||
Helvetica, Verdana, sans-serif;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
outline: none;
|
||||
}}
|
||||
.search-box input:focus {{ border-color: #1d98dc; }}
|
||||
.entry {{
|
||||
padding: 14px 0 4px;
|
||||
scroll-margin-top: 48px;
|
||||
}}
|
||||
.entry h2 {{
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 22px;
|
||||
margin-bottom: 6px;
|
||||
position: relative;
|
||||
}}
|
||||
.entry h2 .anchor {{
|
||||
position: absolute;
|
||||
left: -24px;
|
||||
top: 0;
|
||||
width: 24px;
|
||||
height: 22px;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
transition: opacity .15s;
|
||||
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 16 16'%3E%3Cpath fill='%23168acd' d='M7.775 3.275a.75.75 0 0 0 1.06 1.06l1.25-1.25a2 2 0 1 1 2.83 2.83l-2.5 2.5a2 2 0 0 1-2.83 0 .75.75 0 0 0-1.06 1.06 3.5 3.5 0 0 0 4.95 0l2.5-2.5a3.5 3.5 0 0 0-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 0 1 0-2.83l2.5-2.5a2 2 0 0 1 2.83 0 .75.75 0 0 0 1.06-1.06 3.5 3.5 0 0 0-4.95 0l-2.5 2.5a3.5 3.5 0 0 0 4.95 4.95l1.25-1.25a.75.75 0 0 0-1.06-1.06l-1.25 1.25a2 2 0 0 1-2.83 0z'/%3E%3C/svg%3E") 0 center / 18px no-repeat;
|
||||
cursor: pointer;
|
||||
}}
|
||||
.entry h2:hover .anchor {{ opacity: .6; }}
|
||||
.entry h2 .anchor:hover {{ opacity: 1; }}
|
||||
.entry h2 time {{
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
margin-left: 6px;
|
||||
}}
|
||||
.entry ul {{
|
||||
margin: 0 0 4px 8px;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}}
|
||||
.entry li {{
|
||||
padding: 2px 0 2px 16px;
|
||||
position: relative;
|
||||
color: #333;
|
||||
}}
|
||||
.entry li::before {{
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 9px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #009be1;
|
||||
}}
|
||||
.entry p {{
|
||||
margin: 4px 0;
|
||||
color: #555;
|
||||
font-style: italic;
|
||||
}}
|
||||
.hidden {{ display: none; }}
|
||||
footer {{
|
||||
text-align: center;
|
||||
padding: 24px 15px;
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
}}
|
||||
footer a {{ color: #168acd; text-decoration: none; }}
|
||||
footer a:hover {{ text-decoration: underline; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>Version history</h1>
|
||||
<p>{count} releases since {first_date} · latest: {latest_version}</p>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<div class="search-box">
|
||||
<input type="text" id="search" placeholder="Search versions and changes\u2026"
|
||||
autocomplete="off" spellcheck="false">
|
||||
</div>
|
||||
|
||||
<div id="entries">
|
||||
{entries_html}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
Auto-generated from
|
||||
<a href="https://github.com/telegramdesktop/tdesktop/blob/dev/changelog.txt">changelog.txt</a>.
|
||||
Source code is published under
|
||||
<a href="https://github.com/telegramdesktop/tdesktop">GPL v3</a>.
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
(function() {{
|
||||
var input = document.getElementById('search');
|
||||
var entries = document.querySelectorAll('.entry');
|
||||
var timer;
|
||||
input.addEventListener('input', function() {{
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function() {{
|
||||
var q = input.value.toLowerCase().trim();
|
||||
entries.forEach(function(el) {{
|
||||
if (!q) {{
|
||||
el.classList.remove('hidden');
|
||||
}} else {{
|
||||
el.classList.toggle('hidden', el.textContent.toLowerCase().indexOf(q) === -1);
|
||||
}}
|
||||
}});
|
||||
}}, 150);
|
||||
}});
|
||||
|
||||
// Anchor links: copy URL on click
|
||||
document.addEventListener('click', function(e) {{
|
||||
var anchor = e.target.closest('.anchor');
|
||||
if (!anchor) return;
|
||||
e.preventDefault();
|
||||
var url = location.origin + location.pathname + anchor.getAttribute('href');
|
||||
history.replaceState(null, '', anchor.getAttribute('href'));
|
||||
if (navigator.clipboard) {{
|
||||
navigator.clipboard.writeText(url);
|
||||
}}
|
||||
}});
|
||||
}})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
|
||||
def main():
|
||||
repo = Path(__file__).resolve().parent.parent.parent
|
||||
src = repo / "changelog.txt"
|
||||
if len(sys.argv) > 1:
|
||||
src = Path(sys.argv[1])
|
||||
|
||||
out = repo / "docs" / "changelog" / "index.html"
|
||||
if len(sys.argv) > 2:
|
||||
out = Path(sys.argv[2])
|
||||
|
||||
text = src.read_text(encoding="utf-8")
|
||||
entries = parse_changelog(text)
|
||||
html_content = build_html(entries)
|
||||
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
out.write_text(html_content, encoding="utf-8")
|
||||
|
||||
# Copy favicon files from resources
|
||||
icons_src = repo / "Telegram" / "Resources" / "art"
|
||||
for name in ("icon16.png", "icon32.png"):
|
||||
icon = icons_src / name
|
||||
if icon.exists():
|
||||
shutil.copy2(icon, out.parent / name)
|
||||
|
||||
print(f"Generated {out} ({len(entries)} entries, {out.stat().st_size:,} bytes)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -65,5 +65,11 @@ settings.local.json
|
||||
.cursor/*
|
||||
!.cursor/rules/
|
||||
|
||||
# AI work folder (session-specific, not for version control)
|
||||
.ai
|
||||
|
||||
# Generated changelog page (built by CI, deployed to GitHub Pages)
|
||||
/docs/changelog/
|
||||
|
||||
# Project specific
|
||||
Telegram/SourceFiles/_other/packer_private.h
|
||||
|
||||
@@ -91,6 +91,9 @@
|
||||
[submodule "Telegram/ThirdParty/xdg-desktop-portal"]
|
||||
path = Telegram/ThirdParty/xdg-desktop-portal
|
||||
url = https://github.com/flatpak/xdg-desktop-portal.git
|
||||
[submodule "Telegram/lib_translate"]
|
||||
path = Telegram/lib_translate
|
||||
url = https://github.com/desktop-app/lib_translate
|
||||
[submodule "Telegram/lib_icu"]
|
||||
path = Telegram/lib_icu
|
||||
url = https://github.com/AyuGram/lib_icu.git
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
# Agent Guide for Telegram Desktop
|
||||
|
||||
This guide defines repository-wide instructions for coding agents working with the Telegram Desktop codebase.
|
||||
|
||||
Avoid building the project.
|
||||
|
||||
If you're asked to create a Pull Request, then clearly state in PR description that it was AI generated.
|
||||
|
||||
# Development Guidelines
|
||||
|
||||
## Coding Style
|
||||
|
||||
**Do NOT write comments in code:**
|
||||
|
||||
This is important! Do not write single-line comments that describe what the next line does - they are bloat. Comments are allowed ONLY to describe complex algorithms in detail, when the explanation requires at least 4-5 lines. Self-documenting code with clear variable and function names is preferred.
|
||||
|
||||
```cpp
|
||||
// BAD - don't do this:
|
||||
// Get the user's name
|
||||
auto name = user->name();
|
||||
// Check if premium
|
||||
if (user->isPremium()) {
|
||||
|
||||
// GOOD - no comments needed, code is self-explanatory:
|
||||
auto name = user->name();
|
||||
if (user->isPremium()) {
|
||||
|
||||
// ACCEPTABLE - complex algorithm explanation (4+ lines):
|
||||
// The algorithm works by first collecting all visible messages
|
||||
// in the viewport, then calculating their intersection with
|
||||
// the clip rectangle. Messages are grouped by date headers,
|
||||
// and we need to account for sticky headers that may overlap
|
||||
// with the first message in each group.
|
||||
```
|
||||
|
||||
**Style and formatting rules** are in `REVIEW.md` — see that file for empty-line-before-closing-brace, operator placement in multi-line expressions, if-with-initializer, and other mechanical style rules.
|
||||
|
||||
**Use `auto` for type deduction:**
|
||||
|
||||
Prefer `auto` (or `const auto`, `const auto &`) instead of explicit types:
|
||||
|
||||
```cpp
|
||||
// Prefer this:
|
||||
auto currentTitle = tr::lng_settings_title(tr::now);
|
||||
auto nameProducer = GetNameProducer();
|
||||
|
||||
// Instead of this:
|
||||
QString currentTitle = tr::lng_settings_title(tr::now);
|
||||
rpl::producer<QString> nameProducer = GetNameProducer();
|
||||
```
|
||||
|
||||
## API Usage
|
||||
|
||||
### API Schema Files
|
||||
|
||||
API definitions use [TL Language](https://core.telegram.org/mtproto/TL):
|
||||
|
||||
1. **`Telegram/SourceFiles/mtproto/scheme/mtproto.tl`** - MTProto protocol (encryption, auth, etc.)
|
||||
2. **`Telegram/SourceFiles/mtproto/scheme/api.tl`** - Telegram API (messages, users, chats, etc.)
|
||||
|
||||
### Making API Requests
|
||||
|
||||
Standard pattern using `api()`, generated `MTP...` types, and callbacks:
|
||||
|
||||
```cpp
|
||||
api().request(MTPnamespace_MethodName(
|
||||
MTP_flags(flags_value),
|
||||
MTP_inputPeer(peer),
|
||||
MTP_string(messageText),
|
||||
MTP_long(randomId),
|
||||
MTP_vector<MTPMessageEntity>()
|
||||
)).done([=](const MTPResponseType &result) {
|
||||
// Handle successful response
|
||||
|
||||
// Multiple constructors - use .match() or check type:
|
||||
result.match([&](const MTPDuser &data) {
|
||||
// use data.vfirst_name().v
|
||||
}, [&](const MTPDuserEmpty &data) {
|
||||
// handle empty user
|
||||
});
|
||||
|
||||
// Single constructor - use .data() shortcut:
|
||||
const auto &data = result.data();
|
||||
// use data.vmessages().v
|
||||
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
// Handle API error
|
||||
if (error.type() == u"FLOOD_WAIT_X"_q) {
|
||||
// Handle flood wait
|
||||
}
|
||||
}).handleFloodErrors().send();
|
||||
```
|
||||
|
||||
**Key points:**
|
||||
- Always refer to `api.tl` for method signatures and return types
|
||||
- Use generated `MTP...` types for parameters (`MTP_int`, `MTP_string`, etc.)
|
||||
- For multiple constructors, use `.match()` or check `.type()` against `mtpc_` constants then call `.c_constructorName()`:
|
||||
```cpp
|
||||
// Using match:
|
||||
result.match([&](const MTPDuser &data) { ... }, [&](const MTPDuserEmpty &data) { ... });
|
||||
// Or explicit type check:
|
||||
if (result.type() == mtpc_user) {
|
||||
const auto &data = result.c_user(); // asserts on type mismatch
|
||||
}
|
||||
```
|
||||
- For single constructors, use `.data()` shortcut
|
||||
- Include `.handleFloodErrors()` before `.send()` in rare cases where you want special case flood error handling
|
||||
- Silently ignore HTTP 406 errors in UI: the server uses 406 to mean "show nothing to the user". Guard toasts with `MTP::IgnoreError(error)` or use `MTP::ShowErrorFallback(show, error)` (both in `mtproto/mtproto_response.h`) which shows `error.type()` as a toast unless the error should be ignored.
|
||||
|
||||
## UI Styling
|
||||
|
||||
### Style Files
|
||||
|
||||
UI styles are defined in `.style` files using custom syntax:
|
||||
|
||||
```style
|
||||
using "ui/basic.style";
|
||||
using "ui/widgets/widgets.style";
|
||||
|
||||
MyButtonStyle {
|
||||
textPadding: margins;
|
||||
icon: icon;
|
||||
height: pixels;
|
||||
}
|
||||
|
||||
defaultButton: MyButtonStyle {
|
||||
textPadding: margins(10px, 15px, 10px, 15px);
|
||||
icon: icon{{ "gui/icons/search", iconColor }};
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
primaryButton: MyButtonStyle(defaultButton) {
|
||||
icon: icon{{ "gui/icons/check", iconColor }};
|
||||
}
|
||||
```
|
||||
|
||||
**Built-in types:**
|
||||
- `int` - Integer numbers (e.g., `maxLines: 3;`)
|
||||
- `bool` - Boolean values (e.g., `useShadow: true;`)
|
||||
- `pixels` - Pixel values with `px` suffix (e.g., `10px`)
|
||||
- `color` - Named colors from `ui/colors.palette`
|
||||
- `icon` - Inline icon definition: `icon{{ "path/stem", color }}`
|
||||
- `margins` - Four values: `margins(top, right, bottom, left)`
|
||||
- `size` - Two values: `size(width, height)`
|
||||
- `point` - Two values: `point(x, y)`
|
||||
- `align` - Alignment: `align(center)`, `align(left)`
|
||||
- `font` - Font: `font(14px semibold)`
|
||||
- `double` - Floating point
|
||||
|
||||
**Multi-part icons** (layers drawn bottom-up):
|
||||
```style
|
||||
myComplexIcon: icon{
|
||||
{ "gui/icons/background", iconBgColor },
|
||||
{ "gui/icons/foreground", iconFgColor }
|
||||
};
|
||||
```
|
||||
|
||||
**Borders** are typically separate fields, not a single property:
|
||||
```style
|
||||
chatInput {
|
||||
border: 1px; // width
|
||||
borderFg: defaultInputFieldBorder; // color
|
||||
}
|
||||
```
|
||||
|
||||
**Never hardcode sizes in code:**
|
||||
|
||||
The app supports different interface scale options. Style `px` values are automatically scaled at runtime, but raw integer constants in code are not. Never use hardcoded numbers for margins, paddings, spacing, sizes, coordinates, or any other dimensional values. Always define them in `.style` files and reference via `st::`.
|
||||
|
||||
```cpp
|
||||
// BAD - breaks at non-100% interface scale:
|
||||
p.drawText(10, 20, text);
|
||||
widget->setFixedHeight(48);
|
||||
auto margin = 8;
|
||||
auto iconSize = QSize(24, 24);
|
||||
|
||||
// GOOD - define in .style file and reference:
|
||||
p.drawText(st::myWidgetTextLeft, st::myWidgetTextTop, text);
|
||||
widget->setFixedHeight(st::myWidgetHeight);
|
||||
auto margin = st::myWidgetMargin;
|
||||
auto iconSize = st::myWidgetIconSize;
|
||||
```
|
||||
|
||||
**Duration constants**: Animation durations should NOT go in `.style` files, this is a legacy approach. Prefer `constexpr auto kName = crl::time(N)` in an anonymous namespace in the relevant `.cpp` file.
|
||||
|
||||
### Usage in Code
|
||||
|
||||
```cpp
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
// Access style members
|
||||
int height = st::primaryButton.height;
|
||||
const style::icon &icon = st::primaryButton.icon;
|
||||
style::margins padding = st::primaryButton.textPadding;
|
||||
|
||||
// Use in painting
|
||||
void MyWidget::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
p.fillRect(rect(), st::chatInput.backgroundColor);
|
||||
}
|
||||
```
|
||||
|
||||
## Localization
|
||||
|
||||
### String Definitions
|
||||
|
||||
Strings are defined in `Telegram/Resources/langs/lang.strings`:
|
||||
|
||||
```
|
||||
"lng_settings_title" = "Settings";
|
||||
"lng_confirm_delete_item" = "Are you sure you want to delete {item_name}?";
|
||||
"lng_files_selected#one" = "{count} file selected";
|
||||
"lng_files_selected#other" = "{count} files selected";
|
||||
```
|
||||
|
||||
### Usage in Code
|
||||
|
||||
**Immediate (current value):**
|
||||
|
||||
```cpp
|
||||
auto currentTitle = tr::lng_settings_title(tr::now);
|
||||
|
||||
auto currentConfirmation = tr::lng_confirm_delete_item(
|
||||
tr::now,
|
||||
lt_item_name, currentItemName);
|
||||
|
||||
auto filesText = tr::lng_files_selected(tr::now, lt_count, count);
|
||||
```
|
||||
|
||||
**Reactive (rpl::producer):**
|
||||
|
||||
```cpp
|
||||
auto titleProducer = tr::lng_settings_title();
|
||||
|
||||
auto confirmationProducer = tr::lng_confirm_delete_item(
|
||||
lt_item_name,
|
||||
std::move(itemNameProducer));
|
||||
|
||||
auto filesTextProducer = tr::lng_files_selected(
|
||||
lt_count,
|
||||
countProducer | tr::to_count());
|
||||
```
|
||||
|
||||
**Key points:**
|
||||
- Pass `tr::now` as first argument for immediate `QString`
|
||||
- Omit `tr::now` for reactive `rpl::producer<QString>`
|
||||
- Placeholders use `lt_tag_name, value` pattern
|
||||
- For `{count}`: immediate uses `int`, reactive uses `rpl::producer<float64>` with `| tr::to_count()`
|
||||
- Move producers with `std::move` when passing to placeholders
|
||||
- Rich text projectors — these `tr::` helpers serve double duty: as the **last argument** (projector) they set the return type to `TextWithEntities`, and as **placeholder values** they wrap individual substitutions in formatting. Always prefer them over `Ui::Text::Bold()`, `Ui::Text::RichLangValue`, etc. — see REVIEW.md for the full mapping.
|
||||
- `tr::marked` — basic projection, converts `QString` to `TextWithEntities`
|
||||
- `tr::rich` — interprets `**bold**`/`__italic__` markup in the string
|
||||
- `tr::bold`, `tr::italic`, `tr::underline` — wrap text in that formatting
|
||||
- `tr::link` — wrap as a clickable link
|
||||
- `tr::url(u"https://..."_q)` — returns a projection that converts text to a link pointing to the given URL; can be passed to `rpl::map` or directly to a `tr::lng_...` call
|
||||
```cpp
|
||||
// As last argument (projector):
|
||||
auto title = tr::lng_export_progress_title(tr::now, tr::bold);
|
||||
auto text = tr::lng_proxy_incorrect_secret(tr::now, tr::rich);
|
||||
// As placeholder value wrapper + projector:
|
||||
auto desc = tr::lng_some_key(
|
||||
tr::now,
|
||||
lt_name,
|
||||
tr::bold(userName),
|
||||
lt_group,
|
||||
tr::bold(groupName),
|
||||
tr::rich);
|
||||
// Nested tr::lng as placeholder:
|
||||
auto linked = tr::lng_settings_birthday_contacts(
|
||||
lt_link,
|
||||
tr::lng_settings_birthday_contacts_link(tr::url(link)),
|
||||
tr::marked);
|
||||
```
|
||||
|
||||
## RPL (Reactive Programming Library)
|
||||
|
||||
### Core Concepts
|
||||
|
||||
**Producers** represent streams of values over time:
|
||||
|
||||
```cpp
|
||||
auto intProducer = rpl::single(123); // Emits single value
|
||||
auto lifetime = rpl::lifetime(); // Manages subscription lifetime
|
||||
```
|
||||
|
||||
### Starting Pipelines
|
||||
|
||||
```cpp
|
||||
std::move(counter) | rpl::on_next([=](int value) {
|
||||
qDebug() << "Received: " << value;
|
||||
}, lifetime);
|
||||
|
||||
// Without lifetime parameter - MUST store returned lifetime:
|
||||
auto subscriptionLifetime = std::move(counter) | rpl::on_next([=](int value) {
|
||||
// process value
|
||||
});
|
||||
```
|
||||
|
||||
### Transforming Producers
|
||||
|
||||
```cpp
|
||||
auto strings = std::move(ints) | rpl::map([](int value) {
|
||||
return QString::number(value * 2);
|
||||
});
|
||||
|
||||
auto evenInts = std::move(ints) | rpl::filter([](int value) {
|
||||
return (value % 2 == 0);
|
||||
});
|
||||
```
|
||||
|
||||
### Combining Producers
|
||||
|
||||
**`rpl::combine`** - combines latest values (lambdas receive unpacked arguments):
|
||||
|
||||
```cpp
|
||||
auto combined = rpl::combine(countProducer, textProducer);
|
||||
|
||||
std::move(combined) | rpl::on_next([=](int count, const QString &text) {
|
||||
qDebug() << "Count=" << count << ", Text=" << text;
|
||||
}, lifetime);
|
||||
```
|
||||
|
||||
**`rpl::merge`** - merges producers of same type:
|
||||
|
||||
```cpp
|
||||
auto merged = rpl::merge(sourceA, sourceB);
|
||||
|
||||
std::move(merged) | rpl::on_next([=](QString &&value) {
|
||||
qDebug() << "Merged value: " << value;
|
||||
}, lifetime);
|
||||
```
|
||||
|
||||
**Other pipeline starters** — besides `rpl::on_next`, there are:
|
||||
- `rpl::on_error([=](Error &&e) { ... }, lifetime)` — handle errors
|
||||
- `rpl::on_done([=] { ... }, lifetime)` — handle stream completion
|
||||
- `rpl::on_next_error_done(nextCb, errorCb, doneCb, lifetime)` — handle all three
|
||||
|
||||
The `Error` template parameter defaults to `rpl::no_error`: `rpl::producer<Type, Error = no_error>`.
|
||||
|
||||
**Key points:**
|
||||
- Explicitly `std::move` producers when starting pipelines
|
||||
- Pass `rpl::lifetime` to `on_...` methods or store returned lifetime
|
||||
- Use `rpl::duplicate(producer)` to reuse a producer multiple times
|
||||
- Combined producers automatically unpack tuples in lambdas (works with `rpl::map`, `rpl::filter`, and `rpl::on_next`)
|
||||
@@ -0,0 +1,3 @@
|
||||
# Claude Code Pointer
|
||||
|
||||
Read `AGENTS.md` and treat it as the canonical repository-wide instructions.
|
||||
@@ -12,6 +12,10 @@ include(cmake/validate_special_target.cmake)
|
||||
include(cmake/version.cmake)
|
||||
desktop_app_parse_version(Telegram/build/version)
|
||||
|
||||
if (NOT DEFINED CMAKE_CONFIGURATION_TYPES)
|
||||
set(configuration_types_init 1)
|
||||
endif()
|
||||
|
||||
project(Telegram
|
||||
LANGUAGES C CXX
|
||||
VERSION ${desktop_app_version_cmake}
|
||||
@@ -23,6 +27,12 @@ if (APPLE)
|
||||
enable_language(OBJC OBJCXX)
|
||||
endif()
|
||||
|
||||
if (configuration_types_init
|
||||
AND CMAKE_CONFIGURATION_TYPES
|
||||
AND NOT MinSizeRel IN_LIST CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES};MinSizeRel" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Telegram)
|
||||
|
||||
get_filename_component(third_party_loc "Telegram/ThirdParty" REALPATH)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
Copyright (c) 2014-2025 The Telegram Desktop Authors.
|
||||
Copyright (c) 2014-2026 The Telegram Desktop Authors.
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -87,7 +87,13 @@ brew install --cask ayugram
|
||||
|
||||
### NixOS
|
||||
|
||||
Попробуйте [этот репозиторий](https://github.com/ayugram-port/ayugram-desktop).
|
||||
#### Флейк (рекомендуется)
|
||||
|
||||
Установите `ayugram-desktop` из [ndfined-crp/ayugram-desktop](https://github.com/ndfined-crp/ayugram-desktop)
|
||||
|
||||
#### Nixpkgs
|
||||
|
||||
Установите `ayugram-desktop` из [nixpkgs](https://search.nixos.org/packages?channel=unstable&show=ayugram-desktop)
|
||||
|
||||
### ALT Linux
|
||||
|
||||
@@ -95,12 +101,23 @@ brew install --cask ayugram
|
||||
|
||||
### Gentoo Linux
|
||||
|
||||
Инструкцию по установке можно найти в [этом репозитории](https://github.com/OverLessArtem/ayugram-ebuild-gentoo).
|
||||
Инструкцию по установке можно найти в [этом репозитории](https://codeberg.org/OverLessArtem/ayugram-ebuild-gentoo).
|
||||
|
||||
### Void Linux
|
||||
Инструкцию по установке можно найти в [этом репозитории](https://codeberg.org/OverLessArtem/ayugram-template-void)
|
||||
|
||||
### EPM
|
||||
|
||||
`epm play ayugram`
|
||||
|
||||
### Fedora
|
||||
|
||||
Из репозитория [RPM Fusion](https://admin.rpmfusion.org/pkgdb/package/free/ayugram-desktop/).
|
||||
|
||||
```bash
|
||||
dnf install ayugram-desktop
|
||||
```
|
||||
|
||||
### Любой другой Линукс дистрибутив
|
||||
|
||||
Flatpak: https://github.com/0FL01/AyuGramDesktop-flatpak
|
||||
@@ -142,4 +159,4 @@ Flatpak: https://github.com/0FL01/AyuGramDesktop-flatpak
|
||||
|
||||
### Боты
|
||||
|
||||
- [TelegramDB](https://t.me/tgdatabase) для получения юзернейма по ID
|
||||
- [TelegramDB](https://t.me/tgdatabase) для получения юзернейма по ID (до закрытия бесплатной версии 2 апреля 2026)
|
||||
|
||||
@@ -88,7 +88,13 @@ Note: these binaries aren't officially maintained by us.
|
||||
|
||||
### NixOS
|
||||
|
||||
See [this repository](https://github.com/ayugram-port/ayugram-desktop) for installation manual.
|
||||
#### Flake (recommended)
|
||||
|
||||
Install `ayugram-desktop` from [ndfined-crp/ayugram-desktop](https://github.com/ndfined-crp/ayugram-desktop)
|
||||
|
||||
#### Nixpkgs
|
||||
|
||||
Install `ayugram-desktop` from [nixpkgs](https://search.nixos.org/packages?channel=unstable&show=ayugram-desktop)
|
||||
|
||||
### ALT Linux
|
||||
|
||||
@@ -96,12 +102,23 @@ See [this repository](https://github.com/ayugram-port/ayugram-desktop) for insta
|
||||
|
||||
### Gentoo Linux
|
||||
|
||||
See [this repository](https://github.com/OverLessArtem/ayugram-ebuild-gentoo) for installation manual.
|
||||
See [this repository](https://codeberg.org/OverLessArtem/ayugram-ebuild-gentoo) for installation manual.
|
||||
|
||||
### Void Linux
|
||||
See [this repository](https://codeberg.org/OverLessArtem/ayugram-template-void) for installation manual.
|
||||
|
||||
### EPM
|
||||
|
||||
`epm play ayugram`
|
||||
|
||||
### Fedora
|
||||
|
||||
From [RPM Fusion](https://admin.rpmfusion.org/pkgdb/package/free/ayugram-desktop/) repository.
|
||||
|
||||
```bash
|
||||
dnf install ayugram-desktop
|
||||
```
|
||||
|
||||
### Any other Linux distro
|
||||
|
||||
Flatpak: https://github.com/0FL01/AyuGramDesktop-flatpak
|
||||
@@ -144,4 +161,4 @@ Enjoy using **AyuGram**? Consider sending us a tip!
|
||||
|
||||
### Bots
|
||||
|
||||
- [TelegramDB](https://t.me/tgdatabase) for username lookup by ID
|
||||
- [TelegramDB](https://t.me/tgdatabase) for username lookup by ID (until closing free inline mode at 2 April 2026)
|
||||
|
||||
@@ -0,0 +1,514 @@
|
||||
# Code Review Style Guide
|
||||
|
||||
This file contains style and formatting rules that the review subagent must check and fix. These are mechanical issues that should always be caught during code review.
|
||||
|
||||
## Empty line before closing brace
|
||||
|
||||
Always add an empty line before the closing brace of a **class** (which has one or more sections like `public:` / `private:`). Plain **structs** with just data members do NOT get a trailing empty line — they are compact: `struct Foo { data lines; };`.
|
||||
|
||||
```cpp
|
||||
// BAD:
|
||||
class MyClass {
|
||||
public:
|
||||
void foo();
|
||||
|
||||
private:
|
||||
int _value = 0;
|
||||
};
|
||||
|
||||
// GOOD:
|
||||
class MyClass {
|
||||
public:
|
||||
void foo();
|
||||
|
||||
private:
|
||||
int _value = 0;
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
## Multi-line expressions — operators at the start of continuation lines
|
||||
|
||||
When splitting an expression across multiple lines, place operators (like `&&`, `||`, `;`, `+`, etc.) at the **beginning** of continuation lines, not at the end of the previous line. This makes it immediately obvious from the left edge whether a line is a continuation or new code.
|
||||
|
||||
```cpp
|
||||
// BAD - continuation looks like scope code:
|
||||
if (const auto &lottie = animation->lottie;
|
||||
lottie && lottie->valid() && lottie->framesCount() > 1) {
|
||||
lottie->animate([=] {
|
||||
|
||||
// GOOD - semicolon at start signals continuation:
|
||||
if (const auto &lottie = animation->lottie
|
||||
; lottie && lottie->valid() && lottie->framesCount() > 1) {
|
||||
lottie->animate([=] {
|
||||
|
||||
// BAD - trailing && makes next line look like independent code:
|
||||
if (veryLongExpression() &&
|
||||
anotherLongExpression() &&
|
||||
anotherOne()) {
|
||||
doSomething();
|
||||
|
||||
// GOOD - leading && clearly marks continuation:
|
||||
if (veryLongExpression()
|
||||
&& anotherLongExpression()
|
||||
&& anotherOne()) {
|
||||
doSomething();
|
||||
```
|
||||
|
||||
## Minimize type checks — prefer direct cast over is + as
|
||||
|
||||
Don't check a type and then cast — just cast and check for null. `asUser()` already returns `nullptr` when the peer is not a user, so calling `isUser()` first is redundant. The same applies to `asChannel()`, `asChat()`, etc.
|
||||
|
||||
```cpp
|
||||
// BAD - redundant isUser() check, then asUser():
|
||||
if (peer && peer->isUser()) {
|
||||
peer->asUser()->setNoForwardFlags(
|
||||
|
||||
// GOOD - just cast and null-check:
|
||||
if (const auto user = peer->asUser()) {
|
||||
user->setNoForwardFlags(
|
||||
```
|
||||
|
||||
When you need a specific subtype, look up the specific subtype directly instead of loading a generic type and then casting:
|
||||
|
||||
```cpp
|
||||
// BAD - loads generic peer, then casts:
|
||||
if (const auto peer = session().data().peerLoaded(peerId)
|
||||
; peer && peer->isUser()) {
|
||||
peer->asUser()->setNoForwardFlags(
|
||||
|
||||
// GOOD - look up the specific subtype directly:
|
||||
const auto userId = peerToUser(peerId);
|
||||
if (const auto user = session().data().userLoaded(userId)) {
|
||||
user->setNoForwardFlags(
|
||||
```
|
||||
|
||||
Avoid C++17 `if` with initializer (`;` inside the condition) when the code can be written more clearly with simple nested `if` statements or by extracting the value beforehand:
|
||||
|
||||
```cpp
|
||||
// BAD - complex if-with-initializer:
|
||||
if (const auto peer = session().data().peerLoaded(peerId)
|
||||
; peer && peer->isUser()) {
|
||||
|
||||
// GOOD - simple nested ifs when direct lookup isn't available:
|
||||
if (const auto peer = session().data().peerLoaded(peerId)) {
|
||||
if (const auto user = peer->asUser()) {
|
||||
|
||||
## Always initialize variables of basic types
|
||||
|
||||
Never leave variables of basic types (`int`, `float`, `bool`, pointers, etc.) uninitialized. Custom types with constructors are fine — they initialize themselves. But for any basic type, always provide a default value (`= 0`, `= false`, `= nullptr`, etc.). This applies especially to class fields, where uninitialized members are a persistent source of bugs.
|
||||
|
||||
The only exception is performance-critical hot paths where you can prove no read-from-uninitialized-memory occurs. For class fields there is no such exception — always initialize.
|
||||
|
||||
```cpp
|
||||
// BAD:
|
||||
int _bulletLeft;
|
||||
int _bulletTop;
|
||||
bool _expanded;
|
||||
SomeType *_pointer;
|
||||
|
||||
// GOOD:
|
||||
int _bulletLeft = 0;
|
||||
int _bulletTop = 0;
|
||||
bool _expanded = false;
|
||||
SomeType *_pointer = nullptr;
|
||||
```
|
||||
|
||||
## Use tr:: projections for TextWithEntities
|
||||
|
||||
Inside `tr::lng_...()` calls, always use the `tr::` projection helpers instead of their `Ui::Text::` equivalents. The `tr::` helpers are shorter and work uniformly as both placeholder wrappers and final projectors.
|
||||
|
||||
| Instead of | Use |
|
||||
|---|---|
|
||||
| `Ui::Text::Bold(x)` | `tr::bold(x)` |
|
||||
| `Ui::Text::Italic(x)` | `tr::italic(x)` |
|
||||
| `Ui::Text::RichLangValue` | `tr::rich` |
|
||||
| `Ui::Text::WithEntities` | `tr::marked` |
|
||||
|
||||
```cpp
|
||||
// BAD - verbose Ui::Text:: functions:
|
||||
tr::lng_some_key(
|
||||
tr::now,
|
||||
lt_name,
|
||||
Ui::Text::Bold(name),
|
||||
lt_group,
|
||||
Ui::Text::Bold(group),
|
||||
Ui::Text::RichLangValue)
|
||||
|
||||
// GOOD - concise tr:: helpers:
|
||||
tr::lng_some_key(
|
||||
tr::now,
|
||||
lt_name,
|
||||
tr::bold(name),
|
||||
lt_group,
|
||||
tr::bold(group),
|
||||
tr::rich)
|
||||
```
|
||||
|
||||
Also use `tr::marked()` as the standard way to create `TextWithEntities` — not just as a projector:
|
||||
|
||||
```cpp
|
||||
// BAD - verbose constructor:
|
||||
auto text = TextWithEntities();
|
||||
auto text = TextWithEntities{ u"hello"_q };
|
||||
auto text = TextWithEntities().append(u"hello"_q);
|
||||
|
||||
// GOOD - concise:
|
||||
auto text = tr::marked();
|
||||
auto text = tr::marked(u"hello"_q);
|
||||
```
|
||||
|
||||
## Multi-line calls — one argument per line
|
||||
|
||||
When a function call doesn't fit on one line, put each argument on its own line. Don't group "logical pairs" on the same line — it creates inconsistent line lengths and makes diffs noisier.
|
||||
|
||||
```cpp
|
||||
// BAD - pairs of arguments sharing lines:
|
||||
tr::lng_some_key(
|
||||
tr::now,
|
||||
lt_name, tr::bold(name),
|
||||
lt_group, tr::bold(group),
|
||||
tr::rich)
|
||||
|
||||
// GOOD - one argument per line:
|
||||
tr::lng_some_key(
|
||||
tr::now,
|
||||
lt_name,
|
||||
tr::bold(name),
|
||||
lt_group,
|
||||
tr::bold(group),
|
||||
tr::rich)
|
||||
|
||||
// Single-line is fine when everything fits:
|
||||
auto text = tr::lng_settings_title(tr::now);
|
||||
```
|
||||
|
||||
## std::optional access — avoid value()
|
||||
|
||||
Do not call `std::optional::value()` because it throws an exception that is not available on older macOS targets. Use `has_value()`, `value_or()`, `operator bool()`, or `operator*` instead.
|
||||
|
||||
## Sort includes alphabetically, nested folders first
|
||||
|
||||
After the file's own header, sort `#include` directives alphabetically with two special rules:
|
||||
|
||||
1. **Nested folders before files** in the same directory — like Finder / File Explorer (folders first, then files). E.g. `ui/controls/button.h` sorts before `ui/abstract_button.h`.
|
||||
2. **Style includes (`styles/style_*.h`) always go last**, separated from the rest.
|
||||
|
||||
```cpp
|
||||
// BAD - arbitrary order, style mixed in:
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "styles/style_media_player.h"
|
||||
#include "data/data_document.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
// GOOD - alphabetical, folders first, styles last:
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_document.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
|
||||
#include "styles/style_media_player.h"
|
||||
```
|
||||
|
||||
## Use C++17 nested namespace syntax
|
||||
|
||||
Use `namespace A::B {` instead of nesting `namespace A { namespace B {`. The closing comment mirrors the opening: `} // namespace A::B`.
|
||||
|
||||
```cpp
|
||||
// BAD - old-style nesting:
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
...
|
||||
} // namespace Player
|
||||
} // namespace Media
|
||||
|
||||
// GOOD - C++17 nested:
|
||||
namespace Media::Player {
|
||||
...
|
||||
} // namespace Media::Player
|
||||
```
|
||||
|
||||
## Merge consecutive branches with identical bodies
|
||||
|
||||
When two or more consecutive `if` / `else if` branches execute the same code, combine their conditions into a single branch.
|
||||
|
||||
```cpp
|
||||
// BAD - duplicated body:
|
||||
if (!document) {
|
||||
finalize();
|
||||
return;
|
||||
}
|
||||
if (!document->isSong()) {
|
||||
finalize();
|
||||
return;
|
||||
}
|
||||
|
||||
// GOOD - combined:
|
||||
if (!document || !document->isSong()) {
|
||||
finalize();
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
## Use base::take for read-and-reset
|
||||
|
||||
When you need to read a variable's current value and reset it in one step, use `base::take(var)` instead of manually copying and clearing. `base::take` returns the old value and resets the variable to its default-constructed state.
|
||||
|
||||
```cpp
|
||||
// BAD - manual read + reset:
|
||||
if (_playing) {
|
||||
_listenedMs += crl::now() - _playStartedAt;
|
||||
_playing = false;
|
||||
}
|
||||
|
||||
// GOOD:
|
||||
if (base::take(_playing)) {
|
||||
_listenedMs += crl::now() - _playStartedAt;
|
||||
}
|
||||
|
||||
// BAD - copy fields then clear them one by one:
|
||||
const auto document = _document;
|
||||
const auto contextId = _contextId;
|
||||
_document = nullptr;
|
||||
_listenedMs = 0;
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
// GOOD - take everything upfront, then validate:
|
||||
const auto document = base::take(_document);
|
||||
const auto contextId = base::take(_contextId);
|
||||
const auto duration = static_cast<int>(base::take(_listenedMs) / 1000);
|
||||
if (!document || duration <= 0) {
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
## Don't wrap tr:: lang keys in rpl::single
|
||||
|
||||
`tr::lng_*()` (without `tr::now`) already returns an `rpl::producer`. Wrapping a snapshot in `rpl::single()` defeats live language switching — the value is captured once and never updates. Just call the lang key without `tr::now`.
|
||||
|
||||
```cpp
|
||||
// BAD - frozen snapshot, won't update on language change:
|
||||
rpl::single(tr::lng_ai_compose_title(tr::now))
|
||||
|
||||
// GOOD - live producer that updates automatically:
|
||||
tr::lng_ai_compose_title()
|
||||
```
|
||||
|
||||
## Extract method definitions from local classes
|
||||
|
||||
When defining local classes (e.g. in anonymous namespaces), keep the class body compact — only declarations. Put all method definitions **after** all class definitions. This avoids unnecessary nesting inside the class body and keeps methods at the same indentation level as free functions.
|
||||
|
||||
```cpp
|
||||
// BAD - methods defined inline, adding a nesting level:
|
||||
class MyWidget final : public Ui::RpWidget {
|
||||
public:
|
||||
MyWidget(QWidget *parent)
|
||||
: RpWidget(parent) {
|
||||
// ... 20 lines of setup
|
||||
}
|
||||
|
||||
void setActive(bool active) {
|
||||
_active = active;
|
||||
update();
|
||||
}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override {
|
||||
// ... 30 lines of painting
|
||||
}
|
||||
|
||||
private:
|
||||
bool _active = false;
|
||||
|
||||
};
|
||||
|
||||
// GOOD - class is a compact declaration, methods defined after:
|
||||
class MyWidget final : public Ui::RpWidget {
|
||||
public:
|
||||
MyWidget(QWidget *parent, QString label);
|
||||
|
||||
void setActive(bool active);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
bool _active = false;
|
||||
|
||||
};
|
||||
|
||||
MyWidget::MyWidget(QWidget *parent, QString label)
|
||||
: RpWidget(parent) {
|
||||
// ... 20 lines of setup
|
||||
}
|
||||
|
||||
void MyWidget::setActive(bool active) {
|
||||
_active = active;
|
||||
update();
|
||||
}
|
||||
|
||||
void MyWidget::paintEvent(QPaintEvent *e) {
|
||||
// ... 30 lines of painting
|
||||
}
|
||||
```
|
||||
|
||||
When there are multiple local classes, put **all class definitions first**, then **all method definitions** after. This keeps the declarations readable as an overview.
|
||||
|
||||
## Use RAII for resource cleanup
|
||||
|
||||
When working with raw resources (Win32 HANDLEs, file descriptors, COM objects), use `gsl::finally` or a dedicated RAII wrapper for cleanup instead of calling release functions manually. Manual cleanup breaks when early returns are added later.
|
||||
|
||||
```cpp
|
||||
// BAD - manual cleanup, fragile with early returns:
|
||||
const auto snapshot = CreateToolhelp32Snapshot(...);
|
||||
if (snapshot != INVALID_HANDLE_VALUE) {
|
||||
// ... logic that might grow early returns ...
|
||||
CloseHandle(snapshot);
|
||||
}
|
||||
|
||||
// GOOD - RAII guard, cleanup runs on any exit path:
|
||||
const auto snapshot = CreateToolhelp32Snapshot(...);
|
||||
if (snapshot == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
const auto guard = gsl::finally([&] {
|
||||
CloseHandle(snapshot);
|
||||
});
|
||||
// ... logic, early returns are safe ...
|
||||
```
|
||||
|
||||
## Extract substantial logic from lambdas
|
||||
|
||||
When a lambda grows beyond a few lines of self-contained logic, extract it into a named function (free function in anonymous namespace, or a private method). Lambdas should primarily be glue — captures, dispatch, short transforms. This applies when the lambda's captures are minimal and can easily become function parameters. When a lambda captures many variables from its surrounding context, it may be cleaner to keep it inline.
|
||||
|
||||
```cpp
|
||||
// BAD - substantial logic buried in a lambda:
|
||||
crl::async([=] {
|
||||
auto found = false;
|
||||
auto pe = PROCESSENTRY32();
|
||||
pe.dwSize = sizeof(PROCESSENTRY32);
|
||||
const auto snapshot = CreateToolhelp32Snapshot(...);
|
||||
if (snapshot != INVALID_HANDLE_VALUE) {
|
||||
for (...) {
|
||||
if (/* match */) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseHandle(snapshot);
|
||||
}
|
||||
crl::on_main(weak, [=] { handle(found); });
|
||||
});
|
||||
|
||||
// GOOD - logic extracted, lambda is just glue:
|
||||
crl::async([=] {
|
||||
const auto found = FindRunningReader();
|
||||
crl::on_main(weak, [=] { handle(found); });
|
||||
});
|
||||
```
|
||||
|
||||
## Data-driven matching over chained conditions
|
||||
|
||||
When comparing a value against multiple known constants, store them in a collection and loop instead of chaining `||` conditions. Easier to extend, less repetition, and reads as data rather than logic.
|
||||
|
||||
```cpp
|
||||
// BAD - repetitive chain, hard to extend:
|
||||
if (_wcsicmp(name, L"Narrator.exe") == 0
|
||||
|| _wcsicmp(name, L"nvda.exe") == 0
|
||||
|| _wcsicmp(name, L"jfw.exe") == 0
|
||||
|| _wcsicmp(name, L"Zt.exe") == 0) {
|
||||
|
||||
// GOOD - data-driven, easy to extend:
|
||||
const auto list = std::array{
|
||||
L"Narrator.exe",
|
||||
L"nvda.exe",
|
||||
L"jfw.exe",
|
||||
L"Zt.exe",
|
||||
};
|
||||
for (const auto &entry : list) {
|
||||
if (_wcsicmp(name, entry) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Use !isHidden() for logic checks, not isVisible()
|
||||
|
||||
When you call `show()` / `hide()` / `setVisible()` on a widget and later branch on that state, always check `!isHidden()` (the widget's own flag) — never `isVisible()`. `isVisible()` returns `true` only when the widget **and every ancestor** are visible, so it silently returns `false` during parent show-animations, before the parent is laid out, etc. `isHidden()` reflects exactly the flag you set.
|
||||
|
||||
```cpp
|
||||
// BAD — breaks when parent is still animating / not yet shown:
|
||||
child->setVisible(true);
|
||||
// ... later, in resizeGetHeight or similar:
|
||||
if (child->isVisible()) { // false if parent isn't visible yet!
|
||||
child->moveToRight(x, y, w);
|
||||
}
|
||||
|
||||
// GOOD — checks the widget's own state:
|
||||
if (!child->isHidden()) {
|
||||
child->moveToRight(x, y, w);
|
||||
}
|
||||
```
|
||||
|
||||
The same applies to any logic that depends on a previous `show()`/`hide()` call: skip blocks, layout branches, opacity decisions, etc.
|
||||
|
||||
## Consolidate make_state calls into a single State struct
|
||||
|
||||
Every `make_state` is a separate heap allocation. When a function needs multiple pieces of lambda-captured mutable state, define a local `struct State` with all fields and call `make_state<State>()` once, then capture the resulting pointer everywhere.
|
||||
|
||||
```cpp
|
||||
// BAD - two allocations:
|
||||
const auto shown = lifetime.make_state<bool>(false);
|
||||
const auto count = lifetime.make_state<int>(0);
|
||||
|
||||
// GOOD - one allocation:
|
||||
struct State {
|
||||
bool shown = false;
|
||||
int count = 0;
|
||||
};
|
||||
const auto state = lifetime.make_state<State>();
|
||||
```
|
||||
|
||||
## Use trailing return type when the return type doesn't fit on one line
|
||||
|
||||
When a function's return type is long enough that the declaration would need a line break between the return type and the function name, use trailing return type syntax (`auto ... -> Type`) to keep the function name on the opening line.
|
||||
|
||||
```cpp
|
||||
// BAD - return type orphaned on its own line:
|
||||
not_null<HistoryView::Controls::ComposeAiButton*>
|
||||
SetupCaptionAiButton(SetupCaptionAiButtonArgs &&args);
|
||||
|
||||
// GOOD - trailing return type keeps name visible:
|
||||
auto SetupCaptionAiButton(SetupCaptionAiButtonArgs &&args)
|
||||
-> not_null<HistoryView::Controls::ComposeAiButton*>;
|
||||
```
|
||||
|
||||
## Mind data structure sizes and alignment
|
||||
|
||||
When adding fields to a class or struct, consider the memory layout. A standalone `bool` between two pointer-sized fields wastes 7 bytes to alignment padding. Review new fields for packing opportunities:
|
||||
|
||||
- If the struct already has bitfields, pack new boolean flags as `: 1` members rather than standalone `bool`.
|
||||
- If alignment leaves a gap (e.g., an `int` followed by a pointer), consider whether a new small field can fill it.
|
||||
- For classes instantiated in large quantities (per-message, per-element, per-row), every wasted byte is multiplied thousands of times.
|
||||
|
||||
```cpp
|
||||
// BAD - standalone bool adds 8 bytes (1 + 7 padding) before the next pointer:
|
||||
mutable bool _myFlag = false;
|
||||
mutable std::unique_ptr<Foo> _foo;
|
||||
|
||||
// GOOD - packed into existing bitfield group, no extra bytes:
|
||||
mutable uint32 _myFlag : 1 = 0;
|
||||
```
|
||||
|
||||
## Static member functions use PascalCase
|
||||
|
||||
Non-static member functions use camelCase (`startBatch`, `finalize`). Static member functions use PascalCase (`ShouldTrack`, `Parse`, `Create`), matching the convention for free functions.
|
||||
|
||||
```cpp
|
||||
// BAD - camelCase for static method:
|
||||
[[nodiscard]] static bool shouldTrack(not_null<HistoryItem*> item);
|
||||
|
||||
// GOOD - PascalCase for static method:
|
||||
[[nodiscard]] static bool ShouldTrack(not_null<HistoryItem*> item);
|
||||
```
|
||||
@@ -16,6 +16,7 @@ add_subdirectory(lib_spellcheck)
|
||||
add_subdirectory(lib_storage)
|
||||
add_subdirectory(lib_lottie)
|
||||
add_subdirectory(lib_qr)
|
||||
add_subdirectory(lib_translate)
|
||||
add_subdirectory(lib_webrtc)
|
||||
add_subdirectory(lib_webview)
|
||||
add_subdirectory(codegen)
|
||||
@@ -39,6 +40,7 @@ include(cmake/td_mtproto.cmake)
|
||||
include(cmake/td_scheme.cmake)
|
||||
include(cmake/td_tde2e.cmake)
|
||||
include(cmake/td_ui.cmake)
|
||||
include(cmake/telegram_apple_swift_runtime.cmake)
|
||||
include(cmake/generate_appstream_changelog.cmake)
|
||||
|
||||
if (DESKTOP_APP_TEST_APPS)
|
||||
@@ -82,6 +84,7 @@ PRIVATE
|
||||
desktop-app::lib_storage
|
||||
desktop-app::lib_lottie
|
||||
desktop-app::lib_qr
|
||||
desktop-app::lib_translate
|
||||
desktop-app::lib_webview
|
||||
desktop-app::lib_ffmpeg
|
||||
desktop-app::lib_stripe
|
||||
@@ -131,6 +134,10 @@ set(ayugram_files
|
||||
ayu/utils/taptic_engine/platform/taptic_engine_mac.h
|
||||
ayu/ui/ayu_logo.cpp
|
||||
ayu/ui/ayu_logo.h
|
||||
ayu/ui/toasts.cpp
|
||||
ayu/ui/toasts.h
|
||||
ayu/ui/ayu_userpic.cpp
|
||||
ayu/ui/ayu_userpic.h
|
||||
ayu/ui/utils/ayu_profile_values.cpp
|
||||
ayu/ui/utils/ayu_profile_values.h
|
||||
ayu/ui/utils/color_cut_quantizer.cpp
|
||||
@@ -141,6 +148,8 @@ set(ayugram_files
|
||||
ayu/ui/utils/palette.h
|
||||
ayu/ui/utils/itunes_search.cpp
|
||||
ayu/ui/utils/itunes_search.h
|
||||
ayu/ui/settings/ayu_builder.cpp
|
||||
ayu/ui/settings/ayu_builder.h
|
||||
ayu/ui/settings/settings_appearance.cpp
|
||||
ayu/ui/settings/settings_appearance.h
|
||||
ayu/ui/settings/settings_ayu_utils.cpp
|
||||
@@ -187,10 +196,16 @@ set(ayugram_files
|
||||
ayu/ui/boxes/donate_qr_box.h
|
||||
ayu/ui/boxes/donate_info_box.cpp
|
||||
ayu/ui/boxes/donate_info_box.h
|
||||
ayu/ui/boxes/plugin_info_box.cpp
|
||||
ayu/ui/boxes/plugin_info_box.h
|
||||
ayu/ui/components/image_view.cpp
|
||||
ayu/ui/components/image_view.h
|
||||
ayu/ui/components/icon_picker.cpp
|
||||
ayu/ui/components/icon_picker.h
|
||||
ayu/ui/components/avatar_corners_preview.cpp
|
||||
ayu/ui/components/avatar_corners_preview.h
|
||||
ayu/ui/components/message_preview.cpp
|
||||
ayu/ui/components/message_preview.h
|
||||
ayu/ui/components/saved_music.cpp
|
||||
ayu/ui/components/saved_music.h
|
||||
ayu/libs/json.hpp
|
||||
@@ -202,26 +217,28 @@ set(ayugram_files
|
||||
ayu/features/streamer_mode/platform/streamer_mode_win.h
|
||||
ayu/features/streamer_mode/platform/streamer_mode_linux.cpp
|
||||
ayu/features/streamer_mode/platform/streamer_mode_linux.h
|
||||
ayu/features/streamer_mode/platform/streamer_mode_mac.cpp
|
||||
ayu/features/streamer_mode/platform/streamer_mode_mac.mm
|
||||
ayu/features/streamer_mode/platform/streamer_mode_mac.h
|
||||
ayu/features/streamer_mode/streamer_mode.cpp
|
||||
ayu/features/streamer_mode/streamer_mode.h
|
||||
ayu/features/message_shot/message_shot.cpp
|
||||
ayu/features/message_shot/message_shot.h
|
||||
ayu/features/message_shot/message_shot_theme_state.cpp
|
||||
ayu/features/message_shot/message_shot_theme_state.h
|
||||
ayu/features/forward/ayu_forward.cpp
|
||||
ayu/features/forward/ayu_forward.h
|
||||
ayu/features/forward/ayu_sync.cpp
|
||||
ayu/features/forward/ayu_sync.h
|
||||
ayu/features/translator/ayu_translator.cpp
|
||||
ayu/features/translator/ayu_translator.h
|
||||
ayu/features/translator/ayu_translate_provider.cpp
|
||||
ayu/features/translator/ayu_translate_provider.h
|
||||
ayu/features/translator/html_parser.cpp
|
||||
ayu/features/translator/html_parser.h
|
||||
ayu/features/translator/implementations/google.cpp
|
||||
ayu/features/translator/implementations/google.h
|
||||
ayu/features/translator/implementations/yandex.cpp
|
||||
ayu/features/translator/implementations/yandex.h
|
||||
ayu/features/translator/implementations/telegram.cpp
|
||||
ayu/features/translator/implementations/telegram.h
|
||||
ayu/features/translator/implementations/base.cpp
|
||||
ayu/features/translator/implementations/base.h
|
||||
ayu/features/filters/filters_controller.cpp
|
||||
@@ -230,8 +247,6 @@ set(ayugram_files
|
||||
ayu/features/filters/filters_cache_controller.h
|
||||
ayu/features/filters/filters_utils.cpp
|
||||
ayu/features/filters/filters_utils.h
|
||||
ayu/features/filters/shadow_ban_utils.cpp
|
||||
ayu/features/filters/shadow_ban_utils.h
|
||||
ayu/data/messages_storage.cpp
|
||||
ayu/data/messages_storage.h
|
||||
ayu/data/entities.h
|
||||
@@ -242,6 +257,10 @@ set(ayugram_files
|
||||
info/profile/info_profile_music_button.h
|
||||
)
|
||||
|
||||
if (NOT DESKTOP_APP_DISABLE_SWIFT6)
|
||||
telegram_add_apple_swift_runtime(Telegram)
|
||||
endif()
|
||||
|
||||
target_precompile_headers(Telegram PRIVATE $<$<COMPILE_LANGUAGE:CXX,OBJCXX>:${src_loc}/stdafx.h>)
|
||||
nice_target_sources(Telegram ${src_loc}
|
||||
PRIVATE
|
||||
@@ -268,10 +287,14 @@ PRIVATE
|
||||
api/api_chat_participants.h
|
||||
api/api_cloud_password.cpp
|
||||
api/api_cloud_password.h
|
||||
data/data_search_calendar.h
|
||||
data/data_search_calendar.cpp
|
||||
api/api_common.cpp
|
||||
api/api_common.h
|
||||
api/api_confirm_phone.cpp
|
||||
api/api_confirm_phone.h
|
||||
api/api_compose_with_ai.cpp
|
||||
api/api_compose_with_ai.h
|
||||
api/api_credits.cpp
|
||||
api/api_credits.h
|
||||
api/api_credits_history_entry.cpp
|
||||
@@ -305,6 +328,10 @@ PRIVATE
|
||||
api/api_premium.h
|
||||
api/api_premium_option.cpp
|
||||
api/api_premium_option.h
|
||||
api/api_reactions_notify_settings.cpp
|
||||
api/api_reactions_notify_settings.h
|
||||
api/api_read_metrics.cpp
|
||||
api/api_read_metrics.h
|
||||
api/api_report.cpp
|
||||
api/api_report.h
|
||||
api/api_ringtones.cpp
|
||||
@@ -325,6 +352,8 @@ PRIVATE
|
||||
api/api_statistics_data_deserialize.h
|
||||
api/api_statistics_sender.cpp
|
||||
api/api_statistics_sender.h
|
||||
api/api_stickers_creator.cpp
|
||||
api/api_stickers_creator.h
|
||||
api/api_suggest_post.cpp
|
||||
api/api_suggest_post.h
|
||||
api/api_text_entities.cpp
|
||||
@@ -361,8 +390,12 @@ PRIVATE
|
||||
boxes/peers/add_bot_to_chat_box.h
|
||||
boxes/peers/add_participants_box.cpp
|
||||
boxes/peers/add_participants_box.h
|
||||
boxes/peers/channel_ownership_transfer.cpp
|
||||
boxes/peers/channel_ownership_transfer.h
|
||||
boxes/peers/choose_peer_box.cpp
|
||||
boxes/peers/choose_peer_box.h
|
||||
boxes/peers/create_managed_bot_box.cpp
|
||||
boxes/peers/create_managed_bot_box.h
|
||||
boxes/peers/edit_contact_box.cpp
|
||||
boxes/peers/edit_contact_box.h
|
||||
boxes/peers/edit_forum_topic_box.cpp
|
||||
@@ -373,6 +406,8 @@ PRIVATE
|
||||
boxes/peers/edit_members_visible.h
|
||||
boxes/peers/edit_participant_box.cpp
|
||||
boxes/peers/edit_participant_box.h
|
||||
boxes/peers/edit_tag_control.cpp
|
||||
boxes/peers/edit_tag_control.h
|
||||
boxes/peers/edit_participants_box.cpp
|
||||
boxes/peers/edit_participants_box.h
|
||||
boxes/peers/edit_peer_color_box.cpp
|
||||
@@ -400,6 +435,8 @@ PRIVATE
|
||||
boxes/peers/prepare_short_info_box.h
|
||||
boxes/peers/replace_boost_box.cpp
|
||||
boxes/peers/replace_boost_box.h
|
||||
boxes/peers/tag_info_box.cpp
|
||||
boxes/peers/tag_info_box.h
|
||||
boxes/peers/verify_peers_box.cpp
|
||||
boxes/peers/verify_peers_box.h
|
||||
boxes/about_box.cpp
|
||||
@@ -472,6 +509,8 @@ PRIVATE
|
||||
boxes/report_messages_box.h
|
||||
boxes/ringtones_box.cpp
|
||||
boxes/ringtones_box.h
|
||||
boxes/select_future_owner_box.cpp
|
||||
boxes/select_future_owner_box.h
|
||||
boxes/self_destruction_box.cpp
|
||||
boxes/self_destruction_box.h
|
||||
boxes/send_credits_box.cpp
|
||||
@@ -480,22 +519,34 @@ PRIVATE
|
||||
boxes/send_gif_with_caption_box.h
|
||||
boxes/send_files_box.cpp
|
||||
boxes/send_files_box.h
|
||||
boxes/send_files_box_reply_header.cpp
|
||||
boxes/send_files_box_reply_header.h
|
||||
boxes/share_box.cpp
|
||||
boxes/share_box.h
|
||||
boxes/star_gift_auction_box.cpp
|
||||
boxes/star_gift_auction_box.h
|
||||
boxes/star_gift_box.cpp
|
||||
boxes/star_gift_box.h
|
||||
boxes/star_gift_cover_box.cpp
|
||||
boxes/star_gift_cover_box.h
|
||||
boxes/star_gift_craft_animation.cpp
|
||||
boxes/star_gift_craft_animation.h
|
||||
boxes/star_gift_craft_box.cpp
|
||||
boxes/star_gift_craft_box.h
|
||||
boxes/star_gift_preview_box.cpp
|
||||
boxes/star_gift_preview_box.h
|
||||
boxes/star_gift_resale_box.cpp
|
||||
boxes/star_gift_resale_box.h
|
||||
boxes/sticker_creator_box.cpp
|
||||
boxes/sticker_creator_box.h
|
||||
boxes/sticker_set_box.cpp
|
||||
boxes/sticker_set_box.h
|
||||
boxes/stickers_box.cpp
|
||||
boxes/stickers_box.h
|
||||
boxes/transfer_gift_box.cpp
|
||||
boxes/transfer_gift_box.h
|
||||
boxes/compose_ai_box.cpp
|
||||
boxes/compose_ai_box.h
|
||||
boxes/translate_box.cpp
|
||||
boxes/translate_box.h
|
||||
boxes/url_auth_box.cpp
|
||||
@@ -583,6 +634,8 @@ PRIVATE
|
||||
chat_helpers/emoji_keywords.h
|
||||
chat_helpers/emoji_list_widget.cpp
|
||||
chat_helpers/emoji_list_widget.h
|
||||
chat_helpers/emoji_picker_overlay.cpp
|
||||
chat_helpers/emoji_picker_overlay.h
|
||||
chat_helpers/emoji_sets_manager.cpp
|
||||
chat_helpers/emoji_sets_manager.h
|
||||
chat_helpers/emoji_suggestions_widget.cpp
|
||||
@@ -619,6 +672,9 @@ PRIVATE
|
||||
chat_helpers/ttl_media_layer_widget.h
|
||||
core/application.cpp
|
||||
core/application.h
|
||||
core/proxy_rotation_manager.cpp
|
||||
core/proxy_rotation_manager.h
|
||||
core/cached_webview_availability.h
|
||||
core/bank_card_click_handler.cpp
|
||||
core/bank_card_click_handler.h
|
||||
core/base_integration.cpp
|
||||
@@ -639,6 +695,17 @@ PRIVATE
|
||||
core/crash_reports.h
|
||||
core/credits_amount.h
|
||||
core/deadlock_detector.h
|
||||
core/deep_links/deep_links_chats.cpp
|
||||
core/deep_links/deep_links_chats.h
|
||||
core/deep_links/deep_links_contacts.cpp
|
||||
core/deep_links/deep_links_contacts.h
|
||||
core/deep_links/deep_links_new.cpp
|
||||
core/deep_links/deep_links_new.h
|
||||
core/deep_links/deep_links_router.cpp
|
||||
core/deep_links/deep_links_router.h
|
||||
core/deep_links/deep_links_settings.cpp
|
||||
core/deep_links/deep_links_settings.h
|
||||
core/deep_links/deep_links_types.h
|
||||
core/file_utilities.cpp
|
||||
core/file_utilities.h
|
||||
core/launcher.cpp
|
||||
@@ -796,6 +863,8 @@ PRIVATE
|
||||
data/data_photo_media.h
|
||||
data/data_poll.cpp
|
||||
data/data_poll.h
|
||||
data/data_poll_messages.cpp
|
||||
data/data_poll_messages.h
|
||||
data/data_premium_limits.cpp
|
||||
data/data_premium_limits.h
|
||||
data/data_pts_waiter.cpp
|
||||
@@ -928,6 +997,10 @@ PRIVATE
|
||||
history/admin_log/history_admin_log_section.cpp
|
||||
history/admin_log/history_admin_log_section.h
|
||||
history/view/controls/compose_controls_common.h
|
||||
history/view/controls/history_view_compose_ai_button.cpp
|
||||
history/view/controls/history_view_compose_ai_button.h
|
||||
history/view/controls/history_view_compose_ai_tooltip.cpp
|
||||
history/view/controls/history_view_compose_ai_tooltip.h
|
||||
history/view/controls/history_view_compose_controls.cpp
|
||||
history/view/controls/history_view_compose_controls.h
|
||||
history/view/controls/history_view_compose_media_edit_manager.cpp
|
||||
@@ -984,10 +1057,14 @@ PRIVATE
|
||||
history/view/media/history_view_media_spoiler.h
|
||||
history/view/media/history_view_media_unwrapped.cpp
|
||||
history/view/media/history_view_media_unwrapped.h
|
||||
history/view/media/history_view_no_forwards_request.cpp
|
||||
history/view/media/history_view_no_forwards_request.h
|
||||
history/view/media/history_view_photo.cpp
|
||||
history/view/media/history_view_photo.h
|
||||
history/view/media/history_view_poll.cpp
|
||||
history/view/media/history_view_poll.h
|
||||
history/view/media/menu/history_view_poll_menu.cpp
|
||||
history/view/media/menu/history_view_poll_menu.h
|
||||
history/view/media/history_view_premium_gift.cpp
|
||||
history/view/media/history_view_premium_gift.h
|
||||
history/view/media/history_view_save_document_action.cpp
|
||||
@@ -1029,6 +1106,8 @@ PRIVATE
|
||||
history/view/reactions/history_view_reactions_strip.h
|
||||
history/view/reactions/history_view_reactions_tabs.cpp
|
||||
history/view/reactions/history_view_reactions_tabs.h
|
||||
history/view/history_view_top_peers_selector.cpp
|
||||
history/view/history_view_top_peers_selector.h
|
||||
history/view/history_view_about_view.cpp
|
||||
history/view/history_view_about_view.h
|
||||
history/view/history_view_bottom_info.cpp
|
||||
@@ -1045,6 +1124,12 @@ PRIVATE
|
||||
history/view/history_view_corner_buttons.h
|
||||
history/view/history_view_cursor_state.cpp
|
||||
history/view/history_view_cursor_state.h
|
||||
history/view/history_view_draw_to_reply.cpp
|
||||
history/view/history_view_draw_to_reply.h
|
||||
history/view/history_view_add_poll_option.cpp
|
||||
history/view/history_view_add_poll_option.h
|
||||
history/view/history_view_element_overlay.cpp
|
||||
history/view/history_view_element_overlay.h
|
||||
history/view/history_view_element.cpp
|
||||
history/view/history_view_element.h
|
||||
history/view/history_view_emoji_interactions.cpp
|
||||
@@ -1075,8 +1160,12 @@ PRIVATE
|
||||
history/view/history_view_quick_action.h
|
||||
history/view/history_view_reaction_preview.cpp
|
||||
history/view/history_view_reaction_preview.h
|
||||
history/view/history_view_read_metrics_tracker.cpp
|
||||
history/view/history_view_read_metrics_tracker.h
|
||||
history/view/history_view_reply.cpp
|
||||
history/view/history_view_reply.h
|
||||
history/view/history_view_reply_button.cpp
|
||||
history/view/history_view_reply_button.h
|
||||
history/view/history_view_requests_bar.cpp
|
||||
history/view/history_view_requests_bar.h
|
||||
history/view/history_view_schedule_box.cpp
|
||||
@@ -1095,6 +1184,8 @@ PRIVATE
|
||||
history/view/history_view_sticker_toast.h
|
||||
history/view/history_view_subsection_tabs.cpp
|
||||
history/view/history_view_subsection_tabs.h
|
||||
history/view/history_view_summary_header.cpp
|
||||
history/view/history_view_summary_header.h
|
||||
history/view/history_view_text_helper.cpp
|
||||
history/view/history_view_text_helper.h
|
||||
history/view/history_view_transcribe_button.cpp
|
||||
@@ -1203,6 +1294,8 @@ PRIVATE
|
||||
info/peer_gifts/info_peer_gifts_common.h
|
||||
info/peer_gifts/info_peer_gifts_widget.cpp
|
||||
info/peer_gifts/info_peer_gifts_widget.h
|
||||
info/polls/info_polls_list_widget.cpp
|
||||
info/polls/info_polls_list_widget.h
|
||||
info/polls/info_polls_results_inner_widget.cpp
|
||||
info/polls/info_polls_results_inner_widget.h
|
||||
info/polls/info_polls_results_widget.cpp
|
||||
@@ -1342,6 +1435,12 @@ PRIVATE
|
||||
lang/lang_numbers_animation.h
|
||||
lang/lang_translator.cpp
|
||||
lang/lang_translator.h
|
||||
lang/translate_mtproto_provider.cpp
|
||||
lang/translate_mtproto_provider.h
|
||||
lang/translate_provider.cpp
|
||||
lang/translate_provider.h
|
||||
lang/translate_url_provider.cpp
|
||||
lang/translate_url_provider.h
|
||||
layout/layout_document_generic_preview.cpp
|
||||
layout/layout_document_generic_preview.h
|
||||
layout/layout_item_base.cpp
|
||||
@@ -1364,6 +1463,8 @@ PRIVATE
|
||||
main/session/session_show.h
|
||||
media/audio/media_audio.cpp
|
||||
media/audio/media_audio.h
|
||||
media/audio/media_audio_edit.cpp
|
||||
media/audio/media_audio_edit.h
|
||||
media/audio/media_audio_capture.cpp
|
||||
media/audio/media_audio_capture.h
|
||||
media/audio/media_audio_capture_common.h
|
||||
@@ -1383,6 +1484,8 @@ PRIVATE
|
||||
media/player/media_player_float.h
|
||||
media/player/media_player_instance.cpp
|
||||
media/player/media_player_instance.h
|
||||
media/player/media_player_listen_tracker.cpp
|
||||
media/player/media_player_listen_tracker.h
|
||||
media/player/media_player_panel.cpp
|
||||
media/player/media_player_panel.h
|
||||
media/player/media_player_volume_controller.cpp
|
||||
@@ -1471,6 +1574,8 @@ PRIVATE
|
||||
media/system_media_controls_manager.cpp
|
||||
menu/menu_antispam_validator.cpp
|
||||
menu/menu_antispam_validator.h
|
||||
menu/menu_dock.cpp
|
||||
menu/menu_dock.h
|
||||
menu/menu_item_download_files.cpp
|
||||
menu/menu_item_download_files.h
|
||||
menu/menu_item_rate_transcribe_session.cpp
|
||||
@@ -1500,6 +1605,8 @@ PRIVATE
|
||||
mtproto/facade.h
|
||||
mtproto/mtp_instance.cpp
|
||||
mtproto/mtp_instance.h
|
||||
mtproto/proxy_check.cpp
|
||||
mtproto/proxy_check.h
|
||||
mtproto/sender.h
|
||||
mtproto/session.cpp
|
||||
mtproto/session.h
|
||||
@@ -1513,6 +1620,8 @@ PRIVATE
|
||||
overview/overview_layout.cpp
|
||||
overview/overview_layout.h
|
||||
overview/overview_layout_delegate.h
|
||||
poll/poll_media_upload.cpp
|
||||
poll/poll_media_upload.h
|
||||
passport/passport_encryption.cpp
|
||||
passport/passport_encryption.h
|
||||
passport/passport_form_controller.cpp
|
||||
@@ -1554,11 +1663,15 @@ PRIVATE
|
||||
platform/linux/overlay_widget_linux.h
|
||||
platform/linux/specific_linux.cpp
|
||||
platform/linux/specific_linux.h
|
||||
platform/linux/translate_provider_linux.cpp
|
||||
platform/linux/translate_provider_linux.h
|
||||
platform/linux/tray_linux.cpp
|
||||
platform/linux/tray_linux.h
|
||||
platform/linux/webauthn_linux.cpp
|
||||
platform/mac/file_utilities_mac.mm
|
||||
platform/mac/file_utilities_mac.h
|
||||
platform/mac/global_menu_mac.h
|
||||
platform/mac/global_menu_mac.mm
|
||||
platform/mac/launcher_mac.mm
|
||||
platform/mac/launcher_mac.h
|
||||
platform/mac/integration_mac.mm
|
||||
@@ -1574,8 +1687,10 @@ PRIVATE
|
||||
platform/mac/specific_mac.h
|
||||
platform/mac/specific_mac_p.mm
|
||||
platform/mac/specific_mac_p.h
|
||||
platform/mac/tray_mac.mm
|
||||
platform/mac/translate_provider_mac.h
|
||||
platform/mac/translate_provider_mac.mm
|
||||
platform/mac/tray_mac.h
|
||||
platform/mac/tray_mac.mm
|
||||
platform/mac/webauthn_mac.mm
|
||||
platform/mac/window_title_mac.mm
|
||||
platform/mac/touchbar/items/mac_formatter_item.h
|
||||
@@ -1609,6 +1724,7 @@ PRIVATE
|
||||
platform/win/overlay_widget_win.h
|
||||
platform/win/specific_win.cpp
|
||||
platform/win/specific_win.h
|
||||
platform/win/translate_provider_win.h
|
||||
platform/win/tray_win.cpp
|
||||
platform/win/tray_win.h
|
||||
platform/win/webauthn_win.cpp
|
||||
@@ -1629,11 +1745,10 @@ PRIVATE
|
||||
platform/platform_overlay_widget.cpp
|
||||
platform/platform_overlay_widget.h
|
||||
platform/platform_specific.h
|
||||
platform/platform_translate_provider.h
|
||||
platform/platform_tray.h
|
||||
platform/platform_webauthn.h
|
||||
platform/platform_window_title.h
|
||||
profile/profile_back_button.cpp
|
||||
profile/profile_back_button.h
|
||||
profile/profile_block_widget.cpp
|
||||
profile/profile_block_widget.h
|
||||
profile/profile_cover_drop_area.cpp
|
||||
@@ -1678,61 +1793,73 @@ PRIVATE
|
||||
settings/cloud_password/settings_cloud_password_step.h
|
||||
settings/cloud_password/settings_cloud_password_validate_icon.cpp
|
||||
settings/cloud_password/settings_cloud_password_validate_icon.h
|
||||
settings/settings_active_sessions.cpp
|
||||
settings/settings_active_sessions.h
|
||||
settings/settings_advanced.cpp
|
||||
settings/settings_advanced.h
|
||||
settings/settings_blocked_peers.cpp
|
||||
settings/settings_blocked_peers.h
|
||||
settings/settings_business.cpp
|
||||
settings/settings_business.h
|
||||
settings/settings_chat.cpp
|
||||
settings/settings_chat.h
|
||||
settings/settings_calls.cpp
|
||||
settings/settings_calls.h
|
||||
settings/sections/settings_active_sessions.cpp
|
||||
settings/sections/settings_active_sessions.h
|
||||
settings/sections/settings_advanced.cpp
|
||||
settings/sections/settings_advanced.h
|
||||
settings/sections/settings_chat.cpp
|
||||
settings/sections/settings_chat.h
|
||||
settings/sections/settings_blocked_peers.cpp
|
||||
settings/sections/settings_blocked_peers.h
|
||||
settings/sections/settings_business.cpp
|
||||
settings/sections/settings_business.h
|
||||
settings/sections/settings_calls.cpp
|
||||
settings/sections/settings_calls.h
|
||||
settings/settings_codes.cpp
|
||||
settings/settings_codes.h
|
||||
settings/settings_common_session.cpp
|
||||
settings/settings_common_session.h
|
||||
settings/settings_credits.cpp
|
||||
settings/settings_credits.h
|
||||
settings/detailed_settings_button.cpp
|
||||
settings/detailed_settings_button.h
|
||||
settings/sections/settings_credits.cpp
|
||||
settings/sections/settings_credits.h
|
||||
settings/settings_credits_graphics.cpp
|
||||
settings/settings_credits_graphics.h
|
||||
settings/settings_experimental.cpp
|
||||
settings/settings_experimental.h
|
||||
settings/settings_folders.cpp
|
||||
settings/settings_folders.h
|
||||
settings/settings_global_ttl.cpp
|
||||
settings/settings_global_ttl.h
|
||||
settings/settings_information.cpp
|
||||
settings/settings_information.h
|
||||
settings/sections/settings_folders.cpp
|
||||
settings/sections/settings_folders.h
|
||||
settings/sections/settings_global_ttl.cpp
|
||||
settings/sections/settings_global_ttl.h
|
||||
settings/sections/settings_information.cpp
|
||||
settings/sections/settings_information.h
|
||||
settings/settings_intro.cpp
|
||||
settings/settings_intro.h
|
||||
settings/settings_local_passcode.cpp
|
||||
settings/settings_local_passcode.h
|
||||
settings/settings_main.cpp
|
||||
settings/settings_main.h
|
||||
settings/settings_notifications.cpp
|
||||
settings/settings_notifications.h
|
||||
settings/settings_notifications_type.cpp
|
||||
settings/settings_notifications_type.h
|
||||
settings/settings_passkeys.cpp
|
||||
settings/settings_passkeys.h
|
||||
settings/sections/settings_local_passcode.cpp
|
||||
settings/sections/settings_local_passcode.h
|
||||
settings/sections/settings_main.cpp
|
||||
settings/sections/settings_main.h
|
||||
settings/settings_recent_searches.cpp
|
||||
settings/settings_recent_searches.h
|
||||
settings/settings_search.cpp
|
||||
settings/settings_search.h
|
||||
settings/settings_faq_suggestions.cpp
|
||||
settings/settings_faq_suggestions.h
|
||||
settings/settings_builder.cpp
|
||||
settings/settings_builder.h
|
||||
settings/sections/settings_notifications.cpp
|
||||
settings/sections/settings_notifications.h
|
||||
settings/sections/settings_privacy_security.cpp
|
||||
settings/sections/settings_privacy_security.h
|
||||
settings/sections/settings_notifications_reactions.cpp
|
||||
settings/sections/settings_notifications_reactions.h
|
||||
settings/sections/settings_notifications_type.cpp
|
||||
settings/sections/settings_notifications_type.h
|
||||
settings/sections/settings_passkeys.cpp
|
||||
settings/sections/settings_passkeys.h
|
||||
settings/settings_power_saving.cpp
|
||||
settings/settings_power_saving.h
|
||||
settings/settings_premium.cpp
|
||||
settings/settings_premium.h
|
||||
settings/sections/settings_premium.cpp
|
||||
settings/sections/settings_premium.h
|
||||
settings/settings_privacy_controllers.cpp
|
||||
settings/settings_privacy_controllers.h
|
||||
settings/settings_privacy_security.cpp
|
||||
settings/settings_privacy_security.h
|
||||
settings/settings_scale_preview.cpp
|
||||
settings/settings_scale_preview.h
|
||||
settings/settings_shortcuts.cpp
|
||||
settings/settings_shortcuts.h
|
||||
settings/settings_type.h
|
||||
settings/settings_websites.cpp
|
||||
settings/settings_websites.h
|
||||
settings/sections/settings_shortcuts.cpp
|
||||
settings/sections/settings_shortcuts.h
|
||||
settings/sections/settings_websites.cpp
|
||||
settings/sections/settings_websites.h
|
||||
storage/details/storage_file_utilities.cpp
|
||||
storage/details/storage_file_utilities.h
|
||||
storage/details/storage_settings_scheme.cpp
|
||||
@@ -1789,6 +1916,8 @@ PRIVATE
|
||||
tde2e/tde2e_integration.h
|
||||
ui/boxes/edit_invite_link_session.cpp
|
||||
ui/boxes/edit_invite_link_session.h
|
||||
ui/boxes/emoji_stake_box.cpp
|
||||
ui/boxes/emoji_stake_box.h
|
||||
ui/boxes/peer_qr_box.cpp
|
||||
ui/boxes/peer_qr_box.h
|
||||
ui/chat/attach/attach_item_single_file_preview.cpp
|
||||
@@ -1801,6 +1930,8 @@ PRIVATE
|
||||
ui/chat/choose_theme_controller.h
|
||||
ui/chat/sponsored_message_bar.cpp
|
||||
ui/chat/sponsored_message_bar.h
|
||||
ui/controls/compose_ai_button_factory.cpp
|
||||
ui/controls/compose_ai_button_factory.h
|
||||
ui/controls/emoji_button_factory.cpp
|
||||
ui/controls/emoji_button_factory.h
|
||||
ui/controls/location_picker.cpp
|
||||
@@ -1891,6 +2022,7 @@ PRIVATE
|
||||
window/window_section_common.h
|
||||
window/window_separate_id.cpp
|
||||
window/window_separate_id.h
|
||||
window/session/window_session_media.cpp
|
||||
window/window_session_controller.cpp
|
||||
window/window_session_controller.h
|
||||
window/window_session_controller_link_info.h
|
||||
@@ -1928,10 +2060,23 @@ PRIVATE
|
||||
settings.cpp
|
||||
settings.h
|
||||
stdafx.h
|
||||
tray_accounts_menu.h
|
||||
tray.cpp
|
||||
tray.h
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
nice_target_sources(Telegram ${src_loc}
|
||||
PRIVATE
|
||||
tray_accounts_menu.cpp
|
||||
)
|
||||
else()
|
||||
nice_target_sources(Telegram ${src_loc}
|
||||
PRIVATE
|
||||
tray_accounts_menu_dummy.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (NOT build_winstore)
|
||||
remove_target_sources(Telegram ${src_loc}
|
||||
platform/win/windows_start_task.cpp
|
||||
@@ -2355,12 +2500,19 @@ if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
||||
generate_appstream_changelog(Telegram "${CMAKE_SOURCE_DIR}/changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/com.ayugram.desktop.metainfo.xml")
|
||||
install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install(FILES "Resources/art/icon16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon16@2x.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16@2/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon32.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon32@2x.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32@2/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon48.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon48@2x.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48@2/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon64.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/64x64/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon64@2x.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/64x64@2/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon128.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon128@2x.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128@2/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon256@2x.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256@2/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/art/icon512@2x.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512@2/apps" RENAME "com.ayugram.desktop.png")
|
||||
install(FILES "Resources/icons/tray_monochrome.svg" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/symbolic/apps" RENAME "com.ayugram.desktop-symbolic.svg")
|
||||
install(FILES "Resources/icons/tray_monochrome_attention.svg" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/symbolic/apps" RENAME "com.ayugram.desktop-attention-symbolic.svg")
|
||||
install(FILES "Resources/icons/tray_monochrome_mute.svg" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/symbolic/apps" RENAME "com.ayugram.desktop-mute-symbolic.svg")
|
||||
|
||||
|
After Width: | Height: | Size: 68 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" fill="none"><path fill="#F9F8FE" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#C1C2D3" d="M66.2 69c0-5.9 4.2-12.7 9.4-15.3l35.2-17.5c4-2 7.2 0 7.2 4.4v46c0 6.2-4.4 13.3-9.8 16l-33 16.4c-5 2.5-9 0-9-5.5V68.9Z"/><path fill="#D8DDEB" d="M10.8 40.3c0-4.2 3-6 6.7-4.2L53 53.9c5.2 2.6 9.4 9.4 9.4 15.2v44.3c0 5.7-4 8.2-9.1 5.7l-32.8-16.4a19.4 19.4 0 0 1-9.9-15.9V40.3Z"/><path fill="#F2F1F8" d="m73.5 7.2 36 18c5.3 2.6 5.3 7 0 9.6l-35.9 18a24.1 24.1 0 0 1-19.1 0l-36-18c-5.3-2.7-5.3-7 0-9.6l35.9-18a24.1 24.1 0 0 1 19.1 0Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.7" d="m81 47.1-3.9 2m-4 2h-.3c-.5.4-1.2.6-1.9.9"/><path stroke="#939BAC" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.5" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#191919" d="M85 65.3c0 4.1-2.6 8.7-6 10.4-3.2 1.7-5.9-.3-5.9-4.4 0-4 2.7-8.6 6-10.3 3.3-1.6 6 .3 6 4.3ZM111.3 84.4c0 4-2.6 8.7-6 10.3-3.3 1.7-6-.2-6-4.3 0-4 2.8-8.7 6-10.3 3.4-1.7 6 .3 6 4.3ZM105.4 62.5c-3.4 1.7-6-.3-6-4.3s2.6-8.7 6-10.3c3.3-1.7 6 .3 6 4.3s-2.7 8.7-6 10.3ZM79 107.9c-3.2 1.6-5.9-.3-5.9-4.3 0-4.1 2.7-8.7 6-10.4 3.3-1.6 6 .3 6 4.4 0 4-2.7 8.6-6 10.3ZM43.6 65.4c0 4 2.7 8.7 6 10.3 3.3 1.7 6-.3 6-4.3 0-4.1-2.7-8.7-6-10.4-3.3-1.6-6 .3-6 4.4ZM17.3 84.4c0 4 2.7 8.7 6 10.4 3.3 1.6 6-.3 6-4.4 0-4-2.7-8.7-6-10.3-3.3-1.7-6 .3-6 4.3ZM23.3 62.6c3.3 1.6 6-.3 6-4.4 0-4-2.7-8.6-6-10.3-3.3-1.7-6 .3-6 4.3s2.7 8.7 6 10.4ZM49.6 107.9c3.3 1.6 6-.3 6-4.3s-2.7-8.7-6-10.4c-3.3-1.6-6 .3-6 4.4 0 4 2.7 8.6 6 10.3ZM30.5 74.9c0 4 2.7 8.6 6 10.3 3.3 1.6 6-.3 6-4.4 0-4-2.7-8.7-6-10.3-3.3-1.7-6 .3-6 4.4Z"/><path fill="#E72525" d="M56 30.1c-.3-2.3 3.2-4.3 8-4.4 4.6-.2 8.6 1.6 8.9 4 .3 2.3-3.3 4.3-8 4.4-4.6.2-8.6-1.6-9-4Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".3" stroke-width="3.1" d="M61.8 28.8s2.8-.6 5.1.2m15.3 37.6s0 1.5-1 2.9m27-15.3s0 1.5-1 2.9m-25 41.8s0 1.5-1 2.8m27-15.2s0 1.4-1 2.8m-60.5 9.4s0 1.4.9 2.8M20.3 86.2s.2 1.5 1 2.9m25.4-22.5s0 1.5.9 2.9M20.3 54.2s.2 1.5 1 2.9M34 76.9s0 1.5 1 2.9"/></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" fill="none"><path fill="#F9F8FE" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#C1C2D3" d="M66.2 69c0-5.9 4.2-12.7 9.4-15.3l35.2-17.5c4-2 7.2 0 7.2 4.4v46c0 6.2-4.4 13.3-9.8 16l-33 16.4c-5 2.5-9 0-9-5.5V68.9Z"/><path fill="#D8DDEB" d="M10.8 40.3c0-4.2 3-6 6.7-4.2L53 53.9c5.2 2.6 9.4 9.4 9.4 15.2v44.3c0 5.7-4 8.2-9.1 5.7l-32.8-16.4a19.4 19.4 0 0 1-9.9-15.9V40.3Z"/><path fill="#F2F1F8" d="m73.5 7.2 36 18c5.3 2.6 5.3 7 0 9.6l-35.9 18a24.1 24.1 0 0 1-19.1 0l-36-18c-5.3-2.7-5.3-7 0-9.6l35.9-18a24.1 24.1 0 0 1 19.1 0Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.7" d="m81 47.1-3.9 2m-4 2h-.3c-.5.4-1.2.6-1.9.9"/><path stroke="#939BAC" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.5" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#E72525" d="M86.3 81c0-4 2.7-8.7 6-10.3 3.3-1.7 6 .2 6 4.3 0 4-2.7 8.7-6 10.3-3.3 1.7-6-.3-6-4.3Z"/><path fill="#191919" d="M43.6 97.6c0-4 2.7-6 6-4.4 3.3 1.7 6 6.3 6 10.4 0 4-2.7 6-6 4.3-3.3-1.6-6-6.3-6-10.3ZM17.3 52.2c0-4 2.7-6 6-4.3 3.3 1.7 6 6.3 6 10.3 0 4-2.7 6-6 4.4-3.3-1.7-6-6.3-6-10.4ZM36.5 85.2c-3.3-1.7-6-6.3-6-10.3 0-4 2.7-6 6-4.4 3.3 1.7 6 6.3 6 10.4 0 4-2.7 6-6 4.3ZM97.1 27.7c3 1.9 2.5 4.5-1.1 6a15 15 0 0 1-12-.5c-3-1.9-2.4-4.5 1.2-6a15 15 0 0 1 12 .5ZM45.2 26.4c3 1.8 2.5 4.5-1.1 6a15 15 0 0 1-12-.6c-3-1.8-2.4-4.5 1.2-6a15 15 0 0 1 12 .6Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".3" stroke-width="3.1" d="M90.2 28.6s1.5 0 3 .4m-55-1.8s1.6 0 3 .5m54 48.8s0 1.5-1 2.8M46.8 98.7s0 1.4.9 2.8M20.3 54.2s.2 1.5 1 2.9M34 76.9s0 1.5 1 2.9"/></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" fill="none"><path fill="#F9F8FE" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#C1C2D3" d="M66.2 69c0-5.9 4.2-12.7 9.4-15.3l35.2-17.5c4-2 7.2 0 7.2 4.4v46c0 6.2-4.4 13.3-9.8 16l-33 16.4c-5 2.5-9 0-9-5.5V68.9Z"/><path fill="#D8DDEB" d="M10.8 40.3c0-4.2 3-6 6.7-4.2L53 53.9c5.2 2.6 9.4 9.4 9.4 15.2v44.3c0 5.7-4 8.2-9.1 5.7l-32.8-16.4a19.4 19.4 0 0 1-9.9-15.9V40.3Z"/><path fill="#191919" d="M79 93.2c3.3-1.6 6 .3 6 4.4 0 4-2.7 8.7-6 10.3-3.3 1.7-6-.3-6-4.3s2.7-8.7 6-10.4ZM105.3 48c3.3-1.7 6 .2 6 4.2 0 4.1-2.7 8.7-6 10.4-3.3 1.6-6-.3-6-4.4 0-4 2.7-8.6 6-10.3Z"/><path fill="#E72525" d="M36.3 70.6c3.3 1.7 6 6.3 6 10.3 0 4-2.7 6-6 4.4-3.3-1.7-6-6.3-6-10.4 0-4 2.7-6 6-4.3Z"/><path fill="#F2F1F8" d="m73.5 7.2 36 18c5.3 2.6 5.3 7 0 9.6l-35.9 18a24.1 24.1 0 0 1-19.1 0l-36-18c-5.3-2.7-5.3-7 0-9.6l35.9-18a24.1 24.1 0 0 1 19.1 0Z"/><path fill="#191919" d="M97.1 27.7c3 1.8 2.5 4.5-1.1 6a15 15 0 0 1-12-.6c-3-1.8-2.4-4.4 1.2-6a15 15 0 0 1 12 .6ZM70.8 27.3c3 1.8 2.5 4.5-1.1 6a15 15 0 0 1-12-.6c-3-1.8-2.4-4.5 1.2-6a15 15 0 0 1 12 .6ZM45.2 26.4c3 1.8 2.5 4.5-1.1 6a15 15 0 0 1-12-.6c-3-1.8-2.5-4.5 1.2-6a15 15 0 0 1 12 .6Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".3" stroke-width="3.1" d="M90.2 28.6s1.5 0 3 .4m-29.3-.8s1.5 0 3 .4m-28.6-1.4s1.5 0 3 .5m67 26.5s-.2 1.5-1 2.9M82.2 98.9s0 1.5-1 2.8M34 77s0 1.5 1 2.9"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.7" d="m81 47.1-3.9 2m-4 2h-.3c-.5.4-1.2.6-1.9.9"/><path stroke="#939BAC" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.5" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" fill="none"><path fill="#F9F8FE" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#C1C2D3" d="M66.2 69c0-5.9 4.2-12.7 9.4-15.3l35.2-17.5c4-2 7.2 0 7.2 4.4v46c0 6.2-4.4 13.3-9.8 16l-33 16.4c-5 2.5-9 0-9-5.5V68.9Z"/><path fill="#D8DDEB" d="M10.8 40.3c0-4.2 3-6 6.7-4.2L53 53.9c5.2 2.6 9.4 9.4 9.4 15.2v44.3c0 5.7-4 8.2-9.1 5.7l-32.8-16.4a19.4 19.4 0 0 1-9.9-15.9V40.3Z"/><path fill="#F2F1F8" d="m73.5 7.2 36 18c5.3 2.6 5.3 7 0 9.6l-35.9 18a24.1 24.1 0 0 1-19.1 0l-36-18c-5.3-2.7-5.3-7 0-9.6l35.9-18a24.1 24.1 0 0 1 19.1 0Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.7" d="m81 47.1-3.9 2m-4 2h-.3c-.5.4-1.2.6-1.9.9"/><path stroke="#939BAC" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.5" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#E72525" d="M36.5 85.2c-3.3-1.7-6-6.3-6-10.3 0-4 2.7-6 6-4.4 3.3 1.7 6 6.3 6 10.4 0 4-2.7 6-6 4.3Z"/><path fill="#191919" d="M79 75.7c3.3-1.6 6-6.3 6-10.3 0-4-2.7-6-6-4.3-3.3 1.6-6 6.2-6 10.3 0 4 2.7 6 6 4.3ZM105.3 94.8c3.3-1.7 6-6.3 6-10.3 0-4-2.7-6-6-4.4-3.3 1.6-6 6.3-6 10.3 0 4 2.7 6 6 4.4ZM85 97.6c0-4-2.7-6-6-4.4-3.3 1.7-6 6.3-6 10.4 0 4 2.7 6 6 4.3 3.3-1.6 6-6.3 6-10.3ZM111.3 52.3c0-4.1-2.7-6-6-4.4-3.3 1.7-6 6.3-6 10.3 0 4 2.7 6 6 4.4 3.3-1.7 6-6.3 6-10.3ZM92 85.2c3.4-1.7 6-6.3 6-10.3 0-4-2.6-6-6-4.4-3.2 1.7-6 6.3-6 10.4 0 4 2.8 6 6 4.3ZM97.1 27.7c3 1.9 2.5 4.5-1.1 6a15 15 0 0 1-12-.5c-3-1.9-2.4-4.5 1.2-6a15 15 0 0 1 12 .5ZM70.5 40.3c3 1.8 2.4 4.5-1.2 6a15 15 0 0 1-12-.6c-2.9-1.8-2.4-4.5 1.2-6a15 15 0 0 1 12 .6ZM71.6 16c3 1.9 2.5 4.6-1.1 6a15 15 0 0 1-12-.5c-3-1.8-2.4-4.5 1.2-6a15 15 0 0 1 12 .6ZM45.2 26.4c3 1.8 2.5 4.5-1.1 6a15 15 0 0 1-12-.6c-3-1.8-2.5-4.5 1.1-6a15 15 0 0 1 12 .6Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".3" stroke-width="3.1" d="M90.2 28.6s1.5 0 3 .4M63.6 41.1s1.5 0 3 .5m-1.9-24.7s1.5 0 3 .5m-29.4 9.8s1.5 0 3 .5m40.9 39s0 1.4-1 2.8m27-15.3s0 1.5-1 2.9m-25 41.8s0 1.5-1 2.9m27-15.3s0 1.4-1 2.8m-12-12.8s0 1.5-1 2.9M34 76.9s0 1.5 1 2.9"/></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" fill="none"><path fill="#F9F8FE" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#C1C2D3" d="M66.2 69c0-5.9 4.2-12.7 9.4-15.3l35.2-17.5c4-2 7.2 0 7.2 4.4v46c0 6.2-4.4 13.3-9.8 16l-33 16.4c-5 2.5-9 0-9-5.5V68.9Z"/><path fill="#D8DDEB" d="M10.8 40.3c0-4.2 3-6 6.7-4.2L53 53.9c5.2 2.6 9.4 9.4 9.4 15.2v44.3c0 5.7-4 8.2-9.1 5.7l-32.8-16.4a19.4 19.4 0 0 1-9.9-15.9V40.3Z"/><path fill="#F2F1F8" d="m73.5 7.2 36 18c5.3 2.6 5.3 7 0 9.6l-35.9 18a24.1 24.1 0 0 1-19.1 0l-36-18c-5.3-2.7-5.3-7 0-9.6l35.9-18a24.1 24.1 0 0 1 19.1 0Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.7" d="m81 47.1-3.9 2m-4 2h-.3c-.5.4-1.2.6-1.9.9"/><path stroke="#939BAC" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.5" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#E72525" d="M42.3 80.7c0 4-2.6 6-6 4.4-3.3-1.7-6-6.3-6-10.4 0-4 2.8-6 6-4.3 3.4 1.7 6 6.3 6 10.3Z"/><path fill="#191919" d="M85 65.4c0 4-2.7 8.7-6 10.4-3.3 1.6-6-.3-6-4.4 0-4 2.7-8.6 6-10.3 3.3-1.7 6 .3 6 4.3ZM111.3 84.5c0 4-2.7 8.6-6 10.3-3.3 1.6-6-.3-6-4.3s2.7-8.7 6-10.4c3.3-1.6 6 .3 6 4.4ZM92.1 70.7c3.3-1.6 6 .3 6 4.4 0 4-2.7 8.7-6 10.3-3.3 1.7-6-.3-6-4.3s2.7-8.7 6-10.4ZM45.2 26.4c3 1.8 2.5 4.5-1.1 6a15 15 0 0 1-12-.6c-3-1.8-2.5-4.5 1.1-6a15 15 0 0 1 12 .6ZM71.6 14c3 1.9 2.5 4.6-1 6a15 15 0 0 1-12-.5c-3-1.8-2.5-4.5 1.1-6a15 15 0 0 1 12 .6ZM70.9 27.2c3 1.8 2.5 4.4-1.1 6a15 15 0 0 1-12-.6c-3-1.8-2.5-4.5 1.1-6a15 15 0 0 1 12 .6ZM70.5 40.3c3 1.8 2.4 4.5-1.2 6a15 15 0 0 1-12-.6c-2.9-1.8-2.4-4.5 1.2-6a15 15 0 0 1 12 .6ZM97.1 27.7c3 1.9 2.5 4.5-1.1 6a15 15 0 0 1-12-.6c-3-1.8-2.4-4.4 1.2-6a15 15 0 0 1 12 .6Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".3" stroke-width="3.1" d="M38.3 27.2s1.5 0 3 .5m23.4-12.8s1.5 0 3 .5M64 28s1.5 0 3 .5M63.4 41s1.6 0 3 .5m23.7-13s1.5 0 3 .4m-11 37.6s0 1.5-1 2.9m27 17s0 1.4-1 2.8m-12-12.8s0 1.5-1 2.9M34 76.9s0 1.5 1 2.9"/></svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" fill="none"><path fill="#F9F8FE" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#C1C2D3" d="M66.2 69c0-5.9 4.2-12.7 9.4-15.3l35.2-17.5c4-2 7.2 0 7.2 4.4v46c0 6.2-4.4 13.3-9.8 16l-33 16.4c-5 2.5-9 0-9-5.5V68.9Z"/><path fill="#D8DDEB" d="M10.8 40.3c0-4.2 3-6 6.7-4.2L53 53.9c5.2 2.6 9.4 9.4 9.4 15.2v44.3c0 5.7-4 8.2-9.1 5.7l-32.8-16.4a19.4 19.4 0 0 1-9.9-15.9V40.3Z"/><path fill="#F2F1F8" d="m73.5 7.2 36 18c5.3 2.6 5.3 7 0 9.6l-35.9 18a24.1 24.1 0 0 1-19.1 0l-36-18c-5.3-2.7-5.3-7 0-9.6l35.9-18a24.1 24.1 0 0 1 19.1 0Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.7" d="m81 47.1-3.9 2m-4 2h-.3c-.5.4-1.2.6-1.9.9"/><path stroke="#939BAC" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.5" d="M63.8 5c-2.1.2-4.4.8-6.6 1.5C55 7.3 53 8.2 51.4 9a557 557 0 0 0-17 8.4c-7.7 4-15.9 8.3-19.2 10.7-4 2.9-5.4 7.1-6 11.5-.5 4.4 0 8.9 0 12.2-.1 2.4-.6 11-.7 19.7-.1 8.8 0 17.8 1.4 21.1a19 19 0 0 0 6.2 8.2 51 51 0 0 0 9.1 5.4c3.1 1.5 10.8 5.7 18.8 9.5 8 3.7 16.4 7 20.9 6.8 4-.1 8.9-1.7 12-3.2 9.7-4.5 19-9.4 28.4-14.1 6.5-3.3 11.5-7.3 13.4-15 1.8-7.6 1.8-45.6.6-53.3-1-6.3-9.3-11.5-16-14.8C97.2 19 73.3 4.6 63.9 5Z"/><path fill="#191919" d="M79 93.3c3.4-1.7 6 .2 6 4.3 0 4-2.6 8.7-6 10.3-3.2 1.7-5.9-.3-5.9-4.3s2.7-8.7 6-10.3ZM105.4 48c3.3-1.7 6 .2 6 4.2s-2.7 8.7-6 10.4c-3.3 1.6-6-.3-6-4.4 0-4 2.7-8.6 6-10.3ZM86.3 81c0-4 2.7-8.7 6-10.4 3.3-1.6 6 .3 6 4.4 0 4-2.7 8.7-6 10.3-3.3 1.7-6-.3-6-4.3ZM43.6 65.4c0 4 2.7 8.7 6 10.3 3.3 1.7 6-.3 6-4.4 0-4-2.7-8.6-6-10.3-3.3-1.6-6 .3-6 4.4ZM17.3 84.4c0 4 2.7 8.7 6 10.4 3.3 1.6 6-.3 6-4.4 0-4-2.7-8.7-6-10.3-3.3-1.7-6 .3-6 4.3ZM23.3 62.6c3.3 1.6 6-.3 6-4.4 0-4-2.7-8.7-6-10.3-3.3-1.7-6 .3-6 4.3s2.7 8.7 6 10.4ZM49.6 107.9c3.3 1.6 6-.3 6-4.3 0-4.1-2.7-8.7-6-10.4-3.3-1.6-6 .3-6 4.4 0 4 2.7 8.6 6 10.3ZM30.6 74.8c0 4.1 2.6 8.7 6 10.4 3.2 1.6 6-.3 6-4.4 0-4-2.8-8.7-6-10.3-3.4-1.7-6 .3-6 4.3ZM45.2 26.4c3 1.8 2.5 4.5-1.1 6a15 15 0 0 1-12-.6c-3-1.8-2.5-4.5 1.2-6a15 15 0 0 1 12 .6ZM71.7 14c3 1.9 2.4 4.6-1.2 6a15 15 0 0 1-12-.5c-3-1.8-2.4-4.5 1.2-6a15 15 0 0 1 12 .6ZM57.4 34c3 1.8 2.5 4.5-1.2 6a15 15 0 0 1-12-.6c-2.9-1.8-2.4-4.5 1.2-6a15 15 0 0 1 12 .6ZM84.5 20.8c3 1.8 2.5 4.5-1.1 6a15 15 0 0 1-12-.6c-3-1.8-2.4-4.5 1.2-6a15 15 0 0 1 12 .6ZM70.5 40.3c3 1.8 2.5 4.5-1.2 6a15 15 0 0 1-12-.6c-2.9-1.8-2.4-4.5 1.2-6a15 15 0 0 1 12 .6ZM97.1 28.2c3 1.8 2.5 4.5-1 6a15 15 0 0 1-12-.6c-3-1.8-2.5-4.5 1.1-6a15 15 0 0 1 12 .6Z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".3" stroke-width="3.1" d="M38.3 27.2s1.5 0 3 .5m23.4-12.8s1.5 0 3 .5M50.4 34.8s1.6 0 3 .5m24.2-13.6s1.5 0 3 .4m-17 19s1.5 0 3 .5M90.1 29s1.5 0 3 .5m15 24.7s0 1.5-1 2.9m-25 41.8s0 1.5-1 2.8m14-25.2s0 1.5-1 2.8M46.8 98.7s0 1.4.9 2.8M20.3 86.2s.2 1.5 1 2.9m25.4-22.5s0 1.5.9 2.9M20.3 54.2s.2 1.5 1 2.9M34 76.9s0 1.5 1 2.9"/></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 507 B |
|
Before Width: | Height: | Size: 798 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 360 B |
|
After Width: | Height: | Size: 559 B |
|
After Width: | Height: | Size: 791 B |
|
After Width: | Height: | Size: 604 B |
|
After Width: | Height: | Size: 971 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 472 B After Width: | Height: | Size: 525 B |
|
Before Width: | Height: | Size: 756 B After Width: | Height: | Size: 844 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 444 B |
|
After Width: | Height: | Size: 785 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 371 B After Width: | Height: | Size: 423 B |
|
Before Width: | Height: | Size: 609 B After Width: | Height: | Size: 680 B |
|
Before Width: | Height: | Size: 887 B After Width: | Height: | Size: 955 B |
|
After Width: | Height: | Size: 382 B |
|
After Width: | Height: | Size: 608 B |
|
After Width: | Height: | Size: 858 B |
|
After Width: | Height: | Size: 501 B |
|
After Width: | Height: | Size: 870 B |
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="96.5 111.3 766.5 766.5" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 498.706 365.42 C 515.594 363.348 538.563 365.861 548.485 381.651 C 566.807 410.809 576.751 446.661 588.311 479.176 L 637.399 618.626 L 665.78 698.579 C 676.659 729.146 687.226 748.313 682.54 781.481 C 675.846 788.102 668.071 795.055 658.868 797.629 C 651.616 799.698 643.834 798.72 637.319 794.919 C 631.617 791.632 622.75 782.719 620.419 776.738 C 608.192 745.354 594.733 707.965 586.262 675.669 C 569.761 672.672 533.607 673.757 515.5 673.803 L 465.584 673.663 C 429.007 673.61 428.662 668.91 417.869 707.395 C 410.414 728.146 405.333 752.038 396.071 771.94 C 379.585 807.367 355.479 802.277 330.202 783.696 C 330.019 782.164 329.86 780.629 329.726 779.091 C 328.901 769.751 329.769 760.338 332.289 751.306 C 338.538 728.396 348.963 702.053 357.021 679.401 L 394.065 573.806 L 430.927 469.343 C 440.963 440.571 450.771 403.685 469.355 379.052 C 476.858 369.108 486.798 366.389 498.706 365.42 z M 504.968 436.525 C 508.866 440.798 524.908 486.278 526.975 494.054 C 537.563 533.876 560.589 582.995 564.841 623.288 C 546.777 622.864 528.711 622.571 510.642 622.408 C 489.848 622.27 469.054 622.56 448.271 623.279 C 449.463 605.6 468.851 551.806 475.731 531.511 C 486.173 500.704 494.943 466.856 504.968 436.525 z" fill="#FFFFFF"></path>
|
||||
<path d="M 754.216 524.167 C 763.771 523.439 778.578 525.381 783.67 533.512 C 790.57 544.531 788.595 576.397 788.599 589.29 L 788.562 654.629 C 788.553 691.578 790.329 746.525 787.335 782.106 C 781.652 787.948 775.21 792.333 768.254 796.474 C 752.507 799.502 746.874 792.906 735.396 783.22 C 733.424 762.938 733.05 546.574 736.372 537.446 C 738.677 531.114 748.456 526.925 754.216 524.167 z" fill="#FFFFFF"></path>
|
||||
<path d="M 757.56 426.311 C 768.63 427.03 775.49 429.481 785.226 434.328 C 797.887 454.333 796.6 461.896 785.005 482.127 C 777.16 485.341 772.393 487.022 764.241 489.32 C 731.637 497.151 705.869 438.865 757.56 426.311 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="96.5 111.3 766.5 766.5" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 361.507 181.705 C 372.454 181.635 380.002 234.986 387.127 242.295 C 407.219 262.908 437.857 263.914 463.429 280.408 C 440.851 302.031 406.127 295.636 389.728 315.608 C 381.989 325.033 369.855 367.603 363.046 382.355 C 351.465 373.05 343.119 333.179 332.642 316.843 C 318.951 295.494 264.524 299.57 262.849 280.314 C 270.405 265.304 317.273 260.399 329.146 248.809 C 346.343 232.022 342.909 197.93 361.507 181.705 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 668 B |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="96.5 111.3 766.5 766.5" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 227.159 367.788 C 233.561 369.307 240.228 369.662 246.771 369.89 C 248.488 420.574 264.795 427.64 311.005 436.455 C 310.491 445.011 310.17 446.339 311.45 454.821 C 305.224 455.372 297.895 455.922 291.885 456.866 C 243.817 464.419 258.4 489.944 240.267 514.706 C 237.443 517.55 234.726 520.712 231.065 522.27 C 229.488 521.415 227.953 519.681 227.797 517.859 C 223.678 469.953 209.284 457.548 161.759 455.794 C 162.291 447.329 162.683 442.73 161.662 434.383 C 213.477 429.408 223.054 418.438 227.159 367.788 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 762 B |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M5.9010,12.0 L8.9994,8.4976 A0.6,0.6 0 0,0 8.1006,7.7024 L4.5919,11.6690 A0.5,0.5 0 0,0 4.5919,12.3310 L8.1006,16.2976 A0.6,0.6 0 0,0 8.9994,15.5024 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M18.0990,12.0 L15.0006,8.4976 A0.6,0.6 0 0,1 15.8994,7.7024 L19.4081,11.6690 A0.5,0.5 0 0,1 19.4081,12.3310 L15.8994,16.2976 A0.6,0.6 0 0,1 15.0006,15.5024 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M13.7387,6.2657 L11.4387,17.9657 A0.6,0.6 0 0,1 10.2613,17.7343 L12.5613,6.0343 A0.6,0.6 0 0,1 13.7387,6.2657 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 800 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>General / filled_forge</title>
|
||||
<g id="General-/-filled_forge" stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M26.8803711,42.9691285 L26.8803711,33.7410056 C26.8803711,33.11927 26.3893032,32.6152537 25.7835403,32.6152537 L8.09706803,32.6154971 C7.49130511,32.6152537 7,33.11927 7,33.7410056 C7,33.9944037 7.08318975,34.2404095 7.23626246,34.4392245 C9.56487979,37.4608308 12.1213046,39.7244765 14.9057916,41.2299549 C17.8018091,42.7957341 20.5397889,43.8582429 24.9161047,44.4179683 L25.2457274,44.4589071 C26.0477924,44.5565198 26.7750924,43.9683037 26.8701973,43.1450899 L26.8778029,43.0572626 L26.8803711,42.9691285 Z M30.8798186,32.6154971 L59.9031688,32.6154971 C60.5089318,32.6154971 60.9999998,33.1195134 60.9999998,33.7412491 L60.9999998,36.4408258 C61.000316,37.0212546 60.5706169,37.5067847 60.0076722,37.5620866 C57.3927119,37.8210366 55.1679079,38.6553059 53.3332522,40.064809 C50.2394895,42.4416407 49.2366731,44.4435299 49.1755065,48.0018733 C49.1382293,50.1704578 50.1322377,52.2585707 52.1575318,54.2662123 L52.296812,54.4034138 C52.5894925,54.6869764 52.7553736,55.0818071 52.7553809,55.494901 L52.7553809,58.4989974 C52.7553809,59.3279782 52.1006237,60 51.2929398,60 L31.2454289,60 C30.4377449,60 29.7829878,59.3279782 29.7829878,58.4989974 L29.7829878,55.7751285 C29.7829878,55.2288989 30.07236,54.7258646 30.5381844,54.461705 C32.2097215,53.515021 33.045631,52.0517712 33.045631,50.071432 C33.045631,48.0381514 32.1644305,46.5830842 30.4020296,45.7062304 C30.0237528,45.5192489 29.7835556,45.1264633 29.7829878,44.6955343 L29.7829878,33.7412491 C29.7829878,33.1195134 30.2740556,32.6154971 30.8798186,32.6154971 Z M29.237607,20.3106275 L28.6499023,17.7939453 C28.4616476,16.9877966 28.9457606,16.1776492 29.731199,15.9844306 L29.7897387,15.9713252 L56.7002319,10.5626871 C58.1450662,10.2709171 59.5574843,11.1988248 59.9043292,12.6676634 C60.2342627,14.0648845 59.398154,15.4720725 58.0368282,15.8107056 L57.9904306,15.8217747 L30.9506174,21.431793 C30.2027293,21.5875657 29.4685665,21.1272206 29.2564842,20.3834763 L29.237607,20.3106275 Z M21.0454851,29.4020889 L25.8385445,28.1561792 C27.1874085,27.8053966 28.0160509,26.4127572 27.7040622,25.0209386 L26.1574242,18.1229025 C25.9968368,17.4069908 25.709601,16.7274065 25.3099901,16.1179194 L25.080135,15.7700577 C22.3045369,11.599669 20.2944004,9.68972122 19.0487201,10.0409088 C17.7772179,10.3993761 16.869418,13.1789485 16.3253204,18.3796257 L16.3034188,18.5917858 C16.2363952,19.2528701 16.2767862,19.9207888 16.422944,20.5682925 L17.9744041,27.4363312 C18.2935763,28.8495773 19.6685461,29.729677 21.0454851,29.4020889 Z" id="Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="192px" height="192px" viewBox="0 0 192 192" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Other / Large / large_user_tag</title>
|
||||
<g id="Other-/-Large-/-large_user_tag" stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M134.779316,99.2776105 L155.861136,126.586165 C159.213567,130.92877 158.410871,137.166828 154.068266,140.519258 L137.349024,153.118955 C133.003707,156.393386 126.832804,155.562756 123.507963,151.255889 L102.341831,123.838121 C101.020315,122.126282 100.709294,119.840656 101.525344,117.837943 L107.904357,102.18287 C108.669944,100.304003 110.404748,98.9967346 112.421855,98.7786913 L129.321766,96.9518613 C131.421678,96.724867 133.488626,97.6057022 134.779316,99.2776105 Z M93.0045522,97.5 C96.0738449,97.5 99.0280025,97.6802939 101.867025,98.0408816 C101.658034,98.4287114 101.468568,98.8297452 101.299796,99.2430624 L94.927069,114.849633 C93.2728501,118.900753 93.7623745,123.498344 96.1864336,127.10949 L96.6969772,127.818112 L110.089,145.166 L55.2019123,145.166667 C50.0608762,145.166667 46.4337638,144.380785 44.3205752,142.809021 L44.1207992,142.654461 C42.0402664,140.979657 41,138.612081 41,135.551733 C41,131.543526 42.2098787,127.327177 44.6296361,122.902685 C47.0493935,118.478193 50.5360562,114.345377 55.0896242,110.504236 C59.6431923,106.663095 65.1152969,103.535437 71.5059383,101.121262 C77.8965797,98.7070875 85.0627843,97.5 93.0045522,97.5 Z M115.981148,108.01419 C113.77151,109.670379 113.331818,112.797477 114.999066,114.998781 C116.666338,117.200067 119.809193,117.641973 122.018852,115.98581 C124.22849,114.329621 124.668182,111.202523 123.000934,109.001219 C121.333662,106.799933 118.190807,106.358027 115.981148,108.01419 Z M93,86.6666667 C97.7764274,86.6666667 102.130651,85.4958452 106.062672,83.1542021 C109.994693,80.8125591 113.133919,77.6591033 115.480352,73.6938349 C117.826784,69.7285665 119,65.2736229 119,60.3290043 C119,55.562273 117.815549,51.2391277 115.446646,47.3595686 C113.077743,43.4800095 109.922365,40.394636 105.980514,38.1034483 C102.038662,35.8122605 97.7118241,34.6666667 93,34.6666667 C88.2881759,34.6666667 83.9615135,35.8242275 80.020013,38.1393492 C76.0785124,40.4544708 72.9231351,43.5619993 70.5538811,47.4619346 C68.184627,51.3618699 67,55.6835597 67,60.427004 C67.0196619,65.3062895 68.2027089,69.7285665 70.5491411,73.6938349 C72.8955734,77.6591033 76.0412953,80.8125591 79.9863069,83.1542021 C83.9313185,85.4958452 88.2692162,86.6666667 93,86.6666667 Z" id="Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>General / menu_sidebar1</title>
|
||||
<g id="General-/-menu_sidebar1" stroke="none" fill="none" fill-rule="nonzero">
|
||||
<path d="M51,11.75 C57.7654882,11.75 63.25,17.2345118 63.25,24 L63.25,48 C63.25,54.7654882 57.7654882,60.25 51,60.25 L21,60.25 C14.2345118,60.25 8.75,54.7654882 8.75,48 L8.75,24 C8.75,17.2345118 14.2345118,11.75 21,11.75 L51,11.75 Z M33.75,16.25 L21,16.25 C16.7197932,16.25 13.25,19.7197932 13.25,24 L13.25,48 C13.25,52.2802068 16.7197932,55.75 21,55.75 L33.75,55.75 L33.75,16.25 Z M51,16.25 L38.25,16.25 L38.25,55.75 L51,55.75 C55.2802068,55.75 58.75,52.2802068 58.75,48 L58.75,24 C58.75,19.7197932 55.2802068,16.25 51,16.25 Z M27,38.75 C28.2426407,38.75 29.25,39.7573593 29.25,41 C29.25,42.2426407 28.2426407,43.25 27,43.25 L20,43.25 C18.7573593,43.25 17.75,42.2426407 17.75,41 C17.75,39.7573593 18.7573593,38.75 20,38.75 L27,38.75 Z M27,30.25 C28.2426407,30.25 29.25,31.2573593 29.25,32.5 C29.25,33.7426407 28.2426407,34.75 27,34.75 L20,34.75 C18.7573593,34.75 17.75,33.7426407 17.75,32.5 C17.75,31.2573593 18.7573593,30.25 20,30.25 L27,30.25 Z M27,21.75 C28.2426407,21.75 29.25,22.7573593 29.25,24 C29.25,25.2426407 28.2426407,26.25 27,26.25 L20,26.25 C18.7573593,26.25 17.75,25.2426407 17.75,24 C17.75,22.7573593 18.7573593,21.75 20,21.75 L27,21.75 Z" id="Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>General / menu_sidebar2</title>
|
||||
<g id="General-/-menu_sidebar2" stroke="none" fill="none" fill-rule="nonzero">
|
||||
<path d="M51,11.75 C57.7654882,11.75 63.25,17.2345118 63.25,24 L63.25,48 C63.25,54.7654882 57.7654882,60.25 51,60.25 L21,60.25 C14.2345118,60.25 8.75,54.7654882 8.75,48 L8.75,24 C8.75,17.2345118 14.2345118,11.75 21,11.75 L51,11.75 Z M58.75,40.249 L13.25,40.249 L13.25,48 C13.25,52.2802068 16.7197932,55.75 21,55.75 L51,55.75 C55.2802068,55.75 58.75,52.2802068 58.75,48 L58.75,40.249 Z M52,45.25 C53.2426407,45.25 54.25,46.2573593 54.25,47.5 C54.25,48.7426407 53.2426407,49.75 52,49.75 L47,49.75 C45.7573593,49.75 44.75,48.7426407 44.75,47.5 C44.75,46.2573593 45.7573593,45.25 47,45.25 L52,45.25 Z M38,45.25 C39.2426407,45.25 40.25,46.2573593 40.25,47.5 C40.25,48.7426407 39.2426407,49.75 38,49.75 L33,49.75 C31.7573593,49.75 30.75,48.7426407 30.75,47.5 C30.75,46.2573593 31.7573593,45.25 33,45.25 L38,45.25 Z M25,45.25 C26.2426407,45.25 27.25,46.2573593 27.25,47.5 C27.25,48.7426407 26.2426407,49.75 25,49.75 L20,49.75 C18.7573593,49.75 17.75,48.7426407 17.75,47.5 C17.75,46.2573593 18.7573593,45.25 20,45.25 L25,45.25 Z M51,16.25 L21,16.25 L20.2076076,16.2900124 C16.2996229,16.6868898 13.25,19.9873061 13.25,24 L13.25,35.749 L58.75,35.749 L58.75,24 C58.75,19.7197932 55.2802068,16.25 51,16.25 Z" id="Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>General / menu_sidebar3</title>
|
||||
<g id="General-/-menu_sidebar3" stroke="none" fill="none" fill-rule="nonzero">
|
||||
<path d="M51,11.75 C57.7654882,11.75 63.25,17.2345118 63.25,24 L63.25,48 C63.25,54.7654882 57.7654882,60.25 51,60.25 L21,60.25 C14.2345118,60.25 8.75,54.7654882 8.75,48 L8.75,24 C8.75,17.2345118 14.2345118,11.75 21,11.75 L51,11.75 Z M58.75,36.249 L13.25,36.249 L13.25,48 C13.25,52.2802068 16.7197932,55.75 21,55.75 L51,55.75 C55.2802068,55.75 58.75,52.2802068 58.75,48 L58.75,36.249 Z M51,16.25 L21,16.25 L20.2076076,16.2900124 C16.2996229,16.6868898 13.25,19.9873061 13.25,24 L13.25,31.749 L58.75,31.749 L58.75,24 C58.75,19.7197932 55.2802068,16.25 51,16.25 Z M52,22.25 C53.2426407,22.25 54.25,23.2573593 54.25,24.5 C54.25,25.7426407 53.2426407,26.75 52,26.75 L47,26.75 C45.7573593,26.75 44.75,25.7426407 44.75,24.5 C44.75,23.2573593 45.7573593,22.25 47,22.25 L52,22.25 Z M38,22.25 C39.2426407,22.25 40.25,23.2573593 40.25,24.5 C40.25,25.7426407 39.2426407,26.75 38,26.75 L33,26.75 C31.7573593,26.75 30.75,25.7426407 30.75,24.5 C30.75,23.2573593 31.7573593,22.25 33,22.25 L38,22.25 Z M25,22.25 C26.2426407,22.25 27.25,23.2573593 27.25,24.5 C27.25,25.7426407 26.2426407,26.75 25,26.75 L20,26.75 C18.7573593,26.75 17.75,25.7426407 17.75,24.5 C17.75,23.2573593 18.7573593,22.25 20,22.25 L25,22.25 Z" id="Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Mini / mini_roll</title>
|
||||
<g id="Mini-/-mini_roll" stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M39,2 C43.418278,2 47,5.581722 47,10 L47,31.1887793 C47,33.760437 45.763725,36.1753059 43.6774292,37.6788992 L34.2435723,44.4778795 C32.80623,45.5137721 31.0656753,46.0432193 29.2949637,45.9831622 L9.09321639,45.2979811 C4.83370452,45.1535116 1.4353133,41.695068 1.3654701,37.4336792 L1.01104954,15.8091855 C0.964546919,12.9718908 2.42455232,10.3222835 4.84796505,8.84597906 L14.1687682,3.1678941 C15.4226494,2.40404961 16.8625562,2 18.3307784,2 L39,2 Z M24.3182849,33.7895214 C22.1896369,33.7895214 20.4640268,35.5323942 20.4640268,37.6823369 C20.4640268,39.8322795 22.1896369,41.5751524 24.3182849,41.5751524 C26.4469329,41.5751524 28.1725431,39.8322795 28.1725431,37.6823369 C28.1725431,35.5323942 26.4469329,33.7895214 24.3182849,33.7895214 Z M43.5028298,5.95486664 C43.1084204,5.43724087 42.382444,5.3096145 41.8351419,5.66168777 L31.2535872,12.4687141 L31.2037023,12.4638297 L6.92120008,11.3103335 L6.84373037,11.3084945 C5.94203119,11.3084945 5.21105957,12.0394661 5.21105957,12.9411653 C5.21105957,13.8980472 5.96558985,14.6847215 6.92163968,14.7246185 L30.6738798,15.7148123 L31.0006238,39.5729782 C31.0133849,40.5057402 31.773163,41.2551949 32.7060123,41.2551949 L32.733167,41.2551949 C33.6470713,41.239975 34.3757796,40.4869516 34.3607825,39.5730473 L33.9468798,14.3838123 L43.2886761,7.54001021 C43.7855383,7.16142182 43.8814182,6.45172886 43.5028298,5.95486664 Z M39.599298,27.9369227 C37.937203,27.9369227 36.5898088,29.6797956 36.5898088,31.8297382 C36.5898088,33.9796808 37.937203,35.7225537 39.599298,35.7225537 C41.261393,35.7225537 42.6087873,33.9796808 42.6087873,31.8297382 C42.6087873,29.6797956 41.261393,27.9369227 39.599298,27.9369227 Z M14.6034425,25.1506705 C12.3581563,25.1506705 10.5379922,26.9890433 10.5379922,29.256791 C10.5379922,31.5245387 12.3581563,33.3629114 14.6034425,33.3629114 C16.8487288,33.3629114 18.6688929,31.5245387 18.6688929,29.256791 C18.6688929,26.9890433 16.8487288,25.1506705 14.6034425,25.1506705 Z M6.57813796,16.8317771 C4.44948998,16.8317771 2.72387984,18.5746499 2.72387984,20.7245926 C2.72387984,22.8745352 4.44948998,24.6174081 6.57813796,24.6174081 C8.70678594,24.6174081 10.4323961,22.8745352 10.4323961,20.7245926 C10.4323961,18.5746499 8.70678594,16.8317771 6.57813796,16.8317771 Z M42.1944513,12.7656951 C40.5323563,12.7656951 39.1849621,14.508568 39.1849621,16.6585107 C39.1849621,18.8084533 40.5323563,20.5513262 42.1944513,20.5513262 C43.8565463,20.5513262 45.2039405,18.8084533 45.2039405,16.6585107 C45.2039405,14.508568 43.8565463,12.7656951 42.1944513,12.7656951 Z M24.5822752,3.97181227 C22.5411059,3.97181227 20.8864113,5.2700629 20.8864113,6.8715362 C20.8864113,8.4730095 22.5411059,9.77126013 24.5822752,9.77126013 C26.6234445,9.77126013 28.2781392,8.4730095 28.2781392,6.8715362 C28.2781392,5.2700629 26.6234445,3.97181227 24.5822752,3.97181227 Z" id="Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 72 72">
|
||||
<path fill="#FFFFFF" fill-rule="evenodd" d="M55,37C62.732,37 69,43.268 69,51C69,58.732 62.732,65 55,65C47.268,65 41,58.732 41,51C41,43.268 47.268,37 55,37ZM36,7C52.569,7 66,19.234 66,34.325C66,35.114 65.963,35.895 65.891,36.667C62.867,34.366 59.093,33 55,33C45.059,33 37,41.059 37,51C37,54.863 38.217,58.442 40.288,61.374C38.888,61.556 37.456,61.651 36,61.651C31.929,61.651 28.047,60.912 24.507,59.574C23.5,60.389 22.256,61.163 20.777,61.895C16.779,63.874 12.723,64.458 8.606,63.645C7.977,63.521 7.568,62.91 7.692,62.281C7.731,62.086 7.819,61.903 7.949,61.752C10.634,58.619 11.966,54.919 11.944,50.654C8.21,46.099 6,40.447 6,34.325C6,19.234 19.431,7 36,7ZM55,42.419C53.872,42.419 52.957,43.334 52.957,44.462L52.956,48.956L48.462,48.957C47.334,48.957 46.419,49.872 46.419,51C46.419,52.128 47.334,53.043 48.462,53.043L52.956,53.042L52.957,57.538C52.957,58.666 53.872,59.581 55,59.581C56.128,59.581 57.043,58.666 57.043,57.538L57.042,53.042L61.538,53.043C62.666,53.043 63.581,52.128 63.581,51C63.581,49.872 62.666,48.957 61.538,48.957L57.042,48.956L57.043,44.462C57.043,43.334 56.128,42.419 55,42.419Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 72 72">
|
||||
<path fill="#ffffff" fill-rule="evenodd" d="M32.5952 38C33.3684 38 33.9952 38.6268 33.9952 39.4V56.1373C33.9952 57.0209 33.2788 57.7373 32.3952 57.7373C31.9708 57.7373 31.5639 57.5687 31.2638 57.2686L25.9422 51.947L19.2057 58.6518C17.5821 60.2675 14.9562 60.2611 13.3405 58.6376C11.7247 57.014 11.7311 54.388 13.3547 52.7723L20.0772 46.082L14.7265 40.7314C14.1017 40.1065 14.1017 39.0935 14.7265 38.4686C15.0266 38.1686 15.4336 38 15.8579 38H32.5952Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 549 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 72 72">
|
||||
<path fill="#ffffff" fill-opacity="0.5" fill-rule="evenodd" d="M49.2732 56.5962L45.2962 55.5283C44.5655 55.3321 44.1322 54.5806 44.3285 53.8499C44.4553 53.3778 44.8241 53.009 45.2962 52.8822L49.2732 51.8142C49.8611 51.6563 50.3204 51.1971 50.4783 50.6091L51.5462 46.6322C51.7425 45.9015 52.4939 45.4682 53.2246 45.6644C53.6967 45.7912 54.0655 46.16 54.1923 46.6322L55.2603 50.6091C55.4182 51.1971 55.8774 51.6563 56.4654 51.8142L60.4423 52.8822C61.173 53.0784 61.6063 53.8298 61.4101 54.5605C61.2833 55.0327 60.9145 55.4015 60.4423 55.5283L56.4654 56.5962C55.8774 56.7541 55.4182 57.2134 55.2603 57.8013L54.1923 61.7783C53.9961 62.509 53.2447 62.9423 52.514 62.746C52.0418 62.6193 51.673 62.2504 51.5462 61.7783L50.4783 57.8013C50.3204 57.2134 49.8611 56.7541 49.2732 56.5962Z"/>
|
||||
<path fill="#ffffff" fill-opacity="0.5" fill-rule="evenodd" d="M14.9896 19.8736L12.1138 19.0921C11.5352 18.9349 11.1935 18.3383 11.3508 17.7596C11.4518 17.3879 11.7421 17.0975 12.1138 16.9965L14.9896 16.2151C15.5962 16.0502 16.0691 15.5746 16.2303 14.967L17.0052 12.0468C17.157 11.4749 17.7436 11.1343 18.3155 11.2861C18.6872 11.3847 18.9775 11.6751 19.0761 12.0468L19.851 14.967C20.0123 15.5746 20.4851 16.0502 21.0918 16.2151L23.9675 16.9965C24.5462 17.1538 24.8878 17.7504 24.7306 18.3291C24.6296 18.7008 24.3392 18.9911 23.9675 19.0921L21.0918 19.8736C20.4851 20.0384 20.0123 20.5141 19.851 21.1217L19.0761 24.0419C18.9244 24.6137 18.3378 24.9543 17.7659 24.8026C17.3942 24.7039 17.1039 24.4136 17.0052 24.0419L16.2303 21.1217C16.0691 20.5141 15.5962 20.0384 14.9896 19.8736Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="96.5 111.3 766.5 766.5" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M 229.278 428.274 C 220.345 431.087 209.724 433.028 201.037 428.414 C 195.269 425.35 191.59 419.715 189.754 413.573 C 183.55 392.827 189.646 312.841 194.598 289.706 C 197.361 276.798 201.636 264.234 208.088 252.692 C 225.944 220.748 254.345 196.434 289.828 186.42 C 299.548 183.676 309.688 182.14 319.754 181.423 C 366.51 178.095 414.437 180.111 461.345 180.132 C 499.997 180.149 540.285 177.606 578.657 181.934 C 589.925 183.204 601.183 185.766 611.125 191.369 C 638.232 206.647 660.549 233.285 682.155 255.355 L 741.115 315.299 L 790.708 365.561 C 856.391 432.25 853.313 440.991 854.144 532.569 L 854.982 618.399 C 855.281 646.04 857.577 702.741 854.521 727.102 C 852.435 744.448 846.915 761.205 838.282 776.393 C 820.298 808.656 790.121 832.354 754.514 842.176 C 724.089 850.54 664.593 848.635 631.504 848.632 L 502.763 848.626 L 395.022 848.66 C 368.985 848.68 338.582 849.327 312.926 846.33 C 294.671 844.264 277.017 838.552 261.012 829.532 C 228.881 811.132 205.527 780.551 196.236 744.708 C 190.977 725.111 181.752 660.54 192.22 640.91 C 194.552 636.536 200.443 631.193 204.584 628.088 C 212.69 627.794 223.61 625.977 229.827 632.364 C 238.468 641.24 238.784 653.684 239.449 665.298 C 241.05 693.25 237.429 726.072 251.724 751.22 C 263.068 771.178 283.96 788.616 306.011 795.036 C 326.159 800.066 359.183 798.753 380.622 798.749 L 460.193 798.696 L 703.391 798.695 C 736.68 798.43 755.087 795.322 779.818 770.591 C 807.116 743.293 806.834 720.038 807.012 684.161 C 807.237 638.481 802.094 586.261 804.093 541.122 C 797.284 541.268 784.894 540.995 778.473 540.182 L 670.807 540.171 C 620.731 540.215 563.716 547.86 526.38 508.874 C 515.394 497.402 505.295 482.717 501.211 466.939 C 495.154 439.227 496.977 401.501 496.999 372.596 L 497.103 265.614 C 497.25 253.886 497.577 242.161 498.083 230.443 C 460.209 229.51 422.322 229.222 384.438 229.578 C 335.986 229.564 309.732 221.795 269.362 254.551 C 217.116 296.943 257.721 389.29 229.559 427.897 L 229.278 428.274 z M 543.987 230.048 C 569.591 229.725 580.035 227.497 600.857 244.842 C 616.603 257.958 630.079 272.64 644.427 287.248 L 711.181 355.245 L 759.072 403.921 C 769.77 414.798 785.756 430.108 793.914 442.441 C 801.17 453.41 801.179 474.426 802.716 487.848 L 663.551 488.081 C 655.173 488.09 645.943 488.449 637.627 488.107 C 619.637 487.367 594.727 490.338 577.846 484.153 C 545.853 472.432 543.843 436.102 544.247 407.389 C 544.438 393.841 544.247 379.666 544.256 365.887 C 544.495 320.607 544.405 275.326 543.987 230.048 z" fill="#FFFFFF"></path>
|
||||
<path d="M 306.755 391.204 C 311.745 389.305 333.398 408.89 337.28 412.89 C 364.174 440.939 392.794 467.428 419.422 495.698 C 442.291 519.978 433.144 542.942 411.651 563.866 C 381.068 593.638 352.265 625.515 320.324 653.743 C 313.888 659.431 296.743 655.829 288.97 654.425 C 287.039 649.138 280.827 633.452 282.505 628.63 C 289.515 608.489 314.352 590.824 328.353 575.581 C 338.311 564.74 345.544 555.412 356.226 547.597 L 356.28 545.853 C 344.978 549.112 335.336 551.079 323.481 551.267 C 274.259 551.846 224.991 551.501 175.755 551.624 C 155.295 551.676 133.425 553.912 116.627 541.311 C 114.956 537.351 114 533.126 113.802 528.833 C 113.091 514.333 117.1 501.472 133.314 500.562 C 154.052 499.399 174.698 499.879 195.467 499.925 C 239.08 500.336 282.696 500.334 326.309 499.918 C 332.541 499.822 343.551 500.1 347.833 495.685 C 347.436 491.434 313.367 461.839 307.219 455.232 C 291.043 437.85 275.937 429.066 286.23 402.426 C 291.881 397.453 299.873 394.302 306.755 391.204 z" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Filled / filled_chatlist_mention</title>
|
||||
<g id="Filled-/-filled_chatlist_mention" stroke="none" fill="none" fill-rule="nonzero">
|
||||
<path d="M36,7.94444444 C51.4946555,7.94444444 64.0555556,20.5053445 64.0555556,36 C64.0555556,45.4079588 60.5708092,50.852875 53.5366724,50.4132415 C50.1034854,50.1986673 47.6130345,48.761699 46.074269,46.3154879 C43.5446485,48.7868521 40.1096404,50.3348637 36.3135543,50.4158502 L36,50.4191919 C28.0365002,50.4191919 21.5808081,43.9634998 21.5808081,36 C21.5808081,28.0365002 28.0365002,21.5808081 36,21.5808081 C43.9634998,21.5808081 50.4191919,28.0365002 50.4191919,36 L50.4156549,36.3225752 C50.4221637,36.4530489 50.4193336,36.5855158 50.4077048,36.7192471 C49.9335852,42.1716224 51.0798543,44.1366551 53.917873,44.3140313 C56.580706,44.4804583 57.9444444,42.349617 57.9444444,36 C57.9444444,23.880418 48.119582,14.0555556 36,14.0555556 C23.880418,14.0555556 14.0555556,23.880418 14.0555556,36 C14.0555556,48.119582 23.880418,57.9444444 36,57.9444444 L46.4545455,57.9444444 C48.1420822,57.9444444 49.510101,59.3124633 49.510101,61 C49.510101,62.6875367 48.1420822,64.0555556 46.4545455,64.0555556 L36,64.0555556 C20.5053445,64.0555556 7.94444444,51.4946555 7.94444444,36 C7.94444444,20.5053445 20.5053445,7.94444444 36,7.94444444 Z M36,27.6919192 C31.4115737,27.6919192 27.6919192,31.4115737 27.6919192,36 C27.6919192,40.5884263 31.4115737,44.3080808 36,44.3080808 C40.5884263,44.3080808 44.3080808,40.5884263 44.3080808,36 C44.3080808,31.4115737 40.5884263,27.6919192 36,27.6919192 Z" id="Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Filled / filled_chatlist_poll</title>
|
||||
<g id="Filled-/-filled_chatlist_poll" stroke="none" fill="none" fill-rule="evenodd">
|
||||
<path d="M16,27.6666667 C19.3137085,27.6666667 22,30.3529582 22,33.6666667 L22,55 C22,58.3137085 19.3137085,61 16,61 C12.6862915,61 10,58.3137085 10,55 L10,33.6666667 C10,30.4282697 12.5655749,27.7890949 15.7750617,27.6708051 L16,27.6666667 Z M36,11 C39.3137085,11 42,13.6862915 42,17 L42,55 C42,58.3137085 39.3137085,61 36,61 C32.6862915,61 30,58.3137085 30,55 L30,17 C30,13.6862915 32.6862915,11 36,11 Z M56,36 C59.3137085,36 62,38.6862915 62,42 L62,55 C62,58.3137085 59.3137085,61 56,61 C52.6862915,61 50,58.3137085 50,55 L50,42 C50,38.6862915 52.6862915,36 56,36 Z" id="Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 949 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Filled / filled_chatlist_reaction</title>
|
||||
<g id="Filled-/-filled_chatlist_reaction" stroke="none" fill="none" fill-rule="nonzero">
|
||||
<path d="M39.7777796,60.0291132 C37.6505975,61.9950186 34.3758567,61.9950185 32.2486747,60.0006218 L31.9407931,59.7157078 C17.2464435,46.1823011 7.64613503,36.9178019 8.00999511,25.8631454 C8.17793054,21.0196104 10.6129942,16.375515 14.5594767,13.6403423 C21.5930174,8.75871019 30.1988776,13.0872447 35.2594851,18.4794853 C35.515351,18.7521185 36.4762456,18.7597927 36.7283013,18.4906892 C41.7874742,13.0893374 50.4004979,8.72813558 57.4389884,13.6403423 C61.3854708,16.375515 63.8205345,21.0196104 63.9884699,25.8631454 C64.3803192,36.9178019 54.7520216,46.1823011 40.0576719,59.7726906 L39.7777796,60.0291132 Z" id="Path" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1000 B |
|
After Width: | Height: | Size: 395 B |
|
After Width: | Height: | Size: 541 B |
|
After Width: | Height: | Size: 653 B |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="6px" height="6px" viewBox="0 0 6 6" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="3" cy="3" r="3" fill="#FFFFFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 179 B |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" fill="none" fill-rule="evenodd">
|
||||
<rect x="11" y="11" width="18" height="18" rx="3.5" stroke="#FFFFFF" stroke-width="1.5" fill="none"/>
|
||||
<g transform="translate(20,20) scale(0.19) translate(-36,-36)">
|
||||
<path d="M16,27.6666667 C19.3137085,27.6666667 22,30.3529582 22,33.6666667 L22,55 C22,58.3137085 19.3137085,61 16,61 C12.6862915,61 10,58.3137085 10,55 L10,33.6666667 C10,30.4282697 12.5655749,27.7890949 15.7750617,27.6708051 L16,27.6666667 Z M36,11 C39.3137085,11 42,13.6862915 42,17 L42,55 C42,58.3137085 39.3137085,61 36,61 C32.6862915,61 30,58.3137085 30,55 L30,17 C30,13.6862915 32.6862915,11 36,11 Z M56,36 C59.3137085,36 62,38.6862915 62,42 L62,55 C62,58.3137085 59.3137085,61 56,61 C52.6862915,61 50,58.3137085 50,55 L50,42 C50,38.6862915 52.6862915,36 56,36 Z" fill="#FFFFFF"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1005 B |
|
Before Width: | Height: | Size: 452 B After Width: | Height: | Size: 323 B |
|
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 489 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 688 B |
|
After Width: | Height: | Size: 1.4 KiB |