Compare commits
	
		
			592 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 38e2853dcf | ||
|   | ba5a540ca3 | ||
|   | fb1e05c2e9 | ||
|   | aba84612a7 | ||
|   | 9bebbf4e03 | ||
|   | e41b3f9c10 | ||
|   | 890dc05022 | ||
|   | 375f86ec82 | ||
|   | db248a69c8 | ||
|   | 5951288159 | ||
|   | 17b92c9db2 | ||
|   | 962d1060d9 | ||
|   | cb2640d961 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 29aeb0f082 | ||
|   | 990347f856 | ||
|   | 7a406c1f13 | ||
|   | 9432af2ab5 | ||
|   | 136b13e7ca | ||
|   | ba1c823fb1 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | f1301a4780 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 7957cd4963 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | ee6590d03f | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | f2a1238b20 | ||
|   | e9ce84f368 | ||
|   | 52e84decb4 | ||
|   | e893002bb6 | ||
|   | 3792103e80 | ||
|   | 7a861c9481 | ||
|   | 942b565224 | ||
|   | 88390d7a9a | ||
|   | 966fc4c5d7 | ||
|   | 84dbdf1196 | ||
|   | 211e7f90d9 | ||
|   | e54b8e3fb2 | ||
|   | 836c89ed33 | ||
|   | c7c73afea1 | ||
|   | 7b9ca63b1e | ||
|   | c464183329 | ||
|   | 389f420cad | ||
|   | 6b2888383c | ||
|   | 3c38a867b4 | ||
|   | 7f5a69f4d8 | ||
|   | bb9ab31d5e | ||
|   | 9def80af8a | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 9256bcdbe4 | ||
|   | 9b775022bc | ||
|   | 32371ed2bd | ||
|   | 8b98c08a81 | ||
|   | 7cf72f7447 | ||
|   | 913385b10d | ||
|   | 7306468d08 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 11e5667778 | ||
|   | 38cc02e261 | ||
|   | d52cf46cc1 | ||
|   | c6110dd996 | ||
|   | 51d8de2c38 | ||
|   | 4455a1aa9d | ||
|   | 040d395ddb | ||
|   | 8296cac636 | ||
|   | 3eafe8b87d | ||
|   | c01512e261 | ||
|   | e5cf3aecd5 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | a8f90b41b7 | ||
|   | b79169b975 | ||
|   | 437d52e2ed | ||
|   | 1329721440 | ||
|   | 6affb4fe97 | ||
|   | 15395686aa | ||
|   | 047bcc78ad | ||
|   | 9df68618f2 | ||
|   | 732db087ab | ||
|   | 0e95b33b6a | ||
|   | 816ae7eb7e | ||
|   | 5a5ff194fa | ||
|   | a60edf9cff | ||
|   | 1c2dbb914e | ||
|   | 9c170c426b | ||
|   | c6239c8ad9 | ||
|   | 159b361bac | ||
|   | 160f64c18e | ||
|   | e5916b3789 | ||
|   | 70982b33c5 | ||
|   | b4d614ad45 | ||
|   | 6d2ef41b37 | ||
|   | e102237aab | ||
|   | 665af87031 | ||
|   | 6f4e439697 | ||
|   | 742dcf35c9 | ||
|   | 9cd70c568c | ||
|   | facabf274f | ||
|   | e3ab51022f | ||
|   | d278367cf9 | ||
|   | a70ced8e90 | ||
|   | 567cedc7cc | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 9b3af6efcd | ||
|   | d9edc1eb1d | ||
|   | 65e46b5cec | ||
|   | e00b5f11cb | ||
|   | 6b53d5f269 | ||
|   | 59c80ab140 | ||
|   | da323aad36 | ||
|   | 7c1611c939 | ||
|   | ab861beabe | ||
|   | d260e93161 | ||
|   | 65a1855606 | ||
|   | c0e08e44a4 | ||
|   | 5c1cebcef4 | ||
|   | af25d3a85e | ||
|   | 8cb7183107 | ||
|   | 1bf228d73e | ||
|   | d952b996e6 | ||
|   | 1e407c4059 | ||
|   | b56d1fa60e | ||
|   | 6340f95bfc | ||
|   | 3c08dacf6c | ||
|   | 2908124ad8 | ||
|   | 3d62faaaf2 | ||
|   | b1efa9700d | ||
|   | 1d08af5747 | ||
|   | 2f82d0db87 | ||
|   | d88159907d | ||
|   | 9ed9fbef65 | ||
|   | 86c2e5bb91 | ||
|   | d9b548de1a | ||
|   | 2271c6cbd8 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | c4d4293c46 | ||
|   | 39bdfb6e0d | ||
|   | 1003fd393e | ||
|   | 2ede3c0864 | ||
|   | 674764a035 | ||
|   | a780e7b936 | ||
|   | 03d0ce1f89 | ||
|   | 4182a0cf4c | ||
|   | 305915611e | ||
|   | b0cd59bed9 | ||
|   | 599dcbaa48 | ||
|   | 2806dc98bd | ||
|   | bdc52dc114 | ||
|   | 3f6b9e554c | ||
|   | f47ad7bf31 | ||
|   | f992f72d31 | ||
|   | a26f1db2cb | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 361ab00c61 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | f5cbcf3452 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 599386190a | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | ec541d3cd0 | ||
|   | 3199819ded | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | ccf04d63ec | ||
|   | b9f5fca333 | ||
|   | b6a330928d | ||
|   | 1c65cb3e36 | ||
|   | dbb8c99efb | ||
|   | 0adcb646fe | ||
|   | a1ef70c0bf | ||
|   | 75cd580c3a | ||
|   | e05acb8d18 | ||
|   | 10af684804 | ||
|   | 3e897727ca | ||
|   | d0570d7fe3 | ||
|   | 5cf1956135 | ||
|   | 0b98a2364b | ||
|   | fff307d4bb | ||
|   | 2b7782ba03 | ||
|   | 96d961ee80 | ||
|   | 9f064d76d9 | ||
|   | 2e39106c4b | ||
|   | 1305006391 | ||
|   | 04650464f3 | ||
|   | 3bc7e1e35c | ||
|   | 7019ddbfc7 | ||
|   | 106d990bd2 | ||
|   | b29eb29556 | ||
|   | aeac1854ed | ||
|   | dbc57dd0d3 | ||
|   | 328a87609e | ||
|   | 5d848f3900 | ||
|   | cf4ed45fe4 | ||
|   | 07293094d5 | ||
|   | 0917696c86 | ||
|   | 030a027366 | ||
|   | 372c488585 | ||
|   | 738b8ff1ee | ||
|   | 1561fc5994 | ||
|   | c84f18545e | ||
|   | 48e4dc75f4 | ||
|   | 63a8d556e5 | ||
|   | e5591618ee | ||
|   | 4794748c73 | ||
|   | 02e7e3b971 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | d2aca3c28b | ||
|   | 11b84a04b3 | ||
|   | f243ce66e7 | ||
|   | baf9b65801 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 55419d2524 | ||
|   | 401d0b1298 | ||
|   | fce7dc0f4e | ||
|   | 35489ef5b7 | ||
|   | 546d494587 | ||
|   | e8afa2c940 | ||
|   | c1ef1bf605 | ||
|   | 4ab0dbe7e3 | ||
|   | 44f86a94f4 | ||
|   | a0278154a3 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 8b7e6b200e | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | d6f6c26725 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | cf66343b31 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | d53689332f | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 4105237027 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 436962e4b8 | ||
|   | a85efa1392 | ||
|   | f0115a5e21 | ||
|   | 80105239dc | ||
|   | baad11288a | ||
|   | 7e50646ede | ||
|   | d4b8e47bcb | ||
|   | 0eefd2922c | ||
|   | 30c0f98691 | ||
|   | 06a7c2e138 | ||
|   | 3537b3de8e | ||
|   | ed6450244d | ||
|   | e813880392 | ||
|   | 9a57efa6d9 | ||
|   | 03ee5eba3b | ||
|   | 295ea79231 | ||
|   | a5486176c1 | ||
|   | de58325fd0 | ||
|   | 1e7932d9c7 | ||
|   | 8ba76df409 | ||
|   | a8f9d20229 | ||
|   | 5e6d1b9ae8 | ||
|   | c5afbaef35 | ||
|   | 3b5a36a09f | ||
|   | fcb20d05d7 | ||
|   | 9e6990c44b | ||
|   | 8f3fd9b0dc | ||
|   | 5b1b4a02d8 | ||
|   | 31de530497 | ||
|   | 16b6b1f2b3 | ||
|   | dba83aa50d | ||
|   | bade054a6a | ||
|   | 34d3485dc9 | ||
|   | a84d066daa | ||
|   | 3360cf27cd | ||
|   | c1a13af611 | ||
|   | 47274a658b | ||
|   | b194334031 | ||
|   | 4136c4a807 | ||
|   | f1c212fe75 | ||
|   | d08cbff4b7 | ||
|   | 0b774475fa | ||
|   | c4f6195df3 | ||
|   | 192cdbe322 | ||
|   | a2a25eb5f8 | ||
|   | 274cf1af1c | ||
|   | 7d11c8b767 | ||
|   | abef6bafe3 | ||
|   | da237a5e2d | ||
|   | 7e50e03cfb | ||
|   | 89d5df20a5 | ||
|   | c09a2a37fe | ||
|   | b5745877ca | ||
|   | c0ac15cad7 | ||
|   | 90ce09be2e | ||
|   | fd39afb374 | ||
|   | 6e04549a9b | ||
|   | 80e56fddd9 | ||
|   | 4daf9e1180 | ||
|   | f72abc0e47 | ||
|   | 9c177f3df2 | ||
|   | a279b32c93 | ||
|   | 51465ba026 | ||
|   | 6a7bdcc533 | ||
|   | fddb3a5f10 | ||
|   | 643c7abc12 | ||
|   | 5715afd44c | ||
|   | d65c1c420e | ||
|   | 38139ee6c9 | ||
|   | 6b96bd0185 | ||
|   | f2b9863eea | ||
|   | f56adce51f | ||
|   | 35598c8064 | ||
|   | a5e716eb5d | ||
|   | e8073b7484 | ||
|   | d6a5fc20bb | ||
|   | e763d43085 | ||
|   | a6904d5249 | ||
|   | 7bcb91d3ca | ||
|   | fb0c1efa41 | ||
|   | 03d243d444 | ||
|   | b91585d1fe | ||
|   | d1fa318cda | ||
|   | 42decae424 | ||
|   | 78bc7c20ed | ||
|   | e6616bdf57 | ||
|   | 35362ed3c7 | ||
|   | cd0b9a8e3f | ||
|   | 110aadd65c | ||
|   | cefe1f34be | ||
|   | d469e2152c | ||
|   | 35c2d47518 | ||
|   | c00634a2cf | ||
|   | d1835e262d | ||
|   | d0f304f0ce | ||
|   | 154abe06a7 | ||
|   | ac41cd378c | ||
|   | d59afda2c9 | ||
|   | 4d213833e2 | ||
|   | e9214d4330 | ||
|   | 6b41bb95b2 | ||
|   | 36de13d543 | ||
|   | 3893def9f4 | ||
|   | ff76c815b1 | ||
|   | 1b9b8912ae | ||
|   | b91b0d17c3 | ||
|   | 579b61a806 | ||
|   | 1e2b484929 | ||
|   | ab7d4fa2a2 | ||
|   | f30c8b8a47 | ||
|   | 25dd19dd8c | ||
|   | fdaebc6315 | ||
|   | f1a05c214e | ||
|   | 9ad32ffee9 | ||
|   | 70f83ab019 | ||
|   | 07e64631f2 | ||
|   | 498416e2e3 | ||
|   | 87c4f908fe | ||
|   | 27e6eaacde | ||
|   | ada47920ca | ||
|   | f2606d62ff | ||
|   | 35032152b3 | ||
|   | 90b545fd69 | ||
|   | 4f7776d1f9 | ||
|   | 03bd0c4c9e | ||
|   | b8b4991a46 | ||
|   | 8b68c5da37 | ||
|   | f26e641905 | ||
|   | 57669c9c03 | ||
|   | 094d873f2e | ||
|   | 7b42653271 | ||
|   | 6a82e94c54 | ||
|   | 1fa4d0d3f8 | ||
|   | d5818214f3 | ||
|   | 0c63ec8157 | ||
|   | 8317a219a5 | ||
|   | ec652dd0ac | ||
|   | 8e3dd75c85 | ||
|   | 3f4de3b1cc | ||
|   | e0d3fd17ce | ||
|   | 671c7cf06b | ||
|   | 99a2c5b3fa | ||
|   | 0343de95b8 | ||
|   | 13f258308e | ||
|   | c627288bde | ||
|   | 1eef90f6cb | ||
|   | 29476ea3da | ||
|   | 7609741680 | ||
|   | 5df85b5feb | ||
|   | b4a4d784c3 | ||
|   | d715af0620 | ||
|   | fe8c384618 | ||
|   | 47d4de75da | ||
|   | 016c7fd2c4 | ||
|   | 7ca5e33a3a | ||
|   | 436773837b | ||
|   | fccfe19e13 | ||
|   | 18be0d36f3 | ||
|   | 9ea7de3564 | ||
|   | d1dce76e28 | ||
|   | f4bf824e79 | ||
|   | cbf5d4b71d | ||
|   | 61e5cb2041 | ||
|   | 38a6aa26bc | ||
|   | 32942709bf | ||
|   | a0305c4c04 | ||
|   | 74be1c81b1 | ||
|   | e82a44c03d | ||
|   | 1e9eeeb980 | ||
|   | 0c7111b438 | ||
|   | ef74653a4b | ||
|   | b032f78769 | ||
|   | 7a0a8b5d2b | ||
|   | 459b374d61 | ||
|   | cc6af402be | ||
|   | f3eeadcd8f | ||
|   | 5b0b83faa7 | ||
|   | 5f289e4767 | ||
|   | a36d73cb52 | ||
|   | 9e319006f3 | ||
|   | 7b8a2aef0f | ||
|   | 4fe2f29478 | ||
|   | 76a25917c0 | ||
|   | df6c9b1a1c | ||
|   | 0ef64a6d0b | ||
|   | 1eb6ad58d5 | ||
|   | 49f8cfb0db | ||
|   | 15a8b4c6e5 | ||
|   | 97d68d7b31 | ||
|   | 4dde54e344 | ||
|   | 3304cc106e | ||
|   | dabda21eb7 | ||
|   | 0a64f638c6 | ||
|   | 3cbdfcb43d | ||
|   | 5734221c8f | ||
|   | b6e33e93de | ||
|   | 77b441f14c | ||
|   | 297019c22c | ||
|   | f5f2215b47 | ||
|   | 904114740b | ||
|   | d2d3f7810e | ||
|   | 2b07b3a873 | ||
|   | d17280b341 | ||
|   | f523d3f3bc | ||
|   | f6e4a1770e | ||
|   | 31006507c0 | ||
|   | 9ca6a6bf06 | ||
|   | afdacf14b7 | ||
|   | 5da18ba535 | ||
|   | e6cc937ac2 | ||
|   | 87091a2e03 | ||
|   | d23bc1e02a | ||
|   | 86fcd9208e | ||
|   | f2b97a889c | ||
|   | 97f91102fe | ||
|   | 07b04578c8 | ||
|   | 31f3c1996b | ||
|   | f4116e7300 | ||
|   | 5e5239c16e | ||
|   | 3adfcd1d13 | ||
|   | 3eb43a5413 | ||
|   | 80f41e2ac1 | ||
|   | 9e0b0b4210 | ||
|   | f3380d3184 | ||
|   | 54bc91ea2b | ||
|   | 59d67d3140 | ||
|   | 7b4c307c46 | ||
|   | 92484be87f | ||
|   | 56b8f8b07d | ||
|   | 722de35037 | ||
|   | d93f76c1af | ||
|   | cba0dd5e17 | ||
|   | 7bb25917f8 | ||
|   | a2e2d5ba77 | ||
|   | 61e05cb50e | ||
|   | 49e82adc6c | ||
|   | e4e668b327 | ||
|   | 8028c85c67 | ||
|   | 28cb9cae51 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 7f2eb64131 | ||
|   | c5ff6df7e6 | ||
|   | e2b2982f95 | ||
|   | a6e307010f | ||
|   | c636e35467 | ||
|   | 3d26bd0532 | ||
|   | 16130e46dd | ||
|   | 9f703085ba | ||
|   | 0af09f13cf | ||
|   | cd8619113a | ||
|   | 4bf6d9f80b | ||
|   | a559b2c20c | ||
|   | 72411abfcd | ||
|   | 491c3f1dc0 | ||
|   | d2ffc4df6c | ||
|   | 3e5330a92b | ||
|   | 93e5e4afc0 | ||
|   | aa5528d11e | ||
|   | 251629ab61 | ||
|   | 82d94b5963 | ||
|   | 8240901332 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 0a870b8e7e | ||
|   | 88dd653fa5 | ||
|   | b712b70330 | ||
|   | a018c2f09f | ||
|   | 04c16e53a5 | ||
|   | 5e89e73f76 | ||
|   | 2c9432d7a9 | ||
|   | 19d1775b36 | ||
|   | ecc235c545 | ||
|   | 382b1d2250 | ||
|   | 629693355a | ||
|   | 00a3f8d392 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 80b6e8090e | ||
|   | a5f817d896 | ||
|   | 51b0244cf2 | ||
|   | 01131e2606 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 6283b7668e | ||
|   | d058ecc4ea | ||
|   | 77a0450b5d | ||
|   | 1dd1b9084f | ||
|   | 6341807d02 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 51a1f30225 | ||
|   | 5422482696 | ||
|   | cd7f8b080e | ||
|   | faf29b768f | ||
|   | 7576569dc9 | ||
|   | ea3bcbbc37 | ||
|   | d9f0e158a3 | ||
|   | 195f676500 | ||
|   | a9a2f4820b | ||
|   | 8414db57f0 | ||
|   | 609d68933e | ||
|   | a23b8cebbc | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 89f6b03cd6 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 7bc9de03a6 | ||
|   | 3c865d6054 | ||
|   | fd770b008e | ||
|   | b0d60ef2c2 | ||
|   | 7b9cea06ef | ||
|   | 30608d3e22 | ||
|   | 8bf4e55338 | ||
|   | 6ead1de383 | ||
|   | 3b628ec3c4 | ||
|   | 0ed704d173 | ||
|   | 87b6ef0ec5 | ||
|   | 5184a07cf2 | ||
|   | dba04cc59c | ||
|   | f4045fb5b3 | ||
|   | 16c36163b4 | ||
|   | 1ac033ff18 | ||
|   | ccfd48232a | ||
|   | 429bf179dc | ||
|   | 8ba3fb13eb | ||
|   | 11496d887e | ||
|   | bec48319ec | ||
|   | 71a93b2b43 | ||
|   | 6ed3f9e414 | ||
|   | dc8f592c1f | ||
|   | f66c31c771 | ||
|   | 55e2ae1408 | ||
|   | 19c72627fc | ||
|   | 2a4c53c3a4 | ||
|   | 1f2ebce8ed | ||
|   | fcea9dacb7 | ||
|   | 908872f374 | ||
|   | f688ceafb8 | ||
|   | b47b5d6d8b | ||
|   | 31ce3aa312 | ||
|   | 5b22d92e99 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | df148e25da | ||
|   | 4b26df5c3a | ||
|   | e765be4205 | ||
|   | f7d2457063 | ||
|   | 6032d803aa | ||
|   | 0de371db38 | ||
|   | ce3797c4af | ||
|   | 56dd8c298b | ||
|   | 3533257efe | ||
|   | dc2f08721d | ||
|   | 66608a4131 | ||
|   | 2fa90131eb | ||
|   | a51ed28db6 | ||
|   | 5ec290663b | ||
|   | 1374d6e34d | ||
|   | 45ade17c58 | ||
|   | c753e26187 | ||
|   | 577929eed1 | ||
|   | 1fde8a8fb0 | ||
|   | 77e53cbf9e | ||
|   | ab83e08bc7 | ||
|   | 2fad6e6d5f | ||
|   | a3604a6c95 | ||
|   | 44f6fe6f1f | ||
|   | 311b4e90ca | ||
|   | f5a937c523 | ||
| ![greenkeeper[bot]](/assets/img/avatar_default.png)  | 0632a3ed3f | ||
|   | 71bada97df | ||
|   | 62509edcbe | ||
|   | f97cdfaa20 | ||
|   | 67ec10e86d | ||
|   | 481b3f2c58 | ||
|   | 7d599a68ea | ||
|   | 7ccff732b8 | ||
|   | 7587c896d5 | ||
|   | 91297f1ab3 | ||
|   | d872a16fe0 | ||
|   | 60aa35adf8 | ||
|   | 5035b66773 | ||
|   | fa9da8ecab | ||
|   | 1f9bca7188 | ||
|   | ffa5bdeb50 | ||
|   | e6bfb7398e | ||
|   | 6def0c776f | ||
|   | 24bae9eaed | ||
|   | fb5175a283 | ||
|   | 6e49437154 | ||
|   | 2511ed56ac | ||
|   | c4bfc99cf5 | ||
|   | 4efe38440d | ||
|   | 4a5f2c3c40 | ||
|   | 109738ccb9 | ||
|   | 433dbe179d | ||
|   | b21b33831a | ||
|   | 25438c4d64 | 
| @@ -7,27 +7,51 @@ maintainer: | ||||
|   repository_url: https://github.com/syuilo/misskey # Repository URL | ||||
|   feedback_url: https://github.com/syuilo/misskey/issues # Feedback URL (e.g. github issue) | ||||
|  | ||||
| # URL and Port settings overview | ||||
| # e.g., If you want to realize following structure: | ||||
| # | ||||
| #               +--- https://example.com:123 ----------+ | ||||
| # +------+      |+-------------+      +---------------+| | ||||
| # | User | ---> || Proxy (123) | ---> | Misskey (456) || | ||||
| # +------+      |+-------------+      +---------------+| | ||||
| #               +--------------------------------------+ | ||||
| # | ||||
| # You need to set 'https://example.com:123' to 'url' prop and | ||||
| # You need to set 456 to 'port' prop. | ||||
| # | ||||
| # In other words, the 'url' prop should be the final accessible URL seen by a user. | ||||
| # 'port' prop is a port that the Misskey server should actually listen | ||||
| # on and it is not necessarily the port that a user accesses. | ||||
|  | ||||
| url: http://localhost/ | ||||
| # Final accessible URL seen by a user. | ||||
| url: https://example.tld/ | ||||
|  | ||||
|  | ||||
| ### Port and TLS settings ###################################### | ||||
| # | ||||
| # Misskey supports two deployment options for public. | ||||
| # | ||||
|  | ||||
| # Option 1: With Reverse Proxy | ||||
| # | ||||
| #                 +----- https://example.tld/ ------------+ | ||||
| #   +------+      |+-------------+      +----------------+| | ||||
| #   | User | ---> || Proxy (443) | ---> | Misskey (3000) || | ||||
| #   +------+      |+-------------+      +----------------+| | ||||
| #                 +---------------------------------------+ | ||||
| # | ||||
| #   You need to setup reverse proxy. (eg. Nginx) | ||||
| #   You do not define 'https' section. | ||||
|  | ||||
| # Option 2: Standalone | ||||
| # | ||||
| #                 +- https://example.tld/ -+ | ||||
| #   +------+      |   +---------------+    | | ||||
| #   | User | ---> |   | Misskey (443) |    | | ||||
| #   +------+      |   +---------------+    | | ||||
| #                 +------------------------+ | ||||
| # | ||||
| #   You need to run Misskey as root. | ||||
| #   You need to set Certificate in 'https' section. | ||||
|  | ||||
| # To use option 1, uncomment below line. | ||||
| # port: 3000    # A port that your Misskey server should listen. | ||||
|  | ||||
| # To use option 2, uncomment below lines. | ||||
| # port: 443 | ||||
| # | ||||
| # https: | ||||
| #   # path for certification | ||||
| #   key: /etc/letsencrypt/live/example.tld/privkey.pem | ||||
| #   cert: /etc/letsencrypt/live/example.tld/fullchain.pem | ||||
|  | ||||
| ################################################################ | ||||
|  | ||||
| # A port that your Misskey server should listen. | ||||
| # This value is not a port to use when accessing with a browser. | ||||
| port: 80 | ||||
|  | ||||
| mongodb: | ||||
|   host: localhost | ||||
| @@ -98,12 +122,6 @@ drive: | ||||
| # Below settings are optional | ||||
| # | ||||
|  | ||||
| # TLS | ||||
| # https: | ||||
| #   # path for certification | ||||
| #   key: example-tls-key | ||||
| #   cert: example-tls-cert | ||||
|  | ||||
| # Elasticsearch | ||||
| # elasticsearch: | ||||
| #   host: localhost | ||||
| @@ -141,3 +159,10 @@ drive: | ||||
|  | ||||
| # Summaly proxy | ||||
| # summalyProxy: "http://example.com" | ||||
|  | ||||
| # User recommendation | ||||
| user_recommendation: | ||||
|   external: true | ||||
|   engine: http://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}} | ||||
|   timeout: 300000 | ||||
|  | ||||
|   | ||||
							
								
								
									
										13
									
								
								.config/mongo_initdb_example.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								.config/mongo_initdb_example.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| var user = { | ||||
| 	user: 'example-misskey-user', | ||||
| 	pwd: 'example-misskey-pass', | ||||
| 	roles: [ | ||||
| 	    { | ||||
| 		    role: 'readWrite', | ||||
| 		    db: 'misskey' | ||||
| 	    } | ||||
| 	] | ||||
| }; | ||||
|  | ||||
| db.createUser(user); | ||||
|  | ||||
							
								
								
									
										12
									
								
								.dockerignore
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										12
									
								
								.dockerignore
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| .autogen | ||||
| .git | ||||
| .github | ||||
| .travis | ||||
| .vscode | ||||
| Dockerfile | ||||
| build/ | ||||
| docker-compose.yml | ||||
| node_modules/ | ||||
| mongo/ | ||||
| redis/ | ||||
| elasticsearch/ | ||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,6 @@ | ||||
| /.config/* | ||||
| !/.config/example.yml | ||||
| !/.config/mongo_initdb_example.js | ||||
| /.vscode | ||||
| /node_modules | ||||
| /build | ||||
| @@ -12,3 +13,6 @@ npm-debug.log | ||||
| run.bat | ||||
| api-docs.json | ||||
| *.log | ||||
| /redis | ||||
| /mongo | ||||
| /elasticsearch | ||||
|   | ||||
							
								
								
									
										88
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -5,6 +5,94 @@ ChangeLog | ||||
|  | ||||
| This document describes breaking changes only. | ||||
|  | ||||
| 10.0.0 | ||||
| ------ | ||||
|  | ||||
| ストリーミングAPIに破壊的変更があります。運営者がすべきことはありません。 | ||||
|  | ||||
| 変更は以下の通りです | ||||
|  | ||||
| * ストリーミングでやり取りする際の snake_case が全て camelCase に | ||||
| * リバーシのストリームエンドポイント名が reversi → gamesReversi、reversiGame → gamesReversiGame に | ||||
| * ストリーミングの個々のエンドポイントが廃止され、一旦元となるストリームに接続してから、個々のチャンネル(今までのエンドポイント)に接続します。詳細は後述します。 | ||||
| * ストリームから流れてくる、キャプチャした投稿の更新イベントに投稿自体のデータは含まれず、代わりにアクションが設定されるようになります。詳細は後述します。 | ||||
| * ストリームに接続する際に追加で指定していたパラメータ(トークン除く)が、URLにクエリとして含むのではなくチャンネル接続時にパラメータ指定するように | ||||
|  | ||||
| ### 個々のエンドポイントが廃止されることによる新しいストリーミングAPIの利用方法 | ||||
| 具体的には、まず https://example.misskey/streaming にwebsocket接続します。 | ||||
| 次に、例えば「messaging」ストリーム(チャンネルと呼びます)に接続したいときは、ストリームに次のようなデータを送信します: | ||||
| ``` javascript | ||||
| { | ||||
|   type: 'connect', | ||||
|   body: { | ||||
|     channel: 'messaging', | ||||
|     id: 'foobar', | ||||
|     params: { | ||||
|       otherparty: 'xxxxxxxxxxxx' | ||||
|     } | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| ここで、`id`にはそのチャンネルとやり取りするための任意のIDを設定します。 | ||||
| IDはチャンネルごとではなく「チャンネルの接続ごと」です。なぜなら、同じチャンネルに異なるパラメータで複数接続するケースもあるからです。 | ||||
| `params`はチャンネルに接続する際のパラメータです。チャンネルによって接続時に必要とされるパラメータは異なります。パラメータ不要のチャンネルに接続する際は、このプロパティは省略可能です。 | ||||
|  | ||||
| チャンネルにメッセージを送信するには、次のようなデータを送信します: | ||||
| ``` javascript | ||||
| { | ||||
|   type: 'channel', | ||||
|   body: { | ||||
|     id: 'foobar', | ||||
|     type: 'something', | ||||
|     body: { | ||||
|       some: 'thing' | ||||
|     } | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| ここで、`id`にはチャンネルに接続するときに指定したIDを設定します。 | ||||
|  | ||||
| 逆に、チャンネルからメッセージが流れてくると、次のようなデータが受信されます: | ||||
| ``` javascript | ||||
| { | ||||
|   type: 'channel', | ||||
|   body: { | ||||
|     id: 'foobar', | ||||
|     type: 'something', | ||||
|     body: { | ||||
|       some: 'thing' | ||||
|     } | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| ここで、`id`にはチャンネルに接続するときに指定したIDが設定されています。 | ||||
|  | ||||
| ### 投稿のキャプチャに関する変更 | ||||
| 投稿の更新イベントに投稿情報は含まれなくなりました。代わりに、その投稿が「リアクションされた」「アンケートに投票された」「削除された」といったアクション情報が設定されます。 | ||||
|  | ||||
| 具体的には次のようなデータが受信されます: | ||||
| ``` javascript | ||||
| { | ||||
|   type: 'noteUpdated', | ||||
|   body: { | ||||
|     id: 'xxxxxxxxxxx', | ||||
|     type: 'reacted', | ||||
|     body: { | ||||
|       reaction: 'hmm' | ||||
|     } | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| * reacted ... 投稿にリアクションされた。`reaction`プロパティにリアクションコードが含まれます。 | ||||
| * pollVoted ... アンケートに投票された。`choice`プロパティに選択肢ID、`userId`に投票者IDが含まれます。 | ||||
|  | ||||
| 9.0.0 | ||||
| ----- | ||||
|  | ||||
| Misskey v8.64.0 を使っている方は、9.0.0に際しては特にすべきことはありません。 | ||||
| Misskey v8.64.0 に満たないバージョンをお使いの方は、一旦8.64.0にアップデートして(そして起動して)から9.0.0に再度アップデートしてください。 | ||||
|  | ||||
| 8.0.0 | ||||
| ----- | ||||
|  | ||||
|   | ||||
							
								
								
									
										28
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| FROM alpine:latest AS base | ||||
|  | ||||
| ENV NODE_ENV=production | ||||
|  | ||||
| RUN apk add --no-cache nodejs nodejs-npm | ||||
| RUN apk add vips fftw --update-cache --repository https://dl-3.alpinelinux.org/alpine/edge/testing/ | ||||
| WORKDIR /misskey | ||||
| COPY . ./ | ||||
|  | ||||
| FROM base AS builder | ||||
|  | ||||
| RUN apk add --no-cache	gcc g++ python autoconf automake file make nasm | ||||
| RUN apk add vips-dev fftw-dev --update-cache --repository https://dl-3.alpinelinux.org/alpine/edge/testing/ | ||||
| RUN npm install \ | ||||
|     && npm install -g node-gyp \ | ||||
|     && node-gyp configure \ | ||||
|     && node-gyp build \ | ||||
|     && npm run build | ||||
|  | ||||
| FROM base AS runner | ||||
|  | ||||
| COPY --from=builder /misskey/built ./built | ||||
| COPY --from=builder /misskey/node_modules ./node_modules | ||||
|  | ||||
| RUN apk add --no-cache tini | ||||
| ENTRYPOINT ["/sbin/tini", "--"] | ||||
|  | ||||
| CMD ["npm", "start"] | ||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							| @@ -71,19 +71,21 @@ Please see [Contribution guide](./CONTRIBUTING.md). | ||||
| ---------------------------------------------------------------- | ||||
| <!-- PATREON_START --> | ||||
| <table><tr> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=CXe9AqlZy9AsYfiWd3OBYVOzvODoN47Litz0Tu4BFpU%3D" alt="Gargron"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=Yd60FK_SWfQO56SeiJpy1tDHOnCV4xdEywQe8gn5_Wo%3D" alt="negao"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1?token-time=2145916800&token-hash=d6P5MWHHsCMxUuBAEPAoVc5wLUR19mIhqAq7Ma9h9rI%3D" alt="ne_moni"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/1?token-time=2145916800&token-hash=f03BFb4S2FUx9YEt87TnEmifb4h33OywGBW2akQVtQY%3D" alt="Melilot"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12999811/5f349fafcce44dd1824a8b1ebbec4564/2?token-time=2145916800&token-hash=rwZ8qvbm_kpA4ib3kc07tVKupXeySpY5ATQFGxfL9v0%3D" alt="Axella"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/2?token-time=2145916800&token-hash=mgPdX9TqZxEg4TTPuc477dxhIgYk9246qafjWZEqZ7g%3D" alt="Melilot"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12999811/5f349fafcce44dd1824a8b1ebbec4564/2?token-time=2145916800&token-hash=rwZ8qvbm_kpA4ib3kc07tVKupXeySpY5ATQFGxfL9v0%3D" alt="Xeltica"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/3384329/8b713330cb27404ea6e9fac50ff96efe/1?token-time=2145916800&token-hash=0eu4-m1gTWA9PhptVZt6rdKcusqcD7RB87rJT23VVFI%3D" alt="べすれい"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=GgJ_NmUB6_nnRNLVGUWjV-WX91On7BOu59LKncYV9fE%3D" alt="gutfuckllc"></td> | ||||
| <td><img src="https://c8.patreon.com/2/100/12718187" alt="Peter G."></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=zwSu01tOtn5xTUucDZHuPsCxF2HBEMVs9ROJKTlEV_o%3D" alt="nemu"></td> | ||||
| </tr><tr> | ||||
| <td><a href="https://www.patreon.com/user?u=12731202">negao</a></td> | ||||
| <td><a href="https://www.patreon.com/mastodon">Gargron</a></td> | ||||
| <td><a href="https://www.patreon.com/negao">negao</a></td> | ||||
| <td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td> | ||||
| <td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td> | ||||
| <td><a href="https://www.patreon.com/AxellaMC">Axella</a></td> | ||||
| <td><a href="https://www.patreon.com/AxellaMC">Xeltica</a></td> | ||||
| <td><a href="https://www.patreon.com/user?u=3384329">べすれい</a></td> | ||||
| <td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td> | ||||
| <td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td> | ||||
| @@ -91,19 +93,17 @@ Please see [Contribution guide](./CONTRIBUTING.md). | ||||
| </tr></table> | ||||
| <table><tr> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/2?token-time=2145916800&token-hash=zElv7ZcPL3viGsXbNG_KWiKrbV0vvw1gk0panx8DJoo%3D" alt="Naoki Kosaka"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12931605/ead494101f364dffa90efe49e36fb494/1?token-time=2145916800&token-hash=NzSFPjIlodXyv41rwK61aZWVZWfI4surJaNj8vWKvqM%3D" alt="Reiju"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1?token-time=2145916800&token-hash=UERBN4OyP7Nh5XwwdDg0N0IE5cD6_qUQMO81Z5Wizso%3D" alt="Hiratake"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1?token-time=2145916800&token-hash=S1zP0QyLU52Dqq6dtc9qNYyWfW86XrYHiR4NMbeOrnA%3D" alt="dansup"></td> | ||||
| <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1?token-time=2145916800&token-hash=tMosUojzUYJCH_3t--tvYA-SMCyrS__hzSndyaRSnbo%3D" alt="Takashi Shibuya"></td> | ||||
| </tr><tr> | ||||
| <td><a href="https://www.patreon.com/user?u=5881381">Naoki Kosaka</a></td> | ||||
| <td><a href="https://www.patreon.com/user?u=12931605">Reiju</a></td> | ||||
| <td><a href="https://www.patreon.com/hiratake">Hiratake</a></td> | ||||
| <td><a href="https://www.patreon.com/dansup">dansup</a></td> | ||||
| <td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td> | ||||
| </tr></table> | ||||
|  | ||||
| **Last updated:** Sun, 02 Sep 2018 05:30:06 UTC | ||||
| **Last updated:** Tue, 02 Oct 2018 09:25:07 UTC | ||||
| <!-- PATREON_END --> | ||||
|  | ||||
| :four_leaf_clover: Copyright | ||||
|   | ||||
							
								
								
									
										52
									
								
								docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| version: "3" | ||||
|  | ||||
| services: | ||||
|   web: | ||||
|     build: . | ||||
|     restart: always | ||||
|     links: | ||||
|       - mongo | ||||
|       - redis | ||||
| #      - es | ||||
|     ports: | ||||
|       - "127.0.0.1:3000:3000" | ||||
|     networks: | ||||
|       - internal_network | ||||
|       - external_network | ||||
|  | ||||
|   redis: | ||||
|     restart: always | ||||
|     image: redis:4.0-alpine | ||||
|     networks: | ||||
|       - internal_network | ||||
| ### Uncomment to enable Redis persistance | ||||
| #    volumes: | ||||
| #      - ./redis:/data | ||||
|  | ||||
|   mongo: | ||||
|     restart: always | ||||
|     image: mongo:4.1-bionic | ||||
|     networks: | ||||
|       - internal_network | ||||
|     environment: | ||||
|       MONGO_INITDB_DATABASE: "misskey" | ||||
|     volumes: | ||||
|       - ./.config/mongo_initdb.js:/docker-entrypoint-initdb.d/mongo_initdb.js:ro | ||||
| ### Uncomment to enable MongoDB persistance | ||||
| #      - ./mongo:/data | ||||
|  | ||||
| #  es: | ||||
| #    restart: always | ||||
| #    image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2 | ||||
| #    environment: | ||||
| #      - "ES_JAVA_OPTS=-Xms512m -Xmx512m" | ||||
| #    networks: | ||||
| #      - internal_network | ||||
| #### Uncomment to enable ES persistence | ||||
| ##    volumes: | ||||
| ##      - ./elasticsearch:/usr/share/elasticsearch/data | ||||
|  | ||||
| networks: | ||||
|   internal_network: | ||||
|     internal: true | ||||
|   external_network: | ||||
							
								
								
									
										47
									
								
								docs/docker.en.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								docs/docker.en.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| Docker Guide | ||||
| ================================================================ | ||||
|  | ||||
| This guide describes how to install and setup Misskey with Docker. | ||||
|  | ||||
| [Japanese version also available - 日本語版もあります](./docker.ja.md) | ||||
|  | ||||
| ---------------------------------------------------------------- | ||||
|  | ||||
| *1.* Make configuration files | ||||
| ---------------------------------------------------------------- | ||||
| 1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`. | ||||
| 2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` Copy the `.config/mongo_initdb_example.js` and rename it to `mongo_initdb.js`. | ||||
| 2. Edit `default.yml` and `mongo_initdb.js`. | ||||
|  | ||||
| *2.* Configure Docker | ||||
| ---------------------------------------------------------------- | ||||
| Edit `docker-compose.yml`. | ||||
|  | ||||
| *3.* Build Misskey | ||||
| ---------------------------------------------------------------- | ||||
| Build misskey with the following: | ||||
|  | ||||
| `docker-compose build` | ||||
|  | ||||
| *4.* That is it. | ||||
| ---------------------------------------------------------------- | ||||
| Well done! Now, you have an environment that run to Misskey. | ||||
|  | ||||
| ### Launch normally | ||||
| Just `docker-compose up -d`. GLHF! | ||||
|  | ||||
| ### Way to Update to latest version of your Misskey | ||||
| 1. `git fetch` | ||||
| 2. `git stash` | ||||
| 3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` | ||||
| 4. `git stash pop` | ||||
| 5. `docker-compose build` | ||||
| 6. Check [ChangeLog](../CHANGELOG.md) for migration information | ||||
| 7. `docker-compose stop && docker-compose up -d` | ||||
|  | ||||
| ### Way to execute cli command: | ||||
| `docker-compose run --rm web node cli/mark-admin @example` | ||||
|  | ||||
| ---------------------------------------------------------------- | ||||
|  | ||||
| If you have any questions or troubles, feel free to contact us! | ||||
							
								
								
									
										48
									
								
								docs/docker.ja.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								docs/docker.ja.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| Dockerを使ったMisskey構築方法 | ||||
| ================================================================ | ||||
|  | ||||
| このガイドはDockerを使ったMisskeyセットアップ方法について解説します。 | ||||
|  | ||||
| [英語版もあります - English version also available](./docker.en.md) | ||||
|  | ||||
| ---------------------------------------------------------------- | ||||
|  | ||||
| *1.* 設定ファイルを作成する | ||||
| ---------------------------------------------------------------- | ||||
| 1. `cp .config/example.yml .config/default.yml` `.config/example.yml`をコピーし名前を`default.yml`にする | ||||
| 2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` `.config/mongo_initdb_example.js`をコピーし名前を`mongo_initdb.js`にする | ||||
| 3. `default.yml`と`mongo_initdb.js`を編集する | ||||
|  | ||||
| *2.* Dockerの設定 | ||||
| ---------------------------------------------------------------- | ||||
| `docker-compose.yml`を編集してください。 | ||||
|  | ||||
| *3.* Misskeyのビルド | ||||
| ---------------------------------------------------------------- | ||||
| 次のコマンドでMisskeyをビルドしてください: | ||||
|  | ||||
| `docker-compose build` | ||||
|  | ||||
| *4.* 以上です! | ||||
| ---------------------------------------------------------------- | ||||
| お疲れ様でした。これでMisskeyを動かす準備は整いました。 | ||||
|  | ||||
| ### 通常起動 | ||||
| `docker-compose up -d`するだけです。GLHF! | ||||
|  | ||||
| ### Misskeyを最新バージョンにアップデートする方法: | ||||
| 1. `git fetch` | ||||
| 2. `git stash` | ||||
| 3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` | ||||
| 4. `git stash pop` | ||||
| 5. `docker-compose build` | ||||
| 6. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する | ||||
| 7. `docker-compose stop && docker-compose up -d` | ||||
|  | ||||
| ### cliコマンドを実行する方法: | ||||
|  | ||||
| `docker-compose run --rm web node cli/mark-admin @example` | ||||
|  | ||||
| ---------------------------------------------------------------- | ||||
|  | ||||
| なにかお困りのことがありましたらお気軽にご連絡ください。 | ||||
| @@ -2,7 +2,6 @@ | ||||
|  * Gulp tasks | ||||
|  */ | ||||
|  | ||||
| import * as fs from 'fs'; | ||||
| import * as gulp from 'gulp'; | ||||
| import * as gutil from 'gulp-util'; | ||||
| import * as ts from 'gulp-typescript'; | ||||
| @@ -78,7 +77,7 @@ gulp.task('build:copy', ['build:copy:views', 'build:copy:lang'], () => | ||||
| 	]).pipe(gulp.dest('./built/')) | ||||
| ); | ||||
|  | ||||
| gulp.task('test', ['lint', 'mocha']); | ||||
| gulp.task('test', ['mocha']); | ||||
|  | ||||
| gulp.task('lint', () => | ||||
| 	gulp.src('./src/**/*.ts') | ||||
| @@ -166,9 +165,7 @@ gulp.task('build:client:pug', [ | ||||
| 			.pipe(pug({ | ||||
| 				locals: { | ||||
| 					themeColor: constants.themeColor, | ||||
| 					facss: fa.dom.css(), | ||||
| 					//hljscss: fs.readFileSync('./node_modules/highlight.js/styles/default.css', 'utf8') | ||||
| 					hljscss: fs.readFileSync('./src/client/assets/code-highlight.css', 'utf8') | ||||
| 					facss: fa.dom.css() | ||||
| 				} | ||||
| 			})) | ||||
| 			.pipe(htmlmin({ | ||||
|   | ||||
| @@ -155,8 +155,10 @@ common: | ||||
|     home: "ホーム" | ||||
|     local: "ローカル" | ||||
|     hybrid: "ソーシャル" | ||||
|     hashtag: "ハッシュタグ" | ||||
|     global: "グローバル" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "通知" | ||||
|     list: "リスト" | ||||
|     swap-left: "左に移動" | ||||
| @@ -262,6 +264,41 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "リンクをコピー" | ||||
|   favorite: "お気に入り" | ||||
|   pin: "ピン留め" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "削除" | ||||
|   delete-confirm: "この投稿を削除しますか?" | ||||
|   remote: "投稿元で見る" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop: | ||||
|   banner-crop-title: "バナーとして表示する部分を選択" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "ユーザーの増減" | ||||
|     users-total: "ユーザーの累計" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "{} のフォロワー" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "二段階認証" | ||||
|   other: "その他" | ||||
|   license: "ライセンス" | ||||
|   theme: "テーマ" | ||||
|   behaviour: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。" | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "壁紙を選択" | ||||
|   delete-wallpaper: "壁紙を削除" | ||||
|   dark-mode: "ダークモード" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "円形のアイコンを使用" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用" | ||||
|   post-form-on-timeline: "タイムライン上部に投稿フォームを表示する" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "マップの自動展開" | ||||
|   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。" | ||||
|   sound: "サウンド" | ||||
|   enable-sounds: "サウンドを有効にする" | ||||
|   enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。" | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "リスト" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "おかえりなさい、" | ||||
|   adjective: "さん" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "気になるユーザーをフォロー" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
| @@ -155,8 +155,10 @@ common: | ||||
|     home: "Startseite" | ||||
|     local: "Lokal" | ||||
|     hybrid: "ソーシャル" | ||||
|     hashtag: "ハッシュタグ" | ||||
|     global: "Global" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "Mitteilungen" | ||||
|     list: "Listen" | ||||
|     swap-left: "Nach links" | ||||
| @@ -262,6 +264,41 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "リンクをコピー" | ||||
|   favorite: "Diese Anmerkung favorisieren" | ||||
|   pin: "An die Profilseite pinnen" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "Löschen" | ||||
|   delete-confirm: "Diesen Post löschen?" | ||||
|   remote: "Auf Quelle anzeigen" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop: | ||||
|   banner-crop-title: "バナーとして表示する部分を選択" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "ユーザーの増減" | ||||
|     users-total: "ユーザーの累計" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "Folge ich" | ||||
|   follow: "Folgen" | ||||
|   request-pending: "Ausstehend" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "Follower-Anfragen" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "{} のフォロワー" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "Zwei-Faktor-Authentifizierung" | ||||
|   other: "Anderes" | ||||
|   license: "Lizenz" | ||||
|   theme: "テーマ" | ||||
|   behaviour: "Verhalten" | ||||
|   fetch-on-scroll: "Aktualisieren beim scrollen" | ||||
|   fetch-on-scroll-desc: "Wenn du runterscrollst empfängt die Seite automatisch zusätzliche Inhalte." | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "壁紙を選択" | ||||
|   delete-wallpaper: "壁紙を削除" | ||||
|   dark-mode: "Nacht Modus" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "Kreisförmige Icons" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "Übergang in Fensterköpfen" | ||||
|   post-form-on-timeline: "タイムライン上部に投稿フォームを表示する" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "Karte anzeigen" | ||||
|   show-maps-desc: "Zeige den Standort zu diesem Beitrag automatisch an." | ||||
|   sound: "Ton" | ||||
|   enable-sounds: "Ton aktivieren" | ||||
|   enable-sounds-desc: "Spiel einen Ton ab beim Erhalten eines Beitrags bzw. einer Nachricht. Diese Einstellung wird im Browser gespeichert." | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "Global" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "Listen" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "おかえりなさい、" | ||||
|   adjective: "さん" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "気になるユーザーをフォロー" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
| @@ -109,8 +109,8 @@ common: | ||||
|   use-contrast-reversi-stones: "Make the stone color clear in reversi" | ||||
|   verified-user: "Verified account" | ||||
|   disable-animated-mfm: "Disable animated texts in a post" | ||||
|   always-show-nsfw: "常に閲覧注意のメディアを表示する" | ||||
|   always-mark-nsfw: "常にメディアを閲覧注意として投稿" | ||||
|   always-show-nsfw: "Always show NSFW contents" | ||||
|   always-mark-nsfw: "Always post with a warning about media attachment" | ||||
|   show-full-acct: "Do not omit the hostname from the username" | ||||
|   reduce-motion: "Reduce motion in UI" | ||||
|   this-setting-is-this-device-only: "Only for this device" | ||||
| @@ -155,8 +155,10 @@ common: | ||||
|     home: "Home" | ||||
|     local: "Local" | ||||
|     hybrid: "Social" | ||||
|     hashtag: "Hashtag" | ||||
|     global: "Global" | ||||
|     mentions: "Mentions" | ||||
|     direct: "Direct post" | ||||
|     notifications: "Notifications" | ||||
|     list: "Lists" | ||||
|     swap-left: "Move to the left" | ||||
| @@ -262,6 +264,41 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "NSFW" | ||||
|   click-to-show: "Click to show" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "Hide" | ||||
|   show: "See more" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "Copy link" | ||||
|   favorite: "Favorite this note" | ||||
|   pin: "Pin to your profile" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "Delete" | ||||
|   delete-confirm: "Delete this post?" | ||||
|   remote: "Show original note" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "Following" | ||||
|   follow: "Follow" | ||||
|   request-pending: "Pending follow request" | ||||
|   follow-processing: "Processing follow" | ||||
|   follow-request: "Follow request" | ||||
| desktop: | ||||
|   banner-crop-title: "Crop the part that appears as a banner" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "The number of posts: increase/decrease (Combined)" | ||||
|     local-notes: "The number of posts: increase/decrease (Local)" | ||||
|     remote-notes: "The number of posts: increase/decrease (Remote)" | ||||
|     notes-total: "The number of posts: cumulative total" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "The number of users: increase/decrease" | ||||
|     users-total: "The number of users: cumulative total" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "Capacity used as the storage: increase/decrease" | ||||
|     drive-total: "Capacity used as the storage: cumulative total" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "The number of files on the storage: increase/decrease" | ||||
|     drive-files-total: "The number of files on the storage: cumulative total" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "Requests" | ||||
|     network-time: "Response time" | ||||
|     network-usage: "Traffic" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "Following" | ||||
|   follow: "Follow" | ||||
|   request-pending: "Pending follow request" | ||||
|   follow-processing: "Processing follow" | ||||
|   follow-request: "Follow request" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "{}'s followers" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "Two-factor authentication" | ||||
|   other: "Other" | ||||
|   license: "License" | ||||
|   theme: "Theme" | ||||
|   behaviour: "Behavior" | ||||
|   fetch-on-scroll: "Endless loading on scroll" | ||||
|   fetch-on-scroll-desc: "When you scroll down the page, it automatically fetches additional content." | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "Choose a background" | ||||
|   delete-wallpaper: "Remove background" | ||||
|   dark-mode: "Dark Mode" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "Use circle icons" | ||||
|   contrasted-acct: "Add contrast to username" | ||||
|   gradient-window-header: "Use gradients on window headers" | ||||
|   post-form-on-timeline: "Display post form at the top of the timeline" | ||||
|   suggest-recent-hashtags: "Show recent popular hashtags on the post form" | ||||
|   show-clock-on-header: "Show clock on upper-right" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "Show renoted my posts in timelines" | ||||
|   show-local-renotes: "Show renoted local posts in timelines" | ||||
|   show-maps: "Display a map to show the location" | ||||
|   show-maps-desc: "If there comes a post contains location information, show a map to display the location." | ||||
|   sound: "Sound" | ||||
|   enable-sounds: "Enable sound" | ||||
|   enable-sounds-desc: "Play a sound when you receive a post/message. This setting is stored in the browser." | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "Social" | ||||
|   global: "Global" | ||||
|   mentions: "Mentions" | ||||
|   messages: "Messages" | ||||
|   list: "Lists" | ||||
|   hashtag: "Hashtag" | ||||
|   add-tag-timeline: "Add hashtag tl" | ||||
|   add-list: "Add list" | ||||
|   list-name: "List name" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "Welcome back," | ||||
|   adjective: "-san" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "Following" | ||||
|   follow: "Follow" | ||||
|   request-pending: "Pending follow request" | ||||
|   follow-processing: "Processing follow" | ||||
|   follow-request: "Follow request" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "Let's follow them" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "Social" | ||||
|   global: "Global" | ||||
|   mentions: "Mentions" | ||||
|   messages: "Messages" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "No posts \"{}\" found." | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "Notification style" | ||||
|   notification-position-bottom: "Bottom" | ||||
|   notification-position-top: "Top" | ||||
|   theme: "Theme" | ||||
|   behavior: "Behavior" | ||||
|   fetch-on-scroll: "Endless loading on scroll" | ||||
|   note-visibility: "Post visibility" | ||||
|   | ||||
| @@ -155,8 +155,10 @@ common: | ||||
|     home: "Inicio" | ||||
|     local: "Local" | ||||
|     hybrid: "Social" | ||||
|     hashtag: "ハッシュタグ" | ||||
|     global: "Global" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "Notificaciones" | ||||
|     list: "Listado" | ||||
|     swap-left: "Desplazar a la izq." | ||||
| @@ -262,6 +264,41 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "Copiar enlace" | ||||
|   favorite: "Me gusta esta nota" | ||||
|   pin: "Fijar en el perfil" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "Borrar" | ||||
|   delete-confirm: "¿Seguro que quieres borrar la publicación?" | ||||
|   remote: "Ver el original" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "Siguiendo" | ||||
|   follow: "Seguir" | ||||
|   request-pending: "Solicitud pendiente" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "Solicitar suscripción" | ||||
| desktop: | ||||
|   banner-crop-title: "Corta la parte que aparece como un banner" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "Número de publicaciones: aumentar/disminuir (Combinado)" | ||||
|     local-notes: "Número de publicaciones: aumentar/disminuir (Local)" | ||||
|     remote-notes: "Número de publicaciones: aumentar/disminuir (Remoto)" | ||||
|     notes-total: "Número de publicaciones: Acumulativo total" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "Número de usuarios: aumentar/disminuir" | ||||
|     users-total: "Número de usuarios: Acumulativo total" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "Capacidad de almacenamiento usada: aumentar/disminuir" | ||||
|     drive-total: "Capacidad de almacenamiento usada: Acumulativa total" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "Número de archivos almacenados: aumentar/disminuir" | ||||
|     drive-files-total: "Número de archivos almacenados: Acumulativo total" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "Siguiendo" | ||||
|   follow: "Sigue" | ||||
|   request-pending: "Pendiente de aprobación" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "Solicitud de seguir" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "{} seguidores" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "Autenticación de Doble-Factor" | ||||
|   other: "Otros" | ||||
|   license: "Licencia" | ||||
|   theme: "テーマ" | ||||
|   behaviour: "Acciones" | ||||
|   fetch-on-scroll: "Desplazamiento infinito" | ||||
|   fetch-on-scroll-desc: "Cuando te deslizas al final de la página nuevo contenido se carga automáticamente." | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "Elije un fondo" | ||||
|   delete-wallpaper: "Suprimir fondo" | ||||
|   dark-mode: "Modo Nocturno" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "Usar iconos circulares" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "Usar degradados en las cabeceras de las páginas" | ||||
|   post-form-on-timeline: "Mostrar el formulario de las entradas encima de la línea de tiempo" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "マップの自動展開" | ||||
|   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。" | ||||
|   sound: "サウンド" | ||||
|   enable-sounds: "サウンドを有効にする" | ||||
|   enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。" | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "リスト" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "Bienvenido/a de vuelta," | ||||
|   adjective: "-san" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "気になるユーザーをフォロー" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
| @@ -9,7 +9,7 @@ common: | ||||
|   intro: | ||||
|     title: "C’est quoi Misskey ?" | ||||
|     about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。" | ||||
|     features: "Fonctionnalités" | ||||
|     features: "Options" | ||||
|     rich-contents: "Notes" | ||||
|     rich-contents-desc: "自分の考え、話題の出来事、皆と共有したいことについて発信してください。必要であれば、様々な構文を使って投稿を装飾したり、好きな画像、動画などのファイルやアンケートを添付することもできます。" | ||||
|     reaction: "Réactions" | ||||
| @@ -32,7 +32,7 @@ common: | ||||
|     paragraph2: "Vous pouvez changer l'affichage en <strong>cliquant droit</strong> sur certains widgets." | ||||
|     paragraph3: "Pour supprimer un widget, <strong>glissez et déposez le widget sur la zone étiquetée « Corbeille »</strong> dans l'en-tête." | ||||
|     paragraph4: "Pour terminer la personnalisation, cliquez sur \"Terminer\" dans le coin supérieur droit." | ||||
|     gotit: "Compris!" | ||||
|     gotit: "Compris !" | ||||
|   notification: | ||||
|     file-uploaded: "Le fichier a été téléversé !" | ||||
|     message-from: "Message de {} :" | ||||
| @@ -52,7 +52,7 @@ common: | ||||
|     weeks_ago: "Il y a {} semaines·s" | ||||
|     months_ago: "Il y a {} mois" | ||||
|     years_ago: "Il y a {} an·s" | ||||
|   month-and-day: "{month}/{day}" | ||||
|   month-and-day: "{month} mois/{day} jour" | ||||
|   trash: "Corbeille" | ||||
|   weekday-short: | ||||
|     sunday: "D" | ||||
| @@ -111,8 +111,8 @@ common: | ||||
|   disable-animated-mfm: "Désactiver les textes animés dans les publications" | ||||
|   always-show-nsfw: "常に閲覧注意のメディアを表示する" | ||||
|   always-mark-nsfw: "常にメディアを閲覧注意として投稿" | ||||
|   show-full-acct: "ユーザー名のホストを省略しない" | ||||
|   reduce-motion: "UIの動きを減らす" | ||||
|   show-full-acct: "Afficher l’adresse complète de l’utilisateur" | ||||
|   reduce-motion: "Réduire les animations dans l’interface utilisateur" | ||||
|   this-setting-is-this-device-only: "Uniquement sur cet appareil" | ||||
|   do-not-use-in-production: 'Il s’agit d’une version de développement. Ne pas utiliser dans un environnement de production.' | ||||
|   reversi: | ||||
| @@ -149,14 +149,16 @@ common: | ||||
|     donation: "Dons" | ||||
|     nav: "Navigation" | ||||
|     tips: "Conseils" | ||||
|     hashtags: "Étiquettes" | ||||
|     hashtags: "Hashtags" | ||||
|   deck: | ||||
|     widgets: "Widgets" | ||||
|     home: "Accueil" | ||||
|     local: "Local" | ||||
|     hybrid: "Social" | ||||
|     hashtag: "Hashtag" | ||||
|     global: "Global" | ||||
|     mentions: "あなた宛て" | ||||
|     mentions: "Mentions" | ||||
|     direct: "Messages directs" | ||||
|     notifications: "Notifications" | ||||
|     list: "Liste" | ||||
|     swap-left: "Déplacer à gauche" | ||||
| @@ -187,7 +189,7 @@ auth/views/index.vue: | ||||
|   denied: "L'autorisation de l'application a été refusée." | ||||
|   denied-paragraph: "Cette application ne va pas accéder à votre compte." | ||||
|   already-authorized: "Cette application est déjà autorisée" | ||||
|   allowed: "アプリケーションの連携を許可しました" | ||||
|   allowed: "Permissions autorisées de l’application." | ||||
|   callback-url: "Retour vers l'application" | ||||
|   please-go-back: "Veillez retourner à l'application." | ||||
|   error: "La session n'existe pas." | ||||
| @@ -260,8 +262,43 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
|   flush: "Vider le cache" | ||||
|   set-version: "Choisissez une version" | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
|   sensitive: "Contenu sensible" | ||||
|   click-to-show: "Cliquer pour afficher" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "Installer un thème" | ||||
|   theme-code: "Code du thème" | ||||
|   install: "Installation" | ||||
|   installed: "« {} » a été installé" | ||||
|   create-a-theme: "Créer un thème" | ||||
|   save-created-theme: "Enregistrer le thème" | ||||
|   primary-color: "Couleur primaire" | ||||
|   secondary-color: "Couleur secondaire" | ||||
|   text-color: "Couleur du texte" | ||||
|   base-theme: "Thème de base" | ||||
|   base-theme-light: "Clair" | ||||
|   base-theme-dark: "Sombre" | ||||
|   theme-name: "Nom du Thème" | ||||
|   preview-created-theme: "Prévisualisation" | ||||
|   invalid-theme: "Thème n’est pas valide." | ||||
|   already-installed: "Le thème est déjà installé." | ||||
|   saved: "enregistré" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "Thèmes installés" | ||||
|   select-theme: "Veuillez sélectionner un thème" | ||||
|   uninstall: "Désinstaller" | ||||
|   uninstalled: "« {} » a été désinstallé" | ||||
|   author: "Auteur" | ||||
|   desc: "Description" | ||||
|   export: "エクスポート" | ||||
|   import: "Importer" | ||||
|   import-by-code: "Ou coller du code" | ||||
|   theme-name-required: "Nom du thème est obligatoire." | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "Masquer" | ||||
|   show: "Voir plus" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "Copier le lien" | ||||
|   favorite: "Mettre cette note en favoris" | ||||
|   pin: "Épingler sur votre profil" | ||||
|   unpin: "Désépingler" | ||||
|   delete: "Supprimer" | ||||
|   delete-confirm: "Supprimer cette publication ?" | ||||
|   remote: "Afficher la note originale" | ||||
| @@ -399,7 +437,7 @@ common/views/widgets/posts-monitor.vue: | ||||
|   title: "Graphe des publications" | ||||
|   toggle: "Basculer entre les vues" | ||||
| common/views/widgets/hashtags.vue: | ||||
|   title: "Étiquettes" | ||||
|   title: "Hashtags" | ||||
| common/views/widgets/server.vue: | ||||
|   title: "Informations sur le serveur" | ||||
|   toggle: "Afficher les vues" | ||||
| @@ -418,7 +456,7 @@ common/views/widgets/tips.vue: | ||||
|   tips-line4: "Vous pouvez coller des images à partir du presse-papier sur la fenêtre de la note" | ||||
|   tips-line5: "Vous pouvez téléverser des fichiers sur le Drive en faisant un glisser-déposer" | ||||
|   tips-line6: "Vous pouvez déplacer un dossier en le glissant dans le Drive" | ||||
|   tips-line7: "ドライブでフォルダをドラッグしてフォルダ移動できます" | ||||
|   tips-line7: "Vous pouvez déplacer des dossiers en les glissant dans le Drive" | ||||
|   tips-line8: "Vous pouvez personnaliser l'Accueil via les paramètres" | ||||
|   tips-line9: "Misskey est sous licence AGPLv3" | ||||
|   tips-line10: "タイムマシンウィジェットを利用すると、簡単に過去のタイムラインに遡れます" | ||||
| @@ -426,7 +464,7 @@ common/views/widgets/tips.vue: | ||||
|   tips-line13: "Tous les fichiers attachés à cette publication sont sauvegardés dans le Drive" | ||||
|   tips-line14: "ホームのカスタマイズ中、ウィジェットを右クリックしてデザインを変更できます" | ||||
|   tips-line17: "Vous pouvez mettre un texte en surbrillance en le mettant entre ** **" | ||||
|   tips-line19: "いくつかのウィンドウはブラウザの外に切り離すことができます" | ||||
|   tips-line19: "Plusieurs fenêtres peuvent être détachées en dehors du navigateur." | ||||
|   tips-line20: "カレンダーウィジェットのパーセンテージは、経過の割合を示しています" | ||||
|   tips-line21: "Vous pouvez aussi utiliser l'API pour développer des Bots." | ||||
|   tips-line23: "Mayu est mignone avec ses sourcils." | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "Suit" | ||||
|   follow: "Suivre" | ||||
|   request-pending: "Demande d'abonnement en attente" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "Demande d'abonnement" | ||||
| desktop: | ||||
|   banner-crop-title: "Découpez la partie qui apparaitra comme bannière" | ||||
| @@ -449,7 +488,7 @@ desktop: | ||||
|   uploading-avatar: "Téléversement du nouvel avatar" | ||||
|   avatar-updated: "L'avatar est mis à jour" | ||||
|   choose-avatar: "Choisir un avatar" | ||||
|   invalid-filetype: "この形式のファイルはサポートされていません" | ||||
|   invalid-filetype: "Ce format de fichier n’est pas pris en charge" | ||||
| desktop/views/components/activity.chart.vue: | ||||
|   total: "Noirs ... Total" | ||||
|   notes: "Bleu ... Notes" | ||||
| @@ -475,16 +514,16 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "Nombre d’utilisateurs·trices : augmentation/diminution" | ||||
|     users-total: "Nombre total d’utilisateurs·trices : total cumulé" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "Requêtes" | ||||
|     network-time: "Temps de réponse" | ||||
|     network-usage: "通信量" | ||||
|     network-usage: "Traffic" | ||||
| desktop/views/components/choose-file-from-drive-window.vue: | ||||
|   choose-file: "Sélection de fichiers" | ||||
|   upload: "Téléverser des fichiers à partir de votre ordinateur" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "Abonnements" | ||||
|   follow: "Suivre" | ||||
|   request-pending: "En attente d'approbation" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "Demande d'abonnement" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "{} abonné·e·s" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "Vérification en deux étapes" | ||||
|   other: "Autres" | ||||
|   license: "License" | ||||
|   theme: "Thèmes" | ||||
|   behaviour: "Comportement" | ||||
|   fetch-on-scroll: "Chargement lors du défilement" | ||||
|   fetch-on-scroll-desc: "Chargement automatique du contenu lors du défilement de la page." | ||||
| @@ -689,18 +730,18 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "Sélectionner un fond d'écran" | ||||
|   delete-wallpaper: "Supprimer le fond d'écran" | ||||
|   dark-mode: "Mode nuit" | ||||
|   use-shadow: "Utiliser les ombres dans l'interface utilisateur" | ||||
|   rounded-corners: "Coins arrondis" | ||||
|   circle-icons: "Utiliser des icônes circulaires" | ||||
|   contrasted-acct: "Nom d’utilisateur contrasté" | ||||
|   gradient-window-header: "Utiliser les dégradés sur la barre de titre de la fenêtre" | ||||
|   post-form-on-timeline: "Afficher le formulaire en haut du fil" | ||||
|   suggest-recent-hashtags: "Afficher les hashtags populaires dans le champs de saisie" | ||||
|   show-clock-on-header: "Afficher l'horloge à droite sur le coté supérieur" | ||||
|   show-reply-target: "Afficher les réponses" | ||||
|   show-my-renotes: "Afficher mes republications dans le fil" | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-renoted-my-notes: "Afficher mes republications dans les fils" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "Afficher la carte" | ||||
|   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。" | ||||
|   sound: "Son" | ||||
|   enable-sounds: "Activer le son" | ||||
|   enable-sounds-desc: "Jouer un son lorsque vous recevez un message. Ce paramètre est sauvegardé dans le navigateur." | ||||
| @@ -739,7 +780,7 @@ desktop/views/components/settings.vue: | ||||
|   debug-mode: "Activer le mode debug" | ||||
|   debug-mode-desc: "Ce paramètre est stocké dans le navigateur." | ||||
|   experimental: "Activer les fonctionnalités expérimentales" | ||||
|   experimental-desc: "実験的機能を有効にするとMisskeyの動作が不安定になる可能性があります。この設定はブラウザに記憶されます。" | ||||
|   experimental-desc: "L’activation des fonctionnalités expérimentales peuvent rendre le client Misskey instable. Ce paramètre est stocké dans le navigateur." | ||||
|   tools: "Outils" | ||||
|   task-manager: "Gestionnaire de tâches" | ||||
|   third-parties: "Services tiers" | ||||
| @@ -791,7 +832,7 @@ desktop/views/components/settings.profile.vue: | ||||
|   birthday: "Date de naissance" | ||||
|   save: "Mettre à jour le profil" | ||||
|   locked-account: "Protéger votre compte" | ||||
|   is-locked: "フォローを承認制にする" | ||||
|   is-locked: "Demande d’abonnement en attente d’approbation" | ||||
|   other: "Autre" | ||||
|   is-bot: "Ce compte est un Bot" | ||||
|   is-cat: "Ce compte est un Chat" | ||||
| @@ -808,8 +849,13 @@ desktop/views/components/timeline.vue: | ||||
|   local: "Local" | ||||
|   hybrid: "Social" | ||||
|   global: "Global" | ||||
|   mentions: "あなた宛て" | ||||
|   mentions: "Mentions" | ||||
|   messages: "Messages" | ||||
|   list: "Listes" | ||||
|   hashtag: "Hashtag" | ||||
|   add-tag-timeline: "Ajouter un fil de hashtags" | ||||
|   add-list: "Ajouter une nouvelle liste" | ||||
|   list-name: "Nom de la liste" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "Content de vous revoir !" | ||||
|   adjective: "さん" | ||||
| @@ -925,7 +971,7 @@ desktop/views/pages/selectdrive.vue: | ||||
|   cancel: "Annuler" | ||||
|   upload: "Uploader un ou plusieurs fichier(s) depuis votre PC" | ||||
| desktop/views/pages/search.vue: | ||||
|   not-available: "検索機能はインスタンスの設定で無効になっています。" | ||||
|   not-available: "La fonction de recherche est désactivée dans les paramètres de l’instance." | ||||
|   not-found: "Aucun message trouvé pour '{}'" | ||||
| desktop/views/pages/share.vue: | ||||
|   share-with: "Partager avec {}" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "Abonnements" | ||||
|   follow: "Suivre" | ||||
|   request-pending: "En attente d'approbation" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "Demande d'abonnement" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "Abonnez-vous aux utilisateurs" | ||||
| @@ -1134,7 +1181,8 @@ mobile/views/pages/home.vue: | ||||
|   local: "Local" | ||||
|   hybrid: "Social" | ||||
|   global: "Global" | ||||
|   mentions: "あなた宛て" | ||||
|   mentions: "Mentions" | ||||
|   messages: "Messages" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "Pas de message avec un hashtag {} trouvé." | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1175,7 +1223,7 @@ mobile/views/pages/settings/settings.profile.vue: | ||||
|   avatar: "Avatar" | ||||
|   banner: "Bannière" | ||||
|   is-cat: "Ce compte est un Bot" | ||||
|   is-locked: "フォローを承認制にする" | ||||
|   is-locked: "Demande d’abonnement en attente d’approbation" | ||||
|   advanced: "Avancé" | ||||
|   privacy: "Vie privée" | ||||
|   save: "Mettre à jour le profil" | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "Style de notification" | ||||
|   notification-position-bottom: "en bas" | ||||
|   notification-position-top: "en haut" | ||||
|   theme: "Thème" | ||||
|   behavior: "Comportement" | ||||
|   fetch-on-scroll: "Chargement lors du défilement" | ||||
|   note-visibility: "Visibilité de la publication" | ||||
|   | ||||
| @@ -155,8 +155,10 @@ common: | ||||
|     home: "ホーム" | ||||
|     local: "ローカル" | ||||
|     hybrid: "ソーシャル" | ||||
|     hashtag: "ハッシュタグ" | ||||
|     global: "グローバル" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "通知" | ||||
|     list: "リスト" | ||||
|     swap-left: "左に移動" | ||||
| @@ -262,6 +264,41 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "リンクをコピー" | ||||
|   favorite: "お気に入り" | ||||
|   pin: "ピン留め" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "削除" | ||||
|   delete-confirm: "この投稿を削除しますか?" | ||||
|   remote: "投稿元で見る" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop: | ||||
|   banner-crop-title: "バナーとして表示する部分を選択" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "ユーザーの増減" | ||||
|     users-total: "ユーザーの累計" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "{} のフォロワー" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "二段階認証" | ||||
|   other: "その他" | ||||
|   license: "ライセンス" | ||||
|   theme: "テーマ" | ||||
|   behaviour: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。" | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "壁紙を選択" | ||||
|   delete-wallpaper: "壁紙を削除" | ||||
|   dark-mode: "ダークモード" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "円形のアイコンを使用" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用" | ||||
|   post-form-on-timeline: "タイムライン上部に投稿フォームを表示する" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "マップの自動展開" | ||||
|   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。" | ||||
|   sound: "サウンド" | ||||
|   enable-sounds: "サウンドを有効にする" | ||||
|   enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。" | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "リスト" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "おかえりなさい、" | ||||
|   adjective: "さん" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "気になるユーザーをフォロー" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
| @@ -166,8 +166,10 @@ common: | ||||
|     home: "ホーム" | ||||
|     local: "ローカル" | ||||
|     hybrid: "ソーシャル" | ||||
|     hashtag: "ハッシュタグ" | ||||
|     global: "グローバル" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "通知" | ||||
|     list: "リスト" | ||||
|     swap-left: "左に移動" | ||||
| @@ -283,6 +285,42 @@ common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
|  | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
|  | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -326,6 +364,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "リンクをコピー" | ||||
|   favorite: "お気に入り" | ||||
|   pin: "ピン留め" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "削除" | ||||
|   delete-confirm: "この投稿を削除しますか?" | ||||
|   remote: "投稿元で見る" | ||||
| @@ -486,6 +525,7 @@ common/views/pages/follow.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
|  | ||||
| desktop: | ||||
| @@ -529,13 +569,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "ユーザーの増減" | ||||
|     users-total: "ユーザーの累計" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -628,6 +668,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
|  | ||||
| desktop/views/components/followers-window.vue: | ||||
| @@ -759,6 +800,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "二段階認証" | ||||
|   other: "その他" | ||||
|   license: "ライセンス" | ||||
|   theme: "テーマ" | ||||
|  | ||||
|   behaviour: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
| @@ -777,9 +819,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "壁紙を選択" | ||||
|   delete-wallpaper: "壁紙を削除" | ||||
|   dark-mode: "ダークモード" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "円形のアイコンを使用" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用" | ||||
|   post-form-on-timeline: "タイムライン上部に投稿フォームを表示する" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -788,7 +831,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "マップの自動展開" | ||||
|   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。" | ||||
|  | ||||
|   sound: "サウンド" | ||||
|   enable-sounds: "サウンドを有効にする" | ||||
| @@ -915,7 +957,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "リスト" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
|  | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "おかえりなさい、" | ||||
| @@ -1198,6 +1245,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
|  | ||||
| mobile/views/components/friends-maker.vue: | ||||
| @@ -1317,6 +1365,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|  | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" | ||||
| @@ -1408,6 +1457,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -3,20 +3,20 @@ meta: | ||||
|   lang: "한국어" | ||||
|   divider: "" | ||||
| common: | ||||
|   misskey: "A ⭐ of fediverse" | ||||
|   about-title: "A ⭐ of fediverse." | ||||
|   misskey: "연합우주의 ⭐" | ||||
|   about-title: "연합우주의 ⭐." | ||||
|   about: "Misskey를 찾아 주셔서 감사합니다. Misskey은 지구에서 태어난 <b>분산 마이크로 블로그 SNS </b> 입니다. Fediverse (다양한 SNS로 구성되는 우주)에 존재하는 다른 SNS와 상호 연결되어 있습니다. 잠시 도시의 번잡함에서 벗어나 새로운 인터넷에 다이브 해 보지 않겠습니까." | ||||
|   intro: | ||||
|     title: "Misskeyって?" | ||||
|     title: "Misskey란?" | ||||
|     about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。" | ||||
|     features: "特徴" | ||||
|     rich-contents: "投稿" | ||||
|     features: "특징" | ||||
|     rich-contents: "게시" | ||||
|     rich-contents-desc: "自分の考え、話題の出来事、皆と共有したいことについて発信してください。必要であれば、様々な構文を使って投稿を装飾したり、好きな画像、動画などのファイルやアンケートを添付することもできます。" | ||||
|     reaction: "リアクション" | ||||
|     reaction: "반응" | ||||
|     reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクションを付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。" | ||||
|     ui: "インターフェース" | ||||
|     ui: "인터페이스" | ||||
|     ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。" | ||||
|     drive: "ドライブ" | ||||
|     drive: "드라이브" | ||||
|     drive-desc: "以前投稿したことのある画像をまた投稿したくなったことはありませんか?もしくは、アップロードしたファイルをフォルダ分けして整理したくなったことはありませんか?Misskeyの根幹に組み込まれたドライブ機能によってそれらが解決します。ファイルの共有も簡単です。" | ||||
|     outro: "他にもMisskeyにしかない機能はまだまだあるので、ぜひあなた自身の目で確かめてください。Misskeyは分散型SNSなので、このインスタンスが気に入らなければ他のインスタンスを試すこともできます。それでは、GLHF!" | ||||
|   adblock: | ||||
| @@ -71,7 +71,7 @@ common: | ||||
|     friday: "금요일" | ||||
|     saturday: "토요일" | ||||
|   reactions: | ||||
|     like: "いいね" | ||||
|     like: "좋아요" | ||||
|     love: "좋아" | ||||
|     laugh: "크크" | ||||
|     hmm: "음..." | ||||
| @@ -82,14 +82,14 @@ common: | ||||
|     rip: "RIP" | ||||
|     pudding: "Pudding" | ||||
|   note-visibility: | ||||
|     public: "公開" | ||||
|     home: "ホーム" | ||||
|     home-desc: "ホームタイムラインにのみ公開" | ||||
|     followers: "フォロワー" | ||||
|     followers-desc: "自分のフォロワーにのみ公開" | ||||
|     specified: "ダイレクト" | ||||
|     specified-desc: "指定したユーザーにのみ公開" | ||||
|     private: "非公開" | ||||
|     public: "공개" | ||||
|     home: "홈" | ||||
|     home-desc: "홈 타임라인에만 공개" | ||||
|     followers: "팔로워" | ||||
|     followers-desc: "자신의 팔로워에게만 공개" | ||||
|     specified: "다이렉트" | ||||
|     specified-desc: "지정한 사용자에게만 공개" | ||||
|     private: "비공개" | ||||
|   note-placeholders: | ||||
|     a: "지금 어떻게하고있어?" | ||||
|     b: "뭔가 있었습니까?" | ||||
| @@ -107,14 +107,14 @@ common: | ||||
|   i-like-sushi: "나는(푸딩보다 오히려)스시가 좋아" | ||||
|   show-reversi-board-labels: "리버시 보드의 행과 열 레이블을 표시" | ||||
|   use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける" | ||||
|   verified-user: "公式アカウント" | ||||
|   verified-user: "공식 계정" | ||||
|   disable-animated-mfm: "게시물의 문자 애니메이션을 비활성화 할" | ||||
|   always-show-nsfw: "常に閲覧注意のメディアを表示する" | ||||
|   always-mark-nsfw: "常にメディアを閲覧注意として投稿" | ||||
|   always-show-nsfw: "항상 열람주의 미디어를 표시" | ||||
|   always-mark-nsfw: "항상 미디어를 열람주의로 설정하여 게시" | ||||
|   show-full-acct: "ユーザー名のホストを省略しない" | ||||
|   reduce-motion: "UIの動きを減らす" | ||||
|   this-setting-is-this-device-only: "このデバイスのみ" | ||||
|   do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。' | ||||
|   this-setting-is-this-device-only: "이 장치만" | ||||
|   do-not-use-in-production: '이것은 개발 빌드입니다. 프로덕션 환경에서 사용하지 마십시오.' | ||||
|   reversi: | ||||
|     drawn: "무승부" | ||||
|     my-turn: "당신의 차례입니다" | ||||
| @@ -155,17 +155,19 @@ common: | ||||
|     home: "홈" | ||||
|     local: "로컬" | ||||
|     hybrid: "소셜" | ||||
|     hashtag: "해시태그" | ||||
|     global: "글로벌" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "통지" | ||||
|     list: "목록" | ||||
|     swap-left: "左に移動" | ||||
|     swap-right: "右に移動" | ||||
|     swap-up: "上に移動" | ||||
|     swap-down: "下に移動" | ||||
|     remove: "カラムを削除" | ||||
|     add-column: "カラムを追加" | ||||
|     rename: "名前を変更" | ||||
|     swap-left: "왼쪽으로 이동" | ||||
|     swap-right: "오른쪽으로 이동" | ||||
|     swap-up: "위로 이동" | ||||
|     swap-down: "아래로 이동" | ||||
|     remove: "칼럼 제거" | ||||
|     add-column: "칼럼 추가" | ||||
|     rename: "이름 변경" | ||||
|     stack-left: "左に重ねる" | ||||
|     pop-right: "右に出す" | ||||
| auth/views/form.vue: | ||||
| @@ -246,22 +248,57 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
|   checking-network: "ネットワーク接続を確認中" | ||||
|   internet: "インターネット接続" | ||||
|   checking-internet: "インターネット接続を確認中" | ||||
|   server: "サーバー接続" | ||||
|   server: "서버 연결" | ||||
|   checking-server: "サーバー接続を確認中" | ||||
|   finding: "問題を調べています" | ||||
|   no-network: "ネットワークに接続されていません" | ||||
|   no-network-desc: "お使いのPCのネットワーク接続が正常か確認してください。" | ||||
|   no-internet: "インターネットに接続されていません" | ||||
|   no-internet-desc: "ネットワークには接続されていますが、インターネットには接続されていないようです。お使いのPCのインターネット接続が正常か確認してください。" | ||||
|   no-server: "Misskeyのサーバーに接続できません" | ||||
|   no-server: "Misskey 서버에 연결할 수 없습니다." | ||||
|   no-server-desc: "お使いのPCのインターネット接続は正常ですが、Misskeyのサーバーには接続できませんでした。サーバーがダウンまたはメンテナンスしている可能性があるので、しばらくしてから再度御アクセスください。" | ||||
|   success: "Misskeyのサーバーに接続できました" | ||||
|   success-desc: "正常に接続できるようです。ページを再度読み込みしてください。" | ||||
|   flush: "キャッシュの削除" | ||||
|   set-version: "バージョン指定" | ||||
|   flush: "캐시 삭제" | ||||
|   set-version: "버전 지정" | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
|   sensitive: "열람주의" | ||||
|   click-to-show: "클릭하여 표시" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "밝은 테마" | ||||
|   dark-themes: "어두운 테마" | ||||
|   install-a-theme: "테마 설치" | ||||
|   theme-code: "테마 코드" | ||||
|   install: "설치" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "테마 만들기" | ||||
|   save-created-theme: "테마 저장" | ||||
|   primary-color: "기본 색" | ||||
|   secondary-color: "보조 색" | ||||
|   text-color: "글자 색상" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "테마명" | ||||
|   preview-created-theme: "미리보기" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -271,7 +308,7 @@ common/views/components/messaging.vue: | ||||
|   no-history: "履歴はありません" | ||||
| common/views/components/messaging-room.vue: | ||||
|   empty: "このユーザーと話したことはありません" | ||||
|   more: "もっと読む" | ||||
|   more: "더 보기" | ||||
|   no-history: "これより過去の履歴はありません" | ||||
|   resize-form: "ドラッグしてフォームの広さを調整" | ||||
|   new-message: "新しいメッセージがあります" | ||||
| @@ -286,19 +323,20 @@ common/views/components/messaging-room.message.vue: | ||||
|   is-read: "읽음" | ||||
|   deleted: "このメッセージは削除されました" | ||||
| common/views/components/nav.vue: | ||||
|   about: "Misskeyについて" | ||||
|   stats: "統計" | ||||
|   about: "Misskey에 대하여" | ||||
|   stats: "통계" | ||||
|   status: "ステータス" | ||||
|   wiki: "Wiki" | ||||
|   donors: "ドナー" | ||||
|   repository: "リポジトリ" | ||||
|   develop: "開発者" | ||||
|   feedback: "フィードバック" | ||||
|   donors: "기증자" | ||||
|   repository: "저장소" | ||||
|   develop: "개발자" | ||||
|   feedback: "피드백" | ||||
| common/views/components/note-menu.vue: | ||||
|   detail: "詳細" | ||||
|   copy-link: "リンクをコピー" | ||||
|   copy-link: "링크 복사" | ||||
|   favorite: "お気に入り" | ||||
|   pin: "ピン留め" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "削除" | ||||
|   delete-confirm: "この投稿を削除しますか?" | ||||
|   remote: "投稿元で見る" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop: | ||||
|   banner-crop-title: "バナーとして表示する部分を選択" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "ユーザーの増減" | ||||
|     users-total: "ユーザーの累計" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "{} のフォロワー" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "二段階認証" | ||||
|   other: "その他" | ||||
|   license: "ライセンス" | ||||
|   theme: "テーマ" | ||||
|   behaviour: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。" | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "壁紙を選択" | ||||
|   delete-wallpaper: "壁紙を削除" | ||||
|   dark-mode: "ダークモード" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "円形のアイコンを使用" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用" | ||||
|   post-form-on-timeline: "タイムライン上部に投稿フォームを表示する" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "マップの自動展開" | ||||
|   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。" | ||||
|   sound: "サウンド" | ||||
|   enable-sounds: "サウンドを有効にする" | ||||
|   enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。" | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "リスト" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "おかえりなさい、" | ||||
|   adjective: "さん" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "気になるユーザーをフォロー" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
| @@ -155,8 +155,10 @@ common: | ||||
|     home: "ホーム" | ||||
|     local: "ローカル" | ||||
|     hybrid: "ソーシャル" | ||||
|     hashtag: "ハッシュタグ" | ||||
|     global: "グローバル" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "通知" | ||||
|     list: "リスト" | ||||
|     swap-left: "左に移動" | ||||
| @@ -262,6 +264,41 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "リンクをコピー" | ||||
|   favorite: "Deze notitie toevoegen aan favorieten" | ||||
|   pin: "Vastmaken aan profielpagina" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "削除" | ||||
|   delete-confirm: "この投稿を削除しますか?" | ||||
|   remote: "Origineel tonen" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop: | ||||
|   banner-crop-title: "バナーとして表示する部分を選択" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "ユーザーの増減" | ||||
|     users-total: "ユーザーの累計" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "Volgen" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "Volgers van {}" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "Authenticatie in twee stappen" | ||||
|   other: "Overig" | ||||
|   license: "Licentie" | ||||
|   theme: "テーマ" | ||||
|   behaviour: "Gedrag" | ||||
|   fetch-on-scroll: "Ophalen bij scrollen" | ||||
|   fetch-on-scroll-desc: "Als je omlaag scrolt, wordt de rest van de inhoud automatisch opgehaald." | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "壁紙を選択" | ||||
|   delete-wallpaper: "壁紙を削除" | ||||
|   dark-mode: "Donkere modus" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "Ronde pictogrammen gebruiken" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "Kleurverloop gebruiken op vensterkoppen" | ||||
|   post-form-on-timeline: "Berichtformulier boven de tijdlijn tonen" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "Mijn gerenote bericht tonen op de tijdlijn" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "Kaart tonen" | ||||
|   show-maps-desc: "Kaart van bijgevoegde locatie tonen." | ||||
|   sound: "Geluid" | ||||
|   enable-sounds: "Geluid inschakelen" | ||||
|   enable-sounds-desc: "Een geluid afspelen bij het ontvangen van een bericht. Deze instelling wordt opgeslagen in je browser." | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "Algemeen" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "Lijsten" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "おかえりなさい、" | ||||
|   adjective: "さん" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "Volgen" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "気になるユーザーをフォロー" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "Gedrag" | ||||
|   fetch-on-scroll: "Ophalen bij scrollen" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -155,8 +155,10 @@ common: | ||||
|     home: "Strona główna" | ||||
|     local: "Lokalne" | ||||
|     hybrid: "Społeczność" | ||||
|     hashtag: "ハッシュタグ" | ||||
|     global: "Globalne" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "Powiadomienia" | ||||
|     list: "Listy" | ||||
|     swap-left: "Przesuń w lewo" | ||||
| @@ -262,6 +264,41 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "リンクをコピー" | ||||
|   favorite: "Dodaj do ulubionych" | ||||
|   pin: "Przypnij do profilu" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "Usuń" | ||||
|   delete-confirm: "Czy na pewno chcesz usunąć ten wpis?" | ||||
|   remote: "Pokaż oryginał" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "Śledzisz" | ||||
|   follow: "Śledź" | ||||
|   request-pending: "Oczekiwanie na pozwolenie" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "Poproś o śledzenie" | ||||
| desktop: | ||||
|   banner-crop-title: "バナーとして表示する部分を選択" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "ユーザーの増減" | ||||
|     users-total: "ユーザーの累計" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "Śledzisz" | ||||
|   follow: "Śledź" | ||||
|   request-pending: "Oczekiwanie na pozwolenie" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "Poproś o śledzenie" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "Śledzący" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "Uwierzytelnianie dwuetapowe" | ||||
|   other: "Inne" | ||||
|   license: "Licencja" | ||||
|   theme: "テーマ" | ||||
|   behaviour: "Zachowanie" | ||||
|   fetch-on-scroll: "Automatycznie ładuj po przeciągnięciu w dół" | ||||
|   fetch-on-scroll-desc: "Po przewinięciu na dół strony automatycznie zostaną załadowane nowe treści." | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "Wybierz tło" | ||||
|   delete-wallpaper: "Usuń tło" | ||||
|   dark-mode: "Tryb ciemny" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "Używaj okrągłych ikon" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "Używaj gradientów na pasku tytułu okna" | ||||
|   post-form-on-timeline: "Wyświetlaj formularz tworzenia wpisu w górnej części osi czasu" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "Automatycznie pokazuj mapę" | ||||
|   show-maps-desc: "Mapa będzie automatycznie rozwijana dla wpisów zawierających informacje o lokalizacji." | ||||
|   sound: "Dźwięk" | ||||
|   enable-sounds: "Włącz dźwięk" | ||||
|   enable-sounds-desc: "Odtwarzaj dźwięk przy wstawianiu wpisów, wysyłaniu lub otrzymywaniu wiadomości. Opcja ta jest zapamiętywana przez przeglądarkę." | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "Społeczność" | ||||
|   global: "Globalne" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "Listy" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "Witaj ponownie," | ||||
|   adjective: "さん" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "Śledzisz" | ||||
|   follow: "Śledź" | ||||
|   request-pending: "Oczekiwanie na pozwolenie" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "Poproś o śledzenie" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "Zacznij śledzić ludzi takich jak Ty" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "Społeczność" | ||||
|   global: "Globalne" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "Nie znaleziono wpisów zawierających „{}”." | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "Zachowanie" | ||||
|   fetch-on-scroll: "Automatycznie ładuj po przeciągnięciu w dół" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
| @@ -155,8 +155,10 @@ common: | ||||
|     home: "Início" | ||||
|     local: "Local" | ||||
|     hybrid: "Social" | ||||
|     hashtag: "ハッシュタグ" | ||||
|     global: "Global" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "Notificações" | ||||
|     list: "Listas" | ||||
|     swap-left: "Mover para a esquerda" | ||||
| @@ -262,6 +264,41 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "リンクをコピー" | ||||
|   favorite: "お気に入り" | ||||
|   pin: "ピン留め" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "削除" | ||||
|   delete-confirm: "この投稿を削除しますか?" | ||||
|   remote: "投稿元で見る" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop: | ||||
|   banner-crop-title: "バナーとして表示する部分を選択" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "ユーザーの増減" | ||||
|     users-total: "ユーザーの累計" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "{} のフォロワー" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "二段階認証" | ||||
|   other: "その他" | ||||
|   license: "ライセンス" | ||||
|   theme: "テーマ" | ||||
|   behaviour: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。" | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "壁紙を選択" | ||||
|   delete-wallpaper: "壁紙を削除" | ||||
|   dark-mode: "ダークモード" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "円形のアイコンを使用" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用" | ||||
|   post-form-on-timeline: "タイムライン上部に投稿フォームを表示する" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "マップの自動展開" | ||||
|   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。" | ||||
|   sound: "サウンド" | ||||
|   enable-sounds: "サウンドを有効にする" | ||||
|   enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。" | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "リスト" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "おかえりなさい、" | ||||
|   adjective: "さん" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "気になるユーザーをフォロー" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
| @@ -155,8 +155,10 @@ common: | ||||
|     home: "ホーム" | ||||
|     local: "ローカル" | ||||
|     hybrid: "ソーシャル" | ||||
|     hashtag: "ハッシュタグ" | ||||
|     global: "グローバル" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "通知" | ||||
|     list: "リスト" | ||||
|     swap-left: "左に移動" | ||||
| @@ -262,6 +264,41 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "リンクをコピー" | ||||
|   favorite: "お気に入り" | ||||
|   pin: "ピン留め" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "削除" | ||||
|   delete-confirm: "この投稿を削除しますか?" | ||||
|   remote: "投稿元で見る" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop: | ||||
|   banner-crop-title: "バナーとして表示する部分を選択" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "ユーザーの増減" | ||||
|     users-total: "ユーザーの累計" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "{} のフォロワー" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "二段階認証" | ||||
|   other: "その他" | ||||
|   license: "ライセンス" | ||||
|   theme: "テーマ" | ||||
|   behaviour: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。" | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "壁紙を選択" | ||||
|   delete-wallpaper: "壁紙を削除" | ||||
|   dark-mode: "ダークモード" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "円形のアイコンを使用" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用" | ||||
|   post-form-on-timeline: "タイムライン上部に投稿フォームを表示する" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "マップの自動展開" | ||||
|   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。" | ||||
|   sound: "サウンド" | ||||
|   enable-sounds: "サウンドを有効にする" | ||||
|   enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。" | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "リスト" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "おかえりなさい、" | ||||
|   adjective: "さん" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "気になるユーザーをフォロー" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
| @@ -155,8 +155,10 @@ common: | ||||
|     home: "ホーム" | ||||
|     local: "ローカル" | ||||
|     hybrid: "ソーシャル" | ||||
|     hashtag: "ハッシュタグ" | ||||
|     global: "グローバル" | ||||
|     mentions: "あなた宛て" | ||||
|     direct: "ダイレクト投稿" | ||||
|     notifications: "通知" | ||||
|     list: "リスト" | ||||
|     swap-left: "左に移動" | ||||
| @@ -262,6 +264,41 @@ common/views/components/connect-failed.troubleshooter.vue: | ||||
| common/views/components/media-banner.vue: | ||||
|   sensitive: "閲覧注意" | ||||
|   click-to-show: "クリックして表示" | ||||
| common/views/components/theme.vue: | ||||
|   light-theme: "非ダークモード時に使用するテーマ" | ||||
|   dark-theme: "ダークモード時に使用するテーマ" | ||||
|   light-themes: "明るいテーマ" | ||||
|   dark-themes: "暗いテーマ" | ||||
|   install-a-theme: "テーマのインストール" | ||||
|   theme-code: "テーマコード" | ||||
|   install: "インストール" | ||||
|   installed: "「{}」をインストールしました" | ||||
|   create-a-theme: "テーマの作成" | ||||
|   save-created-theme: "テーマを保存" | ||||
|   primary-color: "プライマリ カラー" | ||||
|   secondary-color: "セカンダリ カラー" | ||||
|   text-color: "文字色" | ||||
|   base-theme: "ベーステーマ" | ||||
|   base-theme-light: "Light" | ||||
|   base-theme-dark: "Dark" | ||||
|   theme-name: "テーマ名" | ||||
|   preview-created-theme: "プレビュー" | ||||
|   invalid-theme: "テーマが正しくありません。" | ||||
|   already-installed: "既にそのテーマはインストールされています。" | ||||
|   saved: "保存しました" | ||||
|   manage-themes: "テーマの管理" | ||||
|   builtin-themes: "標準テーマ" | ||||
|   my-themes: "マイテーマ" | ||||
|   installed-themes: "インストールされたテーマ" | ||||
|   select-theme: "テーマを選択してください" | ||||
|   uninstall: "アンインストール" | ||||
|   uninstalled: "「{}」をアンインストールしました" | ||||
|   author: "作者" | ||||
|   desc: "説明" | ||||
|   export: "エクスポート" | ||||
|   import: "インポート" | ||||
|   import-by-code: "またはコードをペースト" | ||||
|   theme-name-required: "テーマ名は必須です。" | ||||
| common/views/components/cw-button.vue: | ||||
|   hide: "隠す" | ||||
|   show: "もっと見る" | ||||
| @@ -299,6 +336,7 @@ common/views/components/note-menu.vue: | ||||
|   copy-link: "リンクをコピー" | ||||
|   favorite: "お気に入り" | ||||
|   pin: "ピン留め" | ||||
|   unpin: "ピン留め解除" | ||||
|   delete: "削除" | ||||
|   delete-confirm: "この投稿を削除しますか?" | ||||
|   remote: "投稿元で見る" | ||||
| @@ -437,6 +475,7 @@ common/views/pages/follow.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop: | ||||
|   banner-crop-title: "バナーとして表示する部分を選択" | ||||
| @@ -475,13 +514,13 @@ desktop/views/components/charts.vue: | ||||
|     notes: "投稿の増減 (統合)" | ||||
|     local-notes: "投稿の増減 (ローカル)" | ||||
|     remote-notes: "投稿の増減 (リモート)" | ||||
|     notes-total: "投稿の累計" | ||||
|     notes-total: "投稿の積算" | ||||
|     users: "ユーザーの増減" | ||||
|     users-total: "ユーザーの累計" | ||||
|     users-total: "ユーザーの積算" | ||||
|     drive: "ドライブ使用量の増減" | ||||
|     drive-total: "ドライブ使用量の累計" | ||||
|     drive-total: "ドライブ使用量の積算" | ||||
|     drive-files: "ドライブのファイル数の増減" | ||||
|     drive-files-total: "ドライブのファイル数の累計" | ||||
|     drive-files-total: "ドライブのファイル数の積算" | ||||
|     network-requests: "リクエスト" | ||||
|     network-time: "応答時間" | ||||
|     network-usage: "通信量" | ||||
| @@ -563,6 +602,7 @@ desktop/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| desktop/views/components/followers-window.vue: | ||||
|   followers: "{} のフォロワー" | ||||
| @@ -673,6 +713,7 @@ desktop/views/components/settings.vue: | ||||
|   2fa: "二段階認証" | ||||
|   other: "その他" | ||||
|   license: "ライセンス" | ||||
|   theme: "テーマ" | ||||
|   behaviour: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   fetch-on-scroll-desc: "ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。" | ||||
| @@ -689,9 +730,10 @@ desktop/views/components/settings.vue: | ||||
|   choose-wallpaper: "壁紙を選択" | ||||
|   delete-wallpaper: "壁紙を削除" | ||||
|   dark-mode: "ダークモード" | ||||
|   use-shadow: "UIに影を使用" | ||||
|   rounded-corners: "UIの角を丸める" | ||||
|   circle-icons: "円形のアイコンを使用" | ||||
|   contrasted-acct: "ユーザー名にコントラストを付ける" | ||||
|   gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用" | ||||
|   post-form-on-timeline: "タイムライン上部に投稿フォームを表示する" | ||||
|   suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する" | ||||
|   show-clock-on-header: "右上に時計を表示する" | ||||
| @@ -700,7 +742,6 @@ desktop/views/components/settings.vue: | ||||
|   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する" | ||||
|   show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する" | ||||
|   show-maps: "マップの自動展開" | ||||
|   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。" | ||||
|   sound: "サウンド" | ||||
|   enable-sounds: "サウンドを有効にする" | ||||
|   enable-sounds-desc: "投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。" | ||||
| @@ -809,7 +850,12 @@ desktop/views/components/timeline.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
|   list: "リスト" | ||||
|   hashtag: "ハッシュタグ" | ||||
|   add-tag-timeline: "ハッシュタグを追加" | ||||
|   add-list: "リストを追加" | ||||
|   list-name: "リスト名" | ||||
| desktop/views/components/ui.header.vue: | ||||
|   welcome-back: "おかえりなさい、" | ||||
|   adjective: "さん" | ||||
| @@ -1037,6 +1083,7 @@ mobile/views/components/follow-button.vue: | ||||
|   following: "フォロー中" | ||||
|   follow: "フォロー" | ||||
|   request-pending: "フォロー許可待ち" | ||||
|   follow-processing: "フォロー処理中" | ||||
|   follow-request: "フォロー申請" | ||||
| mobile/views/components/friends-maker.vue: | ||||
|   title: "気になるユーザーをフォロー" | ||||
| @@ -1135,6 +1182,7 @@ mobile/views/pages/home.vue: | ||||
|   hybrid: "ソーシャル" | ||||
|   global: "グローバル" | ||||
|   mentions: "あなた宛て" | ||||
|   messages: "メッセージ" | ||||
| mobile/views/pages/tag.vue: | ||||
|   no-posts-found: "ハッシュタグ「{}」が付けられた投稿は見つかりませんでした。" | ||||
| mobile/views/pages/welcome.vue: | ||||
| @@ -1211,6 +1259,7 @@ mobile/views/pages/settings.vue: | ||||
|   notification-position: "通知の表示" | ||||
|   notification-position-bottom: "下" | ||||
|   notification-position-top: "上" | ||||
|   theme: "テーマ" | ||||
|   behavior: "動作" | ||||
|   fetch-on-scroll: "スクロールで自動読み込み" | ||||
|   note-visibility: "投稿の公開範囲" | ||||
|   | ||||
							
								
								
									
										68
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
| { | ||||
| 	"name": "misskey", | ||||
| 	"author": "syuilo <i@syuilo.com>", | ||||
| 	"version": "8.44.0", | ||||
| 	"clientVersion": "1.0.9813", | ||||
| 	"version": "10.5.0", | ||||
| 	"clientVersion": "1.0.10405", | ||||
| 	"codename": "nighthike", | ||||
| 	"main": "./built/index.js", | ||||
| 	"private": true, | ||||
| @@ -27,9 +27,9 @@ | ||||
| 		"@koa/cors": "2.2.2", | ||||
| 		"@prezzemolo/rap": "0.1.2", | ||||
| 		"@prezzemolo/zip": "0.0.3", | ||||
| 		"@types/bcryptjs": "2.4.1", | ||||
| 		"@types/bcryptjs": "2.4.2", | ||||
| 		"@types/dateformat": "1.0.1", | ||||
| 		"@types/debug": "0.0.30", | ||||
| 		"@types/debug": "0.0.31", | ||||
| 		"@types/deep-equal": "1.0.1", | ||||
| 		"@types/double-ended-queue": "2.1.0", | ||||
| 		"@types/elasticsearch": "5.0.26", | ||||
| @@ -51,21 +51,21 @@ | ||||
| 		"@types/koa-logger": "3.1.0", | ||||
| 		"@types/koa-mount": "3.0.1", | ||||
| 		"@types/koa-multer": "1.0.0", | ||||
| 		"@types/koa-router": "7.0.31", | ||||
| 		"@types/koa-router": "7.0.32", | ||||
| 		"@types/koa-send": "4.1.1", | ||||
| 		"@types/koa-views": "2.0.3", | ||||
| 		"@types/koa__cors": "2.2.3", | ||||
| 		"@types/minio": "7.0.0", | ||||
| 		"@types/mkdirp": "0.5.2", | ||||
| 		"@types/mocha": "5.2.3", | ||||
| 		"@types/mongodb": "3.1.7", | ||||
| 		"@types/mongodb": "3.1.11", | ||||
| 		"@types/ms": "0.7.30", | ||||
| 		"@types/node": "10.9.4", | ||||
| 		"@types/node": "10.11.5", | ||||
| 		"@types/portscanner": "2.1.0", | ||||
| 		"@types/pug": "2.0.4", | ||||
| 		"@types/qrcode": "1.2.0", | ||||
| 		"@types/qrcode": "1.3.0", | ||||
| 		"@types/ratelimiter": "2.1.28", | ||||
| 		"@types/redis": "2.8.6", | ||||
| 		"@types/redis": "2.8.7", | ||||
| 		"@types/request": "2.47.1", | ||||
| 		"@types/request-promise-native": "1.0.15", | ||||
| 		"@types/rimraf": "2.0.2", | ||||
| @@ -75,13 +75,15 @@ | ||||
| 		"@types/single-line-log": "1.1.0", | ||||
| 		"@types/speakeasy": "2.0.2", | ||||
| 		"@types/systeminformation": "3.23.0", | ||||
| 		"@types/tinycolor2": "1.4.1", | ||||
| 		"@types/tmp": "0.0.33", | ||||
| 		"@types/uuid": "3.4.4", | ||||
| 		"@types/webpack": "4.4.11", | ||||
| 		"@types/webpack": "4.4.15", | ||||
| 		"@types/webpack-stream": "3.2.10", | ||||
| 		"@types/websocket": "0.0.40", | ||||
| 		"@types/ws": "6.0.1", | ||||
| 		"animejs": "2.2.0", | ||||
| 		"autobind-decorator": "2.1.0", | ||||
| 		"autosize": "4.0.2", | ||||
| 		"autwh": "0.1.0", | ||||
| 		"bcryptjs": "2.4.3", | ||||
| @@ -90,14 +92,14 @@ | ||||
| 		"cafy": "11.3.0", | ||||
| 		"chalk": "2.4.1", | ||||
| 		"chart.js": "2.7.2", | ||||
| 		"commander": "2.17.1", | ||||
| 		"commander": "2.19.0", | ||||
| 		"crc-32": "1.2.0", | ||||
| 		"css-loader": "1.0.0", | ||||
| 		"dateformat": "3.0.3", | ||||
| 		"debug": "4.0.1", | ||||
| 		"debug": "4.1.0", | ||||
| 		"deep-equal": "1.0.1", | ||||
| 		"deepcopy": "0.6.3", | ||||
| 		"diskusage": "0.2.4", | ||||
| 		"diskusage": "0.2.5", | ||||
| 		"dompurify": "1.0.5", | ||||
| 		"double-ended-queue": "2.1.0-0", | ||||
| 		"elasticsearch": "15.1.1", | ||||
| @@ -107,12 +109,12 @@ | ||||
| 		"eslint-plugin-vue": "4.7.1", | ||||
| 		"eventemitter3": "3.1.0", | ||||
| 		"exif-js": "2.3.0", | ||||
| 		"file-loader": "1.1.11", | ||||
| 		"file-type": "9.0.0", | ||||
| 		"file-loader": "2.0.0", | ||||
| 		"file-type": "10.0.0", | ||||
| 		"fuckadblock": "3.2.1", | ||||
| 		"gulp": "3.9.1", | ||||
| 		"gulp-cssnano": "2.1.3", | ||||
| 		"gulp-htmlmin": "4.0.0", | ||||
| 		"gulp-htmlmin": "5.0.1", | ||||
| 		"gulp-imagemin": "4.1.0", | ||||
| 		"gulp-mocha": "6.0.0", | ||||
| 		"gulp-pug": "4.0.1", | ||||
| @@ -132,14 +134,16 @@ | ||||
| 		"is-root": "2.0.0", | ||||
| 		"is-url": "1.2.4", | ||||
| 		"js-yaml": "3.12.0", | ||||
| 		"jsdom": "11.12.0", | ||||
| 		"jsdom": "12.2.0", | ||||
| 		"json5": "2.1.0", | ||||
| 		"json5-loader": "1.0.1", | ||||
| 		"koa": "2.5.1", | ||||
| 		"koa-bodyparser": "4.2.1", | ||||
| 		"koa-compress": "3.0.0", | ||||
| 		"koa-favicon": "2.0.1", | ||||
| 		"koa-json-body": "5.3.0", | ||||
| 		"koa-logger": "3.2.0", | ||||
| 		"koa-mount": "3.0.0", | ||||
| 		"koa-mount": "4.0.0", | ||||
| 		"koa-multer": "1.0.2", | ||||
| 		"koa-router": "7.4.0", | ||||
| 		"koa-send": "5.0.0", | ||||
| @@ -156,7 +160,7 @@ | ||||
| 		"mongodb": "3.1.1", | ||||
| 		"monk": "6.0.6", | ||||
| 		"ms": "2.1.1", | ||||
| 		"nan": "2.11.0", | ||||
| 		"nan": "2.11.1", | ||||
| 		"nested-property": "0.0.7", | ||||
| 		"nprogress": "0.2.0", | ||||
| 		"object-assign-deep": "0.4.0", | ||||
| @@ -168,10 +172,10 @@ | ||||
| 		"promise-sequential": "1.1.1", | ||||
| 		"pug": "2.0.3", | ||||
| 		"punycode": "2.1.1", | ||||
| 		"qrcode": "1.2.2", | ||||
| 		"qrcode": "1.3.0", | ||||
| 		"ratelimiter": "3.2.0", | ||||
| 		"recaptcha-promise": "0.1.3", | ||||
| 		"reconnecting-websocket": "3.2.2", | ||||
| 		"reconnecting-websocket": "4.1.5", | ||||
| 		"redis": "2.8.0", | ||||
| 		"request": "2.88.0", | ||||
| 		"request-promise-native": "1.0.5", | ||||
| @@ -181,48 +185,52 @@ | ||||
| 		"s-age": "1.1.2", | ||||
| 		"sass-loader": "7.1.0", | ||||
| 		"seedrandom": "2.4.4", | ||||
| 		"sharp": "0.20.7", | ||||
| 		"sharp": "0.21.0", | ||||
| 		"showdown": "1.8.6", | ||||
| 		"showdown-highlightjs-extension": "0.1.2", | ||||
| 		"single-line-log": "1.1.2", | ||||
| 		"speakeasy": "2.0.0", | ||||
| 		"stringz": "1.0.0", | ||||
| 		"style-loader": "0.23.0", | ||||
| 		"style-loader": "0.23.1", | ||||
| 		"stylus": "0.54.5", | ||||
| 		"stylus-loader": "3.0.2", | ||||
| 		"summaly": "2.2.0", | ||||
| 		"systeminformation": "3.45.6", | ||||
| 		"systeminformation": "3.45.7", | ||||
| 		"syuilo-password-strength": "0.0.1", | ||||
| 		"textarea-caret": "3.1.0", | ||||
| 		"tinycolor2": "1.4.1", | ||||
| 		"tmp": "0.0.33", | ||||
| 		"ts-loader": "4.4.1", | ||||
| 		"ts-node": "7.0.1", | ||||
| 		"tslint": "5.10.0", | ||||
| 		"typescript": "2.9.2", | ||||
| 		"typescript-eslint-parser": "18.0.0", | ||||
| 		"typescript-eslint-parser": "20.0.0", | ||||
| 		"uglify-es": "3.3.9", | ||||
| 		"url-loader": "1.1.1", | ||||
| 		"uuid": "3.3.2", | ||||
| 		"v-animate-css": "0.0.2", | ||||
| 		"vue": "2.5.17", | ||||
| 		"vue-chartjs": "3.4.0", | ||||
| 		"vue-color": "2.6.0", | ||||
| 		"vue-cropperjs": "2.2.2", | ||||
| 		"vue-js-modal": "1.3.26", | ||||
| 		"vue-json-tree-view": "2.1.4", | ||||
| 		"vue-loader": "15.4.2", | ||||
| 		"vue-router": "3.0.1", | ||||
| 		"vue-style-loader": "4.1.2", | ||||
| 		"vue-svg-inline-loader": "1.2.0", | ||||
| 		"vue-sweetalert2": "1.5.5", | ||||
| 		"vue-template-compiler": "2.5.17", | ||||
| 		"vuedraggable": "2.16.0", | ||||
| 		"vuewordcloud": "18.7.11", | ||||
| 		"vuex": "3.0.1", | ||||
| 		"vuex-persistedstate": "2.5.4", | ||||
| 		"web-push": "3.3.2", | ||||
| 		"web-push": "3.3.3", | ||||
| 		"webfinger.js": "2.6.6", | ||||
| 		"webpack": "4.19.0", | ||||
| 		"webpack-cli": "3.1.0", | ||||
| 		"websocket": "1.0.26", | ||||
| 		"ws": "6.0.0", | ||||
| 		"webpack": "4.20.2", | ||||
| 		"webpack-cli": "3.1.2", | ||||
| 		"websocket": "1.0.28", | ||||
| 		"ws": "6.1.0", | ||||
| 		"xev": "2.0.1" | ||||
| 	}, | ||||
| 	"greenkeeper": { | ||||
|   | ||||
| @@ -27,7 +27,7 @@ body | ||||
| 	z-index 65536 | ||||
|  | ||||
| 	.bar | ||||
| 		background $theme-color | ||||
| 		background var(--primary) | ||||
|  | ||||
| 		position fixed | ||||
| 		z-index 65537 | ||||
| @@ -44,7 +44,7 @@ body | ||||
| 		right 0px | ||||
| 		width 100px | ||||
| 		height 100% | ||||
| 		box-shadow 0 0 10px $theme-color, 0 0 5px $theme-color | ||||
| 		box-shadow 0 0 10px var(--primary), 0 0 5px var(--primary) | ||||
| 		opacity 1 | ||||
|  | ||||
| 		transform rotate(3deg) translate(0px, -4px) | ||||
| @@ -64,8 +64,8 @@ body | ||||
| 		box-sizing border-box | ||||
|  | ||||
| 		border solid 2px transparent | ||||
| 		border-top-color $theme-color | ||||
| 		border-left-color $theme-color | ||||
| 		border-top-color var(--primary) | ||||
| 		border-left-color var(--primary) | ||||
| 		border-radius 50% | ||||
|  | ||||
| 		animation progress-spinner 400ms linear infinite | ||||
| @@ -130,3 +130,16 @@ pre | ||||
|  | ||||
| [data-fa] | ||||
| 	display inline-block | ||||
|  | ||||
| .swal2-popup | ||||
| 	background var(--face) !important | ||||
|  | ||||
| .swal-icon-only | ||||
| 	width 180px !important | ||||
|  | ||||
| 	> .swal2-header | ||||
| 		> .swal2-icon | ||||
| 			margin 1.25em auto 1.875em | ||||
|  | ||||
| 		> .swal2-title | ||||
| 			display none | ||||
|   | ||||
| @@ -1,3 +1,32 @@ | ||||
| <template> | ||||
| <router-view id="app"></router-view> | ||||
| <router-view id="app" v-hotkey.global="keymap"></router-view> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import { url, lang } from './config'; | ||||
|  | ||||
| export default Vue.extend({ | ||||
| 	computed: { | ||||
| 		keymap(): any { | ||||
| 			return { | ||||
| 				'h|slash': this.help, | ||||
| 				'd': this.dark | ||||
| 			}; | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		help() { | ||||
| 			window.open(`${url}/docs/${lang}/keyboard-shortcut`, '_blank'); | ||||
| 		}, | ||||
|  | ||||
| 		dark() { | ||||
| 			this.$store.commit('device/set', { | ||||
| 				key: 'darkmode', | ||||
| 				value: !this.$store.state.device.darkmode | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|   | ||||
| @@ -34,9 +34,6 @@ html | ||||
| 		//- FontAwesome style | ||||
| 		style #{facss} | ||||
|  | ||||
| 		//- highlight.js style | ||||
| 		style #{hljscss} | ||||
|  | ||||
| 	body | ||||
| 		noscript: p | ||||
| 			| JavaScriptを有効にしてください | ||||
|   | ||||
| @@ -20,6 +20,15 @@ | ||||
|  | ||||
| 	const langs = LANGS; | ||||
|  | ||||
| 	//#region Apply theme | ||||
| 	const theme = localStorage.getItem('theme'); | ||||
| 	if (theme) { | ||||
| 		Object.entries(JSON.parse(theme)).forEach(([k, v]) => { | ||||
| 			document.documentElement.style.setProperty(`--${k}`, v.toString()); | ||||
| 		}); | ||||
| 	} | ||||
| 	//#endregion | ||||
|  | ||||
| 	//#region Load settings | ||||
| 	let settings = null; | ||||
| 	const vuex = localStorage.getItem('vuex'); | ||||
| @@ -84,13 +93,6 @@ | ||||
| 		app = isMobile ? 'mobile' : 'desktop'; | ||||
| 	} | ||||
|  | ||||
| 	// Dark/Light | ||||
| 	if (settings) { | ||||
| 		if (settings.device.darkmode) { | ||||
| 			document.documentElement.setAttribute('data-darkmode', 'true'); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Script version | ||||
| 	const ver = localStorage.getItem('v') || VERSION; | ||||
|  | ||||
|   | ||||
							
								
								
									
										110
									
								
								src/client/app/common/hotkey.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/client/app/common/hotkey.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| import keyCode from './keycode'; | ||||
| import { concat } from '../../../prelude/array'; | ||||
|  | ||||
| type pattern = { | ||||
| 	which: string[]; | ||||
| 	ctrl?: boolean; | ||||
| 	shift?: boolean; | ||||
| 	alt?: boolean; | ||||
| }; | ||||
|  | ||||
| type action = { | ||||
| 	patterns: pattern[]; | ||||
|  | ||||
| 	callback: Function; | ||||
| }; | ||||
|  | ||||
| const getKeyMap = keymap => Object.entries(keymap).map(([patterns, callback]): action => { | ||||
| 	const result = { | ||||
| 		patterns: [], | ||||
| 		callback: callback | ||||
| 	} as action; | ||||
|  | ||||
| 	result.patterns = patterns.split('|').map(part => { | ||||
| 		const pattern = { | ||||
| 			which: [], | ||||
| 			ctrl: false, | ||||
| 			alt: false, | ||||
| 			shift: false | ||||
| 		} as pattern; | ||||
|  | ||||
| 		part.trim().split('+').forEach(key => { | ||||
| 			key = key.trim().toLowerCase(); | ||||
| 			switch (key) { | ||||
| 				case 'ctrl': pattern.ctrl = true; break; | ||||
| 				case 'alt': pattern.alt = true; break; | ||||
| 				case 'shift': pattern.shift = true; break; | ||||
| 				default: pattern.which = keyCode(key).map(k => k.toLowerCase()); | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		return pattern; | ||||
| 	}); | ||||
|  | ||||
| 	return result; | ||||
| }); | ||||
|  | ||||
| const ignoreElemens = ['input', 'textarea']; | ||||
|  | ||||
| export default { | ||||
| 	install(Vue) { | ||||
| 		Vue.directive('hotkey', { | ||||
| 			bind(el, binding) { | ||||
| 				el._hotkey_global = binding.modifiers.global === true; | ||||
|  | ||||
| 				const actions = getKeyMap(binding.value); | ||||
|  | ||||
| 				// flatten | ||||
| 				const reservedKeys = concat(concat(actions.map(a => a.patterns.map(p => p.which)))); | ||||
|  | ||||
| 				el.dataset.reservedKeys = reservedKeys.map(key => `'${key}'`).join(' '); | ||||
|  | ||||
| 				el._keyHandler = (e: KeyboardEvent) => { | ||||
| 					const key = e.code.toLowerCase(); | ||||
|  | ||||
| 					const targetReservedKeys = document.activeElement ? ((document.activeElement as any).dataset || {}).reservedKeys || '' : ''; | ||||
| 					if (document.activeElement && ignoreElemens.some(el => document.activeElement.matches(el))) return; | ||||
|  | ||||
| 					for (const action of actions) { | ||||
| 						if (el._hotkey_global && targetReservedKeys.includes(`'${key}'`)) break; | ||||
|  | ||||
| 						const matched = action.patterns.some(pattern => { | ||||
| 							const matched = pattern.which.includes(key) && | ||||
| 								pattern.ctrl == e.ctrlKey && | ||||
| 								pattern.shift == e.shiftKey && | ||||
| 								pattern.alt == e.altKey && | ||||
| 								e.metaKey == false; | ||||
|  | ||||
| 							if (matched) { | ||||
| 								e.preventDefault(); | ||||
| 								e.stopPropagation(); | ||||
| 								action.callback(e); | ||||
| 								return true; | ||||
| 							} else { | ||||
| 								return false; | ||||
| 							} | ||||
| 						}); | ||||
|  | ||||
| 						if (matched) { | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 				}; | ||||
|  | ||||
| 				if (el._hotkey_global) { | ||||
| 					document.addEventListener('keydown', el._keyHandler); | ||||
| 				} else { | ||||
| 					el.addEventListener('keydown', el._keyHandler); | ||||
| 				} | ||||
| 			}, | ||||
|  | ||||
| 			unbind(el) { | ||||
| 				if (el._hotkey_global) { | ||||
| 					document.removeEventListener('keydown', el._keyHandler); | ||||
| 				} else { | ||||
| 					el.removeEventListener('keydown', el._keyHandler); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| }; | ||||
							
								
								
									
										33
									
								
								src/client/app/common/keycode.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/client/app/common/keycode.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| export default (input: string): string[] => { | ||||
| 	if (Object.keys(aliases).some(a => a.toLowerCase() == input.toLowerCase())) { | ||||
| 		const codes = aliases[input]; | ||||
| 		return Array.isArray(codes) ? codes : [codes]; | ||||
| 	} else { | ||||
| 		return [input]; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| export const aliases = { | ||||
| 	'esc': 'Escape', | ||||
| 	'enter': ['Enter', 'NumpadEnter'], | ||||
| 	'up': 'ArrowUp', | ||||
| 	'down': 'ArrowDown', | ||||
| 	'left': 'ArrowLeft', | ||||
| 	'right': 'ArrowRight', | ||||
| 	'plus': ['NumpadAdd', 'Semicolon'], | ||||
| }; | ||||
|  | ||||
| /*! | ||||
| * Programatically add the following | ||||
| */ | ||||
|  | ||||
| // lower case chars | ||||
| for (let i = 97; i < 123; i++) { | ||||
| 	const char = String.fromCharCode(i); | ||||
| 	aliases[char] = `Key${char.toUpperCase()}`; | ||||
| } | ||||
|  | ||||
| // numbers | ||||
| for (let i = 0; i < 10; i++) { | ||||
| 	aliases[i] = [`Numpad${i}`, `Digit${i}`]; | ||||
| } | ||||
| @@ -13,21 +13,21 @@ type Notification = { | ||||
|  | ||||
| export default function(type, data): Notification { | ||||
| 	switch (type) { | ||||
| 		case 'drive_file_created': | ||||
| 		case 'driveFileCreated': | ||||
| 			return { | ||||
| 				title: '%i18n:common.notification.file-uploaded%', | ||||
| 				body: data.name, | ||||
| 				icon: data.url | ||||
| 			}; | ||||
|  | ||||
| 		case 'unread_messaging_message': | ||||
| 		case 'unreadMessagingMessage': | ||||
| 			return { | ||||
| 				title: '%i18n:common.notification.message-from%'.split("{}")[0] + `${getUserName(data.user)}` + '%i18n:common.notification.message-from%'.split("{}")[1] , | ||||
| 				body: data.text, // TODO: getMessagingMessageSummary(data), | ||||
| 				icon: data.user.avatarUrl | ||||
| 			}; | ||||
|  | ||||
| 		case 'reversi_invited': | ||||
| 		case 'reversiInvited': | ||||
| 			return { | ||||
| 				title: '%i18n:common.notification.reversi-invited%', | ||||
| 				body: '%i18n:common.notification.reversi-invited-by%'.split("{}")[0] + `${getUserName(data.parent)}` + '%i18n:common.notification.reversi-invited-by%'.split("{}")[1], | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| require('fuckadblock'); | ||||
|  | ||||
| declare const fuckAdBlock: any; | ||||
|  | ||||
| export default (os) => { | ||||
| 	require('fuckadblock'); | ||||
|  | ||||
| 	function adBlockDetected() { | ||||
| 		os.apis.dialog({ | ||||
| 			title: '%fa:exclamation-triangle%%i18n:common.adblock.detected%', | ||||
|   | ||||
							
								
								
									
										8
									
								
								src/client/app/common/scripts/get-md5.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/client/app/common/scripts/get-md5.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| const crypto = require('crypto'); | ||||
|  | ||||
| export default (data: ArrayBuffer) => { | ||||
|   const buf = new Buffer(data); | ||||
|   const hash = crypto.createHash("md5"); | ||||
|   hash.update(buf); | ||||
|   return hash.digest("hex"); | ||||
| }; | ||||
							
								
								
									
										128
									
								
								src/client/app/common/scripts/note-subscriber.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/client/app/common/scripts/note-subscriber.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| export default prop => ({ | ||||
| 	data() { | ||||
| 		return { | ||||
| 			connection: null | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	computed: { | ||||
| 		$_ns_note_(): any { | ||||
| 			return this[prop]; | ||||
| 		}, | ||||
|  | ||||
| 		$_ns_isRenote(): boolean { | ||||
| 			return (this.$_ns_note_.renote != null && | ||||
| 				this.$_ns_note_.text == null && | ||||
| 				this.$_ns_note_.fileIds.length == 0 && | ||||
| 				this.$_ns_note_.poll == null); | ||||
| 		}, | ||||
|  | ||||
| 		$_ns_target(): any { | ||||
| 			return this.$_ns_isRenote ? this.$_ns_note_.renote : this.$_ns_note_; | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	created() { | ||||
| 		if (this.$store.getters.isSignedIn) { | ||||
| 			this.connection = (this as any).os.stream; | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	mounted() { | ||||
| 		this.capture(true); | ||||
|  | ||||
| 		if (this.$store.getters.isSignedIn) { | ||||
| 			this.connection.on('_connected_', this.onStreamConnected); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	beforeDestroy() { | ||||
| 		this.decapture(true); | ||||
|  | ||||
| 		if (this.$store.getters.isSignedIn) { | ||||
| 			this.connection.off('_connected_', this.onStreamConnected); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		capture(withHandler = false) { | ||||
| 			if (this.$store.getters.isSignedIn) { | ||||
| 				const data = { | ||||
| 					id: this.$_ns_target.id | ||||
| 				} as any; | ||||
|  | ||||
| 				if ( | ||||
| 					(this.$_ns_target.visibleUserIds || []).includes(this.$store.state.i.id) || | ||||
| 					(this.$_ns_target.mentions || []).includes(this.$store.state.i.id) | ||||
| 				) { | ||||
| 					data.read = true; | ||||
| 				} | ||||
|  | ||||
| 				this.connection.send('sn', data); | ||||
| 				if (withHandler) this.connection.on('noteUpdated', this.onStreamNoteUpdated); | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| 		decapture(withHandler = false) { | ||||
| 			if (this.$store.getters.isSignedIn) { | ||||
| 				this.connection.send('un', { | ||||
| 					id: this.$_ns_target.id | ||||
| 				}); | ||||
| 				if (withHandler) this.connection.off('noteUpdated', this.onStreamNoteUpdated); | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| 		onStreamConnected() { | ||||
| 			this.capture(); | ||||
| 		}, | ||||
|  | ||||
| 		onStreamNoteUpdated(data) { | ||||
| 			const { type, id, body } = data; | ||||
|  | ||||
| 			if (id !== this.$_ns_target.id) return; | ||||
|  | ||||
| 			switch (type) { | ||||
| 				case 'reacted': { | ||||
| 					const reaction = body.reaction; | ||||
|  | ||||
| 					if (this.$_ns_target.reactionCounts == null) { | ||||
| 						Vue.set(this.$_ns_target, 'reactionCounts', {}); | ||||
| 					} | ||||
|  | ||||
| 					if (this.$_ns_target.reactionCounts[reaction] == null) { | ||||
| 						Vue.set(this.$_ns_target.reactionCounts, reaction, 0); | ||||
| 					} | ||||
|  | ||||
| 					this.$_ns_target.reactionCounts[reaction]++; | ||||
|  | ||||
| 					if (body.userId == this.$store.state.i.id) { | ||||
| 						Vue.set(this.$_ns_target, 'myReaction', reaction); | ||||
| 					} | ||||
| 					break; | ||||
| 				} | ||||
|  | ||||
| 				case 'pollVoted': { | ||||
| 					if (body.userId == this.$store.state.i.id) return; | ||||
| 					const choice = body.choice; | ||||
| 					this.$_ns_target.poll.choices.find(c => c.id === choice).votes++; | ||||
| 					break; | ||||
| 				} | ||||
|  | ||||
| 				case 'deleted': { | ||||
| 					Vue.set(this.$_ns_target, 'deletedAt', body.deletedAt); | ||||
| 					this.$_ns_target.text = null; | ||||
| 					this.$_ns_target.tags = []; | ||||
| 					this.$_ns_target.fileIds = []; | ||||
| 					this.$_ns_target.poll = null; | ||||
| 					this.$_ns_target.geo = null; | ||||
| 					this.$_ns_target.cw = null; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			this.$emit(`update:${prop}`, this.$_ns_note_); | ||||
| 		}, | ||||
| 	} | ||||
| }); | ||||
							
								
								
									
										231
									
								
								src/client/app/common/scripts/stream.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								src/client/app/common/scripts/stream.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,231 @@ | ||||
| import autobind from 'autobind-decorator'; | ||||
| import { EventEmitter } from 'eventemitter3'; | ||||
| import ReconnectingWebsocket from 'reconnecting-websocket'; | ||||
| import { wsUrl } from '../../config'; | ||||
| import MiOS from '../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Misskey stream connection | ||||
|  */ | ||||
| export default class Stream extends EventEmitter { | ||||
| 	private stream: ReconnectingWebsocket; | ||||
| 	private state: string; | ||||
| 	private buffer: any[]; | ||||
| 	private sharedConnections: SharedConnection[] = []; | ||||
| 	private nonSharedConnections: NonSharedConnection[] = []; | ||||
|  | ||||
| 	constructor(os: MiOS) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.state = 'initializing'; | ||||
| 		this.buffer = []; | ||||
|  | ||||
| 		const user = os.store.state.i; | ||||
|  | ||||
| 		this.stream = new ReconnectingWebsocket(wsUrl + (user ? `?i=${user.token}` : '')); | ||||
| 		this.stream.addEventListener('open', this.onOpen); | ||||
| 		this.stream.addEventListener('close', this.onClose); | ||||
| 		this.stream.addEventListener('message', this.onMessage); | ||||
| 	} | ||||
|  | ||||
| 	public useSharedConnection = (channel: string): SharedConnection => { | ||||
| 		const existConnection = this.sharedConnections.find(c => c.channel === channel); | ||||
|  | ||||
| 		if (existConnection) { | ||||
| 			existConnection.use(); | ||||
| 			return existConnection; | ||||
| 		} else { | ||||
| 			const connection = new SharedConnection(this, channel); | ||||
| 			connection.use(); | ||||
| 			this.sharedConnections.push(connection); | ||||
| 			return connection; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@autobind | ||||
| 	public removeSharedConnection(connection: SharedConnection) { | ||||
| 		this.sharedConnections = this.sharedConnections.filter(c => c.id !== connection.id); | ||||
| 	} | ||||
|  | ||||
| 	public connectToChannel = (channel: string, params?: any): NonSharedConnection => { | ||||
| 		const connection = new NonSharedConnection(this, channel, params); | ||||
| 		this.nonSharedConnections.push(connection); | ||||
| 		return connection; | ||||
| 	} | ||||
|  | ||||
| 	@autobind | ||||
| 	public disconnectToChannel(connection: NonSharedConnection) { | ||||
| 		this.nonSharedConnections = this.nonSharedConnections.filter(c => c.id !== connection.id); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Callback of when open connection | ||||
| 	 */ | ||||
| 	@autobind | ||||
| 	private onOpen() { | ||||
| 		const isReconnect = this.state == 'reconnecting'; | ||||
|  | ||||
| 		this.state = 'connected'; | ||||
| 		this.emit('_connected_'); | ||||
|  | ||||
| 		// バッファーを処理 | ||||
| 		const _buffer = [].concat(this.buffer); // Shallow copy | ||||
| 		this.buffer = []; // Clear buffer | ||||
| 		_buffer.forEach(data => { | ||||
| 			this.send(data); // Resend each buffered messages | ||||
| 		}); | ||||
|  | ||||
| 		// チャンネル再接続 | ||||
| 		if (isReconnect) { | ||||
| 			this.sharedConnections.forEach(c => { | ||||
| 				c.connect(); | ||||
| 			}); | ||||
| 			this.nonSharedConnections.forEach(c => { | ||||
| 				c.connect(); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Callback of when close connection | ||||
| 	 */ | ||||
| 	@autobind | ||||
| 	private onClose() { | ||||
| 		this.state = 'reconnecting'; | ||||
| 		this.emit('_disconnected_'); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Callback of when received a message from connection | ||||
| 	 */ | ||||
| 	@autobind | ||||
| 	private onMessage(message) { | ||||
| 		const { type, body } = JSON.parse(message.data); | ||||
|  | ||||
| 		if (type == 'channel') { | ||||
| 			const id = body.id; | ||||
| 			const connection = this.sharedConnections.find(c => c.id === id) || this.nonSharedConnections.find(c => c.id === id); | ||||
| 			connection.emit(body.type, body.body); | ||||
| 		} else { | ||||
| 			this.emit(type, body); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Send a message to connection | ||||
| 	 */ | ||||
| 	@autobind | ||||
| 	public send(typeOrPayload, payload?) { | ||||
| 		const data = payload === undefined ? typeOrPayload : { | ||||
| 			type: typeOrPayload, | ||||
| 			body: payload | ||||
| 		}; | ||||
|  | ||||
| 		// まだ接続が確立されていなかったらバッファリングして次に接続した時に送信する | ||||
| 		if (this.state != 'connected') { | ||||
| 			this.buffer.push(data); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		this.stream.send(JSON.stringify(data)); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Close this connection | ||||
| 	 */ | ||||
| 	@autobind | ||||
| 	public close() { | ||||
| 		this.stream.removeEventListener('open', this.onOpen); | ||||
| 		this.stream.removeEventListener('message', this.onMessage); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| abstract class Connection extends EventEmitter { | ||||
| 	public channel: string; | ||||
| 	public id: string; | ||||
| 	protected params: any; | ||||
| 	protected stream: Stream; | ||||
|  | ||||
| 	constructor(stream: Stream, channel: string, params?: any) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.stream = stream; | ||||
| 		this.channel = channel; | ||||
| 		this.params = params; | ||||
| 		this.id = Math.random().toString(); | ||||
| 		this.connect(); | ||||
| 	} | ||||
|  | ||||
| 	@autobind | ||||
| 	public connect() { | ||||
| 		this.stream.send('connect', { | ||||
| 			channel: this.channel, | ||||
| 			id: this.id, | ||||
| 			params: this.params | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	@autobind | ||||
| 	public send(typeOrPayload, payload?) { | ||||
| 		const type = payload === undefined ? typeOrPayload.type : typeOrPayload; | ||||
| 		const body = payload === undefined ? typeOrPayload.body : payload; | ||||
|  | ||||
| 		this.stream.send('ch', { | ||||
| 			id: this.id, | ||||
| 			type: type, | ||||
| 			body: body | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	public abstract dispose(): void; | ||||
| } | ||||
|  | ||||
| class SharedConnection extends Connection { | ||||
| 	private users = 0; | ||||
| 	private disposeTimerId: any; | ||||
|  | ||||
| 	constructor(stream: Stream, channel: string) { | ||||
| 		super(stream, channel); | ||||
| 	} | ||||
|  | ||||
| 	@autobind | ||||
| 	public use() { | ||||
| 		this.users++; | ||||
|  | ||||
| 		// タイマー解除 | ||||
| 		if (this.disposeTimerId) { | ||||
| 			clearTimeout(this.disposeTimerId); | ||||
| 			this.disposeTimerId = null; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@autobind | ||||
| 	public dispose() { | ||||
| 		this.users--; | ||||
|  | ||||
| 		// そのコネクションの利用者が誰もいなくなったら | ||||
| 		if (this.users === 0) { | ||||
| 			// また直ぐに再利用される可能性があるので、一定時間待ち、 | ||||
| 			// 新たな利用者が現れなければコネクションを切断する | ||||
| 			this.disposeTimerId = setTimeout(() => { | ||||
| 				this.disposeTimerId = null; | ||||
| 				this.removeAllListeners(); | ||||
| 				this.stream.send('disconnect', { id: this.id }); | ||||
| 				this.stream.removeSharedConnection(this); | ||||
| 			}, 3000); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class NonSharedConnection extends Connection { | ||||
| 	constructor(stream: Stream, channel: string, params?: any) { | ||||
| 		super(stream, channel, params); | ||||
| 	} | ||||
|  | ||||
| 	@autobind | ||||
| 	public dispose() { | ||||
| 		this.removeAllListeners(); | ||||
| 		this.stream.send('disconnect', { id: this.id }); | ||||
| 		this.stream.disconnectToChannel(this); | ||||
| 	} | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| import Stream from './stream'; | ||||
| import StreamManager from './stream-manager'; | ||||
| import MiOS from '../../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Drive stream connection | ||||
|  */ | ||||
| export class DriveStream extends Stream { | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(os, 'drive', { | ||||
| 			i: me.token | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export class DriveStreamManager extends StreamManager<DriveStream> { | ||||
| 	private me; | ||||
| 	private os: MiOS; | ||||
|  | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.me = me; | ||||
| 		this.os = os; | ||||
| 	} | ||||
|  | ||||
| 	public getConnection() { | ||||
| 		if (this.connection == null) { | ||||
| 			this.connection = new DriveStream(this.os, this.me); | ||||
| 		} | ||||
|  | ||||
| 		return this.connection; | ||||
| 	} | ||||
| } | ||||
| @@ -1,13 +0,0 @@ | ||||
| import Stream from '../../stream'; | ||||
| import MiOS from '../../../../../mios'; | ||||
|  | ||||
| export class ReversiGameStream extends Stream { | ||||
| 	constructor(os: MiOS, me, game) { | ||||
| 		super(os, 'games/reversi-game', me ? { | ||||
| 			i: me.token, | ||||
| 			game: game.id | ||||
| 		} : { | ||||
| 			game: game.id | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -1,31 +0,0 @@ | ||||
| import StreamManager from '../../stream-manager'; | ||||
| import Stream from '../../stream'; | ||||
| import MiOS from '../../../../../mios'; | ||||
|  | ||||
| export class ReversiStream extends Stream { | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(os, 'games/reversi', { | ||||
| 			i: me.token | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export class ReversiStreamManager extends StreamManager<ReversiStream> { | ||||
| 	private me; | ||||
| 	private os: MiOS; | ||||
|  | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.me = me; | ||||
| 		this.os = os; | ||||
| 	} | ||||
|  | ||||
| 	public getConnection() { | ||||
| 		if (this.connection == null) { | ||||
| 			this.connection = new ReversiStream(this.os, this.me); | ||||
| 		} | ||||
|  | ||||
| 		return this.connection; | ||||
| 	} | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| import Stream from './stream'; | ||||
| import StreamManager from './stream-manager'; | ||||
| import MiOS from '../../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Global timeline stream connection | ||||
|  */ | ||||
| export class GlobalTimelineStream extends Stream { | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(os, 'global-timeline', { | ||||
| 			i: me.token | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export class GlobalTimelineStreamManager extends StreamManager<GlobalTimelineStream> { | ||||
| 	private me; | ||||
| 	private os: MiOS; | ||||
|  | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.me = me; | ||||
| 		this.os = os; | ||||
| 	} | ||||
|  | ||||
| 	public getConnection() { | ||||
| 		if (this.connection == null) { | ||||
| 			this.connection = new GlobalTimelineStream(this.os, this.me); | ||||
| 		} | ||||
|  | ||||
| 		return this.connection; | ||||
| 	} | ||||
| } | ||||
| @@ -1,102 +0,0 @@ | ||||
| import Stream from './stream'; | ||||
| import StreamManager from './stream-manager'; | ||||
| import MiOS from '../../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Home stream connection | ||||
|  */ | ||||
| export class HomeStream extends Stream { | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(os, '', { | ||||
| 			i: me.token | ||||
| 		}); | ||||
|  | ||||
| 		// 最終利用日時を更新するため定期的にaliveメッセージを送信 | ||||
| 		setInterval(() => { | ||||
| 			this.send({ type: 'alive' }); | ||||
| 			me.lastUsedAt = new Date(); | ||||
| 		}, 1000 * 60); | ||||
|  | ||||
| 		// 自分の情報が更新されたとき | ||||
| 		this.on('meUpdated', i => { | ||||
| 			if (os.debug) { | ||||
| 				console.log('I updated:', i); | ||||
| 			} | ||||
|  | ||||
| 			os.store.dispatch('mergeMe', i); | ||||
| 		}); | ||||
|  | ||||
| 		this.on('read_all_notifications', () => { | ||||
| 			os.store.dispatch('mergeMe', { | ||||
| 				hasUnreadNotification: false | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		this.on('unread_notification', () => { | ||||
| 			os.store.dispatch('mergeMe', { | ||||
| 				hasUnreadNotification: true | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		this.on('read_all_messaging_messages', () => { | ||||
| 			os.store.dispatch('mergeMe', { | ||||
| 				hasUnreadMessagingMessage: false | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		this.on('unread_messaging_message', () => { | ||||
| 			os.store.dispatch('mergeMe', { | ||||
| 				hasUnreadMessagingMessage: true | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		this.on('clientSettingUpdated', x => { | ||||
| 			os.store.commit('settings/set', { | ||||
| 				key: x.key, | ||||
| 				value: x.value | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		this.on('home_updated', x => { | ||||
| 			os.store.commit('settings/setHome', x); | ||||
| 		}); | ||||
|  | ||||
| 		this.on('mobile_home_updated', x => { | ||||
| 			os.store.commit('settings/setMobileHome', x); | ||||
| 		}); | ||||
|  | ||||
| 		this.on('widgetUpdated', x => { | ||||
| 			os.store.commit('settings/setWidget', { | ||||
| 				id: x.id, | ||||
| 				data: x.data | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		// トークンが再生成されたとき | ||||
| 		// このままではMisskeyが利用できないので強制的にサインアウトさせる | ||||
| 		this.on('my_token_regenerated', () => { | ||||
| 			alert('%i18n:common.my-token-regenerated%'); | ||||
| 			os.signout(); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export class HomeStreamManager extends StreamManager<HomeStream> { | ||||
| 	private me; | ||||
| 	private os: MiOS; | ||||
|  | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.me = me; | ||||
| 		this.os = os; | ||||
| 	} | ||||
|  | ||||
| 	public getConnection() { | ||||
| 		if (this.connection == null) { | ||||
| 			this.connection = new HomeStream(this.os, this.me); | ||||
| 		} | ||||
|  | ||||
| 		return this.connection; | ||||
| 	} | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| import Stream from './stream'; | ||||
| import StreamManager from './stream-manager'; | ||||
| import MiOS from '../../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Hybrid timeline stream connection | ||||
|  */ | ||||
| export class HybridTimelineStream extends Stream { | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(os, 'hybrid-timeline', { | ||||
| 			i: me.token | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export class HybridTimelineStreamManager extends StreamManager<HybridTimelineStream> { | ||||
| 	private me; | ||||
| 	private os: MiOS; | ||||
|  | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.me = me; | ||||
| 		this.os = os; | ||||
| 	} | ||||
|  | ||||
| 	public getConnection() { | ||||
| 		if (this.connection == null) { | ||||
| 			this.connection = new HybridTimelineStream(this.os, this.me); | ||||
| 		} | ||||
|  | ||||
| 		return this.connection; | ||||
| 	} | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| import Stream from './stream'; | ||||
| import StreamManager from './stream-manager'; | ||||
| import MiOS from '../../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Local timeline stream connection | ||||
|  */ | ||||
| export class LocalTimelineStream extends Stream { | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(os, 'local-timeline', me ? { | ||||
| 			i: me.token | ||||
| 		} : {}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export class LocalTimelineStreamManager extends StreamManager<LocalTimelineStream> { | ||||
| 	private me; | ||||
| 	private os: MiOS; | ||||
|  | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.me = me; | ||||
| 		this.os = os; | ||||
| 	} | ||||
|  | ||||
| 	public getConnection() { | ||||
| 		if (this.connection == null) { | ||||
| 			this.connection = new LocalTimelineStream(this.os, this.me); | ||||
| 		} | ||||
|  | ||||
| 		return this.connection; | ||||
| 	} | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| import Stream from './stream'; | ||||
| import StreamManager from './stream-manager'; | ||||
| import MiOS from '../../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Messaging index stream connection | ||||
|  */ | ||||
| export class MessagingIndexStream extends Stream { | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(os, 'messaging-index', { | ||||
| 			i: me.token | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export class MessagingIndexStreamManager extends StreamManager<MessagingIndexStream> { | ||||
| 	private me; | ||||
| 	private os: MiOS; | ||||
|  | ||||
| 	constructor(os: MiOS, me) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.me = me; | ||||
| 		this.os = os; | ||||
| 	} | ||||
|  | ||||
| 	public getConnection() { | ||||
| 		if (this.connection == null) { | ||||
| 			this.connection = new MessagingIndexStream(this.os, this.me); | ||||
| 		} | ||||
|  | ||||
| 		return this.connection; | ||||
| 	} | ||||
| } | ||||
| @@ -1,20 +0,0 @@ | ||||
| import Stream from './stream'; | ||||
| import MiOS from '../../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Messaging stream connection | ||||
|  */ | ||||
| export class MessagingStream extends Stream { | ||||
| 	constructor(os: MiOS, me, otherparty) { | ||||
| 		super(os, 'messaging', { | ||||
| 			i: me.token, | ||||
| 			otherparty | ||||
| 		}); | ||||
|  | ||||
| 		(this as any).on('_connected_', () => { | ||||
| 			this.send({ | ||||
| 				i: me.token | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -1,30 +0,0 @@ | ||||
| import Stream from './stream'; | ||||
| import StreamManager from './stream-manager'; | ||||
| import MiOS from '../../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Notes stats stream connection | ||||
|  */ | ||||
| export class NotesStatsStream extends Stream { | ||||
| 	constructor(os: MiOS) { | ||||
| 		super(os, 'notes-stats'); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export class NotesStatsStreamManager extends StreamManager<NotesStatsStream> { | ||||
| 	private os: MiOS; | ||||
|  | ||||
| 	constructor(os: MiOS) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.os = os; | ||||
| 	} | ||||
|  | ||||
| 	public getConnection() { | ||||
| 		if (this.connection == null) { | ||||
| 			this.connection = new NotesStatsStream(this.os); | ||||
| 		} | ||||
|  | ||||
| 		return this.connection; | ||||
| 	} | ||||
| } | ||||
| @@ -1,30 +0,0 @@ | ||||
| import Stream from './stream'; | ||||
| import StreamManager from './stream-manager'; | ||||
| import MiOS from '../../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Server stats stream connection | ||||
|  */ | ||||
| export class ServerStatsStream extends Stream { | ||||
| 	constructor(os: MiOS) { | ||||
| 		super(os, 'server-stats'); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export class ServerStatsStreamManager extends StreamManager<ServerStatsStream> { | ||||
| 	private os: MiOS; | ||||
|  | ||||
| 	constructor(os: MiOS) { | ||||
| 		super(); | ||||
|  | ||||
| 		this.os = os; | ||||
| 	} | ||||
|  | ||||
| 	public getConnection() { | ||||
| 		if (this.connection == null) { | ||||
| 			this.connection = new ServerStatsStream(this.os); | ||||
| 		} | ||||
|  | ||||
| 		return this.connection; | ||||
| 	} | ||||
| } | ||||
| @@ -1,109 +0,0 @@ | ||||
| import { EventEmitter } from 'eventemitter3'; | ||||
| import * as uuid from 'uuid'; | ||||
| import Connection from './stream'; | ||||
| import { erase } from '../../../../../prelude/array'; | ||||
|  | ||||
| /** | ||||
|  * ストリーム接続を管理するクラス | ||||
|  * 複数の場所から同じストリームを利用する際、接続をまとめたりする | ||||
|  */ | ||||
| export default abstract class StreamManager<T extends Connection> extends EventEmitter { | ||||
| 	private _connection: T = null; | ||||
|  | ||||
| 	private disposeTimerId: any; | ||||
|  | ||||
| 	/** | ||||
| 	 * コネクションを必要としているユーザー | ||||
| 	 */ | ||||
| 	private users = []; | ||||
|  | ||||
| 	protected set connection(connection: T) { | ||||
| 		this._connection = connection; | ||||
|  | ||||
| 		if (this._connection == null) { | ||||
| 			this.emit('disconnected'); | ||||
| 		} else { | ||||
| 			this.emit('connected', this._connection); | ||||
|  | ||||
| 			this._connection.on('_connected_', () => { | ||||
| 				this.emit('_connected_'); | ||||
| 			}); | ||||
|  | ||||
| 			this._connection.on('_disconnected_', () => { | ||||
| 				this.emit('_disconnected_'); | ||||
| 			}); | ||||
|  | ||||
| 			this._connection.user = 'Managed'; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	protected get connection() { | ||||
| 		return this._connection; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * コネクションを持っているか否か | ||||
| 	 */ | ||||
| 	public get hasConnection() { | ||||
| 		return this._connection != null; | ||||
| 	} | ||||
|  | ||||
| 	public get state(): string { | ||||
| 		if (!this.hasConnection) return 'no-connection'; | ||||
| 		return this._connection.state; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * コネクションを要求します | ||||
| 	 */ | ||||
| 	public abstract getConnection(): T; | ||||
|  | ||||
| 	/** | ||||
| 	 * 現在接続しているコネクションを取得します | ||||
| 	 */ | ||||
| 	public borrow() { | ||||
| 		return this._connection; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * コネクションを要求するためのユーザーIDを発行します | ||||
| 	 */ | ||||
| 	public use() { | ||||
| 		// タイマー解除 | ||||
| 		if (this.disposeTimerId) { | ||||
| 			clearTimeout(this.disposeTimerId); | ||||
| 			this.disposeTimerId = null; | ||||
| 		} | ||||
|  | ||||
| 		// ユーザーID生成 | ||||
| 		const userId = uuid(); | ||||
|  | ||||
| 		this.users.push(userId); | ||||
|  | ||||
| 		this._connection.user = `Managed (${ this.users.length })`; | ||||
|  | ||||
| 		return userId; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * コネクションを利用し終わってもう必要ないことを通知します | ||||
| 	 * @param userId use で発行したユーザーID | ||||
| 	 */ | ||||
| 	public dispose(userId) { | ||||
| 		this.users = erase(userId, this.users); | ||||
|  | ||||
| 		this._connection.user = `Managed (${ this.users.length })`; | ||||
|  | ||||
| 		// 誰もコネクションの利用者がいなくなったら | ||||
| 		if (this.users.length == 0) { | ||||
| 			// また直ぐに再利用される可能性があるので、一定時間待ち、 | ||||
| 			// 新たな利用者が現れなければコネクションを切断する | ||||
| 			this.disposeTimerId = setTimeout(() => { | ||||
| 				this.disposeTimerId = null; | ||||
|  | ||||
| 				this.connection.close(); | ||||
| 				this.connection = null; | ||||
| 			}, 3000); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,137 +0,0 @@ | ||||
| import { EventEmitter } from 'eventemitter3'; | ||||
| import * as uuid from 'uuid'; | ||||
| import * as ReconnectingWebsocket from 'reconnecting-websocket'; | ||||
| import { wsUrl } from '../../../config'; | ||||
| import MiOS from '../../../mios'; | ||||
|  | ||||
| /** | ||||
|  * Misskey stream connection | ||||
|  */ | ||||
| export default class Connection extends EventEmitter { | ||||
| 	public state: string; | ||||
| 	private buffer: any[]; | ||||
| 	public socket: ReconnectingWebsocket; | ||||
| 	public name: string; | ||||
| 	public connectedAt: Date; | ||||
| 	public user: string = null; | ||||
| 	public in: number = 0; | ||||
| 	public out: number = 0; | ||||
| 	public inout: Array<{ | ||||
| 		type: 'in' | 'out', | ||||
| 		at: Date, | ||||
| 		data: string | ||||
| 	}> = []; | ||||
| 	public id: string; | ||||
| 	public isSuspended = false; | ||||
| 	private os: MiOS; | ||||
|  | ||||
| 	constructor(os: MiOS, endpoint, params?) { | ||||
| 		super(); | ||||
|  | ||||
| 		//#region BIND | ||||
| 		this.onOpen =    this.onOpen.bind(this); | ||||
| 		this.onClose =   this.onClose.bind(this); | ||||
| 		this.onMessage = this.onMessage.bind(this); | ||||
| 		this.send =      this.send.bind(this); | ||||
| 		this.close =     this.close.bind(this); | ||||
| 		//#endregion | ||||
|  | ||||
| 		this.id = uuid(); | ||||
| 		this.os = os; | ||||
| 		this.name = endpoint; | ||||
| 		this.state = 'initializing'; | ||||
| 		this.buffer = []; | ||||
|  | ||||
| 		const query = params | ||||
| 			? Object.keys(params) | ||||
| 				.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`) | ||||
| 				.join('&') | ||||
| 			: null; | ||||
|  | ||||
| 		this.socket = new ReconnectingWebsocket(`${wsUrl}/${endpoint}${query ? `?${query}` : ''}`); | ||||
| 		this.socket.addEventListener('open', this.onOpen); | ||||
| 		this.socket.addEventListener('close', this.onClose); | ||||
| 		this.socket.addEventListener('message', this.onMessage); | ||||
|  | ||||
| 		// Register this connection for debugging | ||||
| 		this.os.registerStreamConnection(this); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Callback of when open connection | ||||
| 	 */ | ||||
| 	private onOpen() { | ||||
| 		this.state = 'connected'; | ||||
| 		this.emit('_connected_'); | ||||
|  | ||||
| 		this.connectedAt = new Date(); | ||||
|  | ||||
| 		// バッファーを処理 | ||||
| 		const _buffer = [].concat(this.buffer); // Shallow copy | ||||
| 		this.buffer = []; // Clear buffer | ||||
| 		_buffer.forEach(data => { | ||||
| 			this.send(data); // Resend each buffered messages | ||||
|  | ||||
| 			if (this.os.debug) { | ||||
| 				this.out++; | ||||
| 				this.inout.push({ type: 'out', at: new Date(), data }); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Callback of when close connection | ||||
| 	 */ | ||||
| 	private onClose() { | ||||
| 		this.state = 'reconnecting'; | ||||
| 		this.emit('_disconnected_'); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Callback of when received a message from connection | ||||
| 	 */ | ||||
| 	private onMessage(message) { | ||||
| 		if (this.isSuspended) return; | ||||
|  | ||||
| 		if (this.os.debug) { | ||||
| 			this.in++; | ||||
| 			this.inout.push({ type: 'in', at: new Date(), data: message.data }); | ||||
| 		} | ||||
|  | ||||
| 		try { | ||||
| 			const msg = JSON.parse(message.data); | ||||
| 			if (msg.type) this.emit(msg.type, msg.body); | ||||
| 		} catch (e) { | ||||
| 			// noop | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Send a message to connection | ||||
| 	 */ | ||||
| 	public send(data) { | ||||
| 		if (this.isSuspended) return; | ||||
|  | ||||
| 		// まだ接続が確立されていなかったらバッファリングして次に接続した時に送信する | ||||
| 		if (this.state != 'connected') { | ||||
| 			this.buffer.push(data); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		if (this.os.debug) { | ||||
| 			this.out++; | ||||
| 			this.inout.push({ type: 'out', at: new Date(), data }); | ||||
| 		} | ||||
|  | ||||
| 		this.socket.send(JSON.stringify(data)); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Close this connection | ||||
| 	 */ | ||||
| 	public close() { | ||||
| 		this.os.unregisterStreamConnection(this); | ||||
| 		this.socket.removeEventListener('open', this.onOpen); | ||||
| 		this.socket.removeEventListener('message', this.onMessage); | ||||
| 	} | ||||
| } | ||||
| @@ -1,17 +0,0 @@ | ||||
| import Stream from './stream'; | ||||
| import MiOS from '../../mios'; | ||||
|  | ||||
| export class UserListStream extends Stream { | ||||
| 	constructor(os: MiOS, me, listId) { | ||||
| 		super(os, 'user-list', { | ||||
| 			i: me.token, | ||||
| 			listId | ||||
| 		}); | ||||
|  | ||||
| 		(this as any).on('_connected_', () => { | ||||
| 			this.send({ | ||||
| 				i: me.token | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -259,15 +259,13 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .mk-autocomplete | ||||
| 	position fixed | ||||
| 	z-index 65535 | ||||
| 	max-width 100% | ||||
| 	margin-top calc(1em + 8px) | ||||
| 	overflow hidden | ||||
| 	background isDark ? #313543 : #fff | ||||
| 	background var(--faceHeader) | ||||
| 	border solid 1px rgba(#000, 0.1) | ||||
| 	border-radius 4px | ||||
| 	transition top 0.1s ease, left 0.1s ease | ||||
| @@ -299,16 +297,16 @@ root(isDark) | ||||
| 				text-overflow ellipsis | ||||
|  | ||||
| 			&:hover | ||||
| 				background isDark ? rgba(#fff, 0.1) : rgba(#000, 0.1) | ||||
| 				background var(--autocompleteItemHoverBg) | ||||
|  | ||||
| 			&[data-selected='true'] | ||||
| 				background $theme-color | ||||
| 				background var(--primary) | ||||
|  | ||||
| 				&, * | ||||
| 					color #fff !important | ||||
|  | ||||
| 			&:active | ||||
| 				background darken($theme-color, 10%) | ||||
| 				background var(--primaryDarken10) | ||||
|  | ||||
| 				&, * | ||||
| 					color #fff !important | ||||
| @@ -325,15 +323,15 @@ root(isDark) | ||||
|  | ||||
| 		.name | ||||
| 			margin 0 8px 0 0 | ||||
| 			color isDark ? rgba(#fff, 0.8) : rgba(#000, 0.8) | ||||
| 			color var(--autocompleteItemText) | ||||
|  | ||||
| 		.username | ||||
| 			color isDark ? rgba(#fff, 0.3) : rgba(#000, 0.3) | ||||
| 			color var(--autocompleteItemTextSub) | ||||
|  | ||||
| 	> .hashtags > li | ||||
|  | ||||
| 		.name | ||||
| 			color isDark ? rgba(#fff, 0.8) : rgba(#000, 0.8) | ||||
| 			color var(--autocompleteItemText) | ||||
|  | ||||
| 	> .emojis > li | ||||
|  | ||||
| @@ -343,15 +341,9 @@ root(isDark) | ||||
| 			width 24px | ||||
|  | ||||
| 		.name | ||||
| 			color isDark ? rgba(#fff, 0.8) : rgba(#000, 0.8) | ||||
| 			color var(--autocompleteItemText) | ||||
|  | ||||
| 		.alias | ||||
| 			margin 0 0 0 8px | ||||
| 			color isDark ? rgba(#fff, 0.3) : rgba(#000, 0.3) | ||||
|  | ||||
| .mk-autocomplete[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-autocomplete:not([data-darkmode]) | ||||
| 	root(false) | ||||
| 			color var(--autocompleteItemTextSub) | ||||
| </style> | ||||
|   | ||||
| @@ -58,6 +58,11 @@ export default Vue.extend({ | ||||
| 			}; | ||||
| 		} | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		if (this.user.avatarColor) { | ||||
| 			this.$el.style.color = `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`; | ||||
| 		} | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		onClick(e) { | ||||
| 			this.$emit('click', e); | ||||
| @@ -67,8 +72,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
|  | ||||
| root(isDark) | ||||
| .mk-avatar | ||||
| 	display inline-block | ||||
| 	vertical-align bottom | ||||
|  | ||||
| @@ -79,7 +83,7 @@ root(isDark) | ||||
| 	&.cat::before, | ||||
| 	&.cat::after | ||||
| 		background #df548f | ||||
| 		border solid 4px isDark ? #e0eefd : #202224 | ||||
| 		border solid 4px currentColor | ||||
| 		box-sizing border-box | ||||
| 		content '' | ||||
| 		display inline-block | ||||
| @@ -105,9 +109,4 @@ root(isDark) | ||||
| 		transition border-radius 1s ease | ||||
| 		z-index 1 | ||||
|  | ||||
| .mk-avatar[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-avatar:not([data-darkmode]) | ||||
| 	root(false) | ||||
| </style> | ||||
|   | ||||
| @@ -39,7 +39,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
|  | ||||
| .mk-connect-failed | ||||
| 	width 100% | ||||
| @@ -70,17 +70,17 @@ export default Vue.extend({ | ||||
| 		display block | ||||
| 		margin 1em auto 0 auto | ||||
| 		padding 8px 10px | ||||
| 		color $theme-color-foreground | ||||
| 		background $theme-color | ||||
| 		color var(--primaryForeground) | ||||
| 		background var(--primary) | ||||
|  | ||||
| 		&:focus | ||||
| 			outline solid 3px rgba($theme-color, 0.3) | ||||
| 			outline solid 3px var(--primaryAlpha03) | ||||
|  | ||||
| 		&:hover | ||||
| 			background lighten($theme-color, 10%) | ||||
| 			background var(--primaryLighten10) | ||||
|  | ||||
| 		&:active | ||||
| 			background darken($theme-color, 10%) | ||||
| 			background var(--primaryDarken10) | ||||
|  | ||||
| 	> .thanks | ||||
| 		display block | ||||
|   | ||||
| @@ -22,23 +22,17 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| root(isDark) | ||||
| .nrvgflfuaxwgkxoynpnumyookecqrrvh | ||||
| 	display inline-block | ||||
| 	padding 4px 8px | ||||
| 	font-size 0.7em | ||||
| 	color isDark ? #393f4f : #fff | ||||
| 	background isDark ? #687390 : #b1b9c1 | ||||
| 	color var(--cwButtonFg) | ||||
| 	background var(--cwButtonBg) | ||||
| 	border-radius 2px | ||||
| 	cursor pointer | ||||
| 	user-select none | ||||
|  | ||||
| 	&:hover | ||||
| 		background isDark ? #707b97 : #bbc4ce | ||||
|  | ||||
| .nrvgflfuaxwgkxoynpnumyookecqrrvh[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .nrvgflfuaxwgkxoynpnumyookecqrrvh:not([data-darkmode]) | ||||
| 	root(false) | ||||
| 		background var(--cwButtonHoverBg) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| </template> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
|  | ||||
| .a | ||||
| 	display block | ||||
| @@ -18,8 +18,8 @@ | ||||
| 		display block | ||||
| 		//fill #151513 | ||||
| 		//color #fff | ||||
| 		fill $theme-color | ||||
| 		color $theme-color-foreground | ||||
| 		fill var(--primary) | ||||
| 		color var(--primaryForeground) | ||||
|  | ||||
| 		.octo-arm | ||||
| 			transform-origin 130px 106px | ||||
|   | ||||
| @@ -186,9 +186,8 @@ export default Vue.extend({ | ||||
| 		if (this.game.isStarted && !this.game.isEnded) { | ||||
| 			this.pollingClock = setInterval(() => { | ||||
| 				const crc32 = CRC32.str(this.logs.map(x => x.pos.toString()).join('')); | ||||
| 				this.connection.send({ | ||||
| 					type: 'check', | ||||
| 					crc32 | ||||
| 				this.connection.send('check', { | ||||
| 					crc32: crc32 | ||||
| 				}); | ||||
| 			}, 3000); | ||||
| 		} | ||||
| @@ -224,9 +223,8 @@ export default Vue.extend({ | ||||
| 				sound.play(); | ||||
| 			} | ||||
|  | ||||
| 			this.connection.send({ | ||||
| 				type: 'set', | ||||
| 				pos | ||||
| 			this.connection.send('set', { | ||||
| 				pos: pos | ||||
| 			}); | ||||
|  | ||||
| 			this.checkEnd(); | ||||
| @@ -304,9 +302,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .xqnhankfuuilcwvhgsopeqncafzsquya | ||||
| 	text-align center | ||||
|  | ||||
| 	> .go-index | ||||
| @@ -319,7 +315,7 @@ root(isDark) | ||||
|  | ||||
| 	> header | ||||
| 		padding 8px | ||||
| 		border-bottom dashed 1px isDark ? #4c5761 : #c4cdd4 | ||||
| 		border-bottom dashed 1px var(--reversiGameHeaderLine) | ||||
|  | ||||
| 		a | ||||
| 			color inherit | ||||
| @@ -386,30 +382,30 @@ root(isDark) | ||||
| 						user-select none | ||||
|  | ||||
| 					&.empty | ||||
| 						border solid 2px isDark ? #51595f : #eee | ||||
| 						border solid 2px var(--reversiGameEmptyCell) | ||||
|  | ||||
| 					&.empty.can | ||||
| 						background isDark ? #51595f : #eee | ||||
| 						background var(--reversiGameEmptyCell) | ||||
|  | ||||
| 					&.empty.myTurn | ||||
| 						border-color isDark ? #6a767f : #ddd | ||||
| 						border-color var(--reversiGameEmptyCellMyTurn) | ||||
|  | ||||
| 						&.can | ||||
| 							background isDark ? #51595f : #eee | ||||
| 							background var(--reversiGameEmptyCellCanPut) | ||||
| 							cursor pointer | ||||
|  | ||||
| 							&:hover | ||||
| 								border-color darken($theme-color, 10%) | ||||
| 								background $theme-color | ||||
| 								border-color var(--primaryDarken10) | ||||
| 								background var(--primary) | ||||
|  | ||||
| 							&:active | ||||
| 								background darken($theme-color, 10%) | ||||
| 								background var(--primaryDarken10) | ||||
|  | ||||
| 					&.prev | ||||
| 						box-shadow 0 0 0 4px rgba($theme-color, 0.7) | ||||
| 						box-shadow 0 0 0 4px var(--primaryAlpha07) | ||||
|  | ||||
| 					&.isEnded | ||||
| 						border-color isDark ? #6a767f : #ddd | ||||
| 						border-color var(--reversiGameEmptyCellMyTurn) | ||||
|  | ||||
| 					&.none | ||||
| 						border-color transparent !important | ||||
| @@ -458,10 +454,4 @@ root(isDark) | ||||
| 			margin 0 8px | ||||
| 			min-width 70px | ||||
|  | ||||
| .xqnhankfuuilcwvhgsopeqncafzsquya[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .xqnhankfuuilcwvhgsopeqncafzsquya:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
| import Vue from 'vue'; | ||||
| import XGame from './reversi.game.vue'; | ||||
| import XRoom from './reversi.room.vue'; | ||||
| import { ReversiGameStream } from '../../../../scripts/streaming/games/reversi/reversi-game'; | ||||
|  | ||||
| export default Vue.extend({ | ||||
| 	components: { | ||||
| @@ -34,12 +33,13 @@ export default Vue.extend({ | ||||
| 	}, | ||||
| 	created() { | ||||
| 		this.g = this.game; | ||||
| 		this.connection = new ReversiGameStream((this as any).os, this.$store.state.i, this.game); | ||||
| 		this.connection = (this as any).os.stream.connectToChannel('gamesReversiGame', { | ||||
| 			gameId: this.game.id | ||||
| 		}); | ||||
| 		this.connection.on('started', this.onStarted); | ||||
| 	}, | ||||
| 	beforeDestroy() { | ||||
| 		this.connection.off('started', this.onStarted); | ||||
| 		this.connection.close(); | ||||
| 		this.connection.dispose(); | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		onStarted(game) { | ||||
|   | ||||
| @@ -59,15 +59,13 @@ export default Vue.extend({ | ||||
| 			myGames: [], | ||||
| 			matching: null, | ||||
| 			invitations: [], | ||||
| 			connection: null, | ||||
| 			connectionId: null | ||||
| 			connection: null | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	mounted() { | ||||
| 		if (this.$store.getters.isSignedIn) { | ||||
| 			this.connection = (this as any).os.streams.reversiStream.getConnection(); | ||||
| 			this.connectionId = (this as any).os.streams.reversiStream.use(); | ||||
| 			this.connection = (this as any).os.stream.useSharedConnection('gamesReversi'); | ||||
|  | ||||
| 			this.connection.on('invited', this.onInvited); | ||||
|  | ||||
| @@ -90,8 +88,7 @@ export default Vue.extend({ | ||||
|  | ||||
| 	beforeDestroy() { | ||||
| 		if (this.connection) { | ||||
| 			this.connection.off('invited', this.onInvited); | ||||
| 			(this as any).os.streams.reversiStream.dispose(this.connectionId); | ||||
| 			this.connection.dispose(); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| @@ -138,9 +135,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .phgnkghfpyvkrvwiajkiuoxyrdaqpzcx | ||||
| 	> h1 | ||||
| 		margin 0 | ||||
| 		padding 24px | ||||
| @@ -148,7 +143,7 @@ root(isDark) | ||||
| 		text-align center | ||||
| 		font-weight normal | ||||
| 		color #fff | ||||
| 		background linear-gradient(to bottom, isDark ? #45730e : #8bca3e, isDark ? #464300 : #d6cf31) | ||||
| 		background linear-gradient(to bottom, var(--reversiBannerGradientStart), var(--reversiBannerGradientEnd)) | ||||
|  | ||||
| 		& + p | ||||
| 			margin 0 | ||||
| @@ -156,7 +151,7 @@ root(isDark) | ||||
| 			margin-bottom 12px | ||||
| 			text-align center | ||||
| 			font-size 14px | ||||
| 			border-bottom solid 1px isDark ? #535f65 : #d3d9dc | ||||
| 			border-bottom solid 1px var(--faceDivider) | ||||
|  | ||||
| 	> .play | ||||
| 		margin 0 auto | ||||
| @@ -171,14 +166,14 @@ root(isDark) | ||||
| 				padding 16px | ||||
| 				font-size 14px | ||||
| 				text-align left | ||||
| 				background isDark ? #282c37 : #f5f5f5 | ||||
| 				background var(--reversiDescBg) | ||||
| 				border-radius 8px | ||||
|  | ||||
| 	> section | ||||
| 		margin 0 auto | ||||
| 		padding 0 16px 16px 16px | ||||
| 		max-width 500px | ||||
| 		border-top solid 1px isDark ? #535f65 : #d3d9dc | ||||
| 		border-top solid 1px var(--faceDivider) | ||||
|  | ||||
| 		> h2 | ||||
| 			margin 0 | ||||
| @@ -189,9 +184,9 @@ root(isDark) | ||||
| 	.invitation | ||||
| 		margin 8px 0 | ||||
| 		padding 8px | ||||
| 		color isDark ? #fff : #677f84 | ||||
| 		background isDark ? #282c37 : #fff | ||||
| 		box-shadow 0 2px 16px rgba(#000, isDark ? 0.7 : 0.15) | ||||
| 		color var(--text) | ||||
| 		background var(--face) | ||||
| 		box-shadow 0 2px 16px var(--reversiListItemShadow) | ||||
| 		border-radius 6px | ||||
| 		cursor pointer | ||||
|  | ||||
| @@ -200,13 +195,13 @@ root(isDark) | ||||
| 			user-select none | ||||
|  | ||||
| 		&:focus | ||||
| 			border-color $theme-color | ||||
| 			border-color var(--primary) | ||||
|  | ||||
| 		&:hover | ||||
| 			background isDark ? #313543 : #f5f5f5 | ||||
| 			box-shadow 0 0 0 100px inset rgba(0, 0, 0, 0.05) | ||||
|  | ||||
| 		&:active | ||||
| 			background isDark ? #1e222b : #eee | ||||
| 			box-shadow 0 0 0 100px inset rgba(0, 0, 0, 0.1) | ||||
|  | ||||
| 		> .avatar | ||||
| 			width 32px | ||||
| @@ -221,9 +216,9 @@ root(isDark) | ||||
| 		display block | ||||
| 		margin 8px 0 | ||||
| 		padding 8px | ||||
| 		color isDark ? #fff : #677f84 | ||||
| 		background isDark ? #282c37 : #fff | ||||
| 		box-shadow 0 2px 16px rgba(#000, isDark ? 0.7 : 0.15) | ||||
| 		color var(--text) | ||||
| 		background var(--face) | ||||
| 		box-shadow 0 2px 16px var(--reversiListItemShadow) | ||||
| 		border-radius 6px | ||||
| 		cursor pointer | ||||
|  | ||||
| @@ -232,10 +227,10 @@ root(isDark) | ||||
| 			user-select none | ||||
|  | ||||
| 		&:hover | ||||
| 			background isDark ? #313543 : #f5f5f5 | ||||
| 			box-shadow 0 0 0 100px inset rgba(0, 0, 0, 0.05) | ||||
|  | ||||
| 		&:active | ||||
| 			background isDark ? #1e222b : #eee | ||||
| 			box-shadow 0 0 0 100px inset rgba(0, 0, 0, 0.1) | ||||
|  | ||||
| 		> .avatar | ||||
| 			width 32px | ||||
| @@ -246,10 +241,4 @@ root(isDark) | ||||
| 			margin 0 8px | ||||
| 			line-height 32px | ||||
|  | ||||
| .phgnkghfpyvkrvwiajkiuoxyrdaqpzcx[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .phgnkghfpyvkrvwiajkiuoxyrdaqpzcx:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -47,9 +47,9 @@ | ||||
| 			</header> | ||||
|  | ||||
| 			<div> | ||||
| 				<mk-switch v-model="game.settings.isLlotheo" @change="updateSettings" text="%i18n:@is-llotheo%"/> | ||||
| 				<mk-switch v-model="game.settings.loopedBoard" @change="updateSettings" text="%i18n:@looped-map%"/> | ||||
| 				<mk-switch v-model="game.settings.canPutEverywhere" @change="updateSettings" text="%i18n:@can-put-everywhere%"/> | ||||
| 				<ui-switch v-model="game.settings.isLlotheo" @change="updateSettings">%i18n:@is-llotheo%</ui-switch> | ||||
| 				<ui-switch v-model="game.settings.loopedBoard" @change="updateSettings">%i18n:@looped-map%</ui-switch> | ||||
| 				<ui-switch v-model="game.settings.canPutEverywhere" @change="updateSettings">%i18n:@can-put-everywhere%</ui-switch> | ||||
| 			</div> | ||||
| 		</div> | ||||
|  | ||||
| @@ -60,7 +60,7 @@ | ||||
|  | ||||
| 			<div> | ||||
| 				<template v-for="item in form"> | ||||
| 					<mk-switch v-if="item.type == 'switch'" v-model="item.value" :key="item.id" :text="item.label" @change="onChangeForm(item)">{{ item.desc || '' }}</mk-switch> | ||||
| 					<ui-switch v-if="item.type == 'switch'" v-model="item.value" :key="item.id" :text="item.label" @change="onChangeForm(item)">{{ item.desc || '' }}</ui-switch> | ||||
|  | ||||
| 					<div class="card" v-if="item.type == 'radio'" :key="item.id"> | ||||
| 						<header> | ||||
| @@ -149,9 +149,9 @@ export default Vue.extend({ | ||||
| 	}, | ||||
|  | ||||
| 	created() { | ||||
| 		this.connection.on('change-accepts', this.onChangeAccepts); | ||||
| 		this.connection.on('update-settings', this.onUpdateSettings); | ||||
| 		this.connection.on('init-form', this.onInitForm); | ||||
| 		this.connection.on('changeAccepts', this.onChangeAccepts); | ||||
| 		this.connection.on('updateSettings', this.onUpdateSettings); | ||||
| 		this.connection.on('initForm', this.onInitForm); | ||||
| 		this.connection.on('message', this.onMessage); | ||||
|  | ||||
| 		if (this.game.user1Id != this.$store.state.i.id && this.game.settings.form1) this.form = this.game.settings.form1; | ||||
| @@ -159,9 +159,9 @@ export default Vue.extend({ | ||||
| 	}, | ||||
|  | ||||
| 	beforeDestroy() { | ||||
| 		this.connection.off('change-accepts', this.onChangeAccepts); | ||||
| 		this.connection.off('update-settings', this.onUpdateSettings); | ||||
| 		this.connection.off('init-form', this.onInitForm); | ||||
| 		this.connection.off('changeAccepts', this.onChangeAccepts); | ||||
| 		this.connection.off('updateSettings', this.onUpdateSettings); | ||||
| 		this.connection.off('initForm', this.onInitForm); | ||||
| 		this.connection.off('message', this.onMessage); | ||||
| 	}, | ||||
|  | ||||
| @@ -171,15 +171,11 @@ export default Vue.extend({ | ||||
| 		}, | ||||
|  | ||||
| 		accept() { | ||||
| 			this.connection.send({ | ||||
| 				type: 'accept' | ||||
| 			}); | ||||
| 			this.connection.send('accept', {}); | ||||
| 		}, | ||||
|  | ||||
| 		cancel() { | ||||
| 			this.connection.send({ | ||||
| 				type: 'cancel-accept' | ||||
| 			}); | ||||
| 			this.connection.send('cancelAccept', {}); | ||||
| 		}, | ||||
|  | ||||
| 		onChangeAccepts(accepts) { | ||||
| @@ -189,8 +185,7 @@ export default Vue.extend({ | ||||
| 		}, | ||||
|  | ||||
| 		updateSettings() { | ||||
| 			this.connection.send({ | ||||
| 				type: 'update-settings', | ||||
| 			this.connection.send('updateSettings', { | ||||
| 				settings: this.game.settings | ||||
| 			}); | ||||
| 		}, | ||||
| @@ -216,8 +211,7 @@ export default Vue.extend({ | ||||
| 		}, | ||||
|  | ||||
| 		onChangeForm(item) { | ||||
| 			this.connection.send({ | ||||
| 				type: 'update-form', | ||||
| 			this.connection.send('updateForm', { | ||||
| 				id: item.id, | ||||
| 				value: item.value | ||||
| 			}); | ||||
| @@ -238,9 +232,9 @@ export default Vue.extend({ | ||||
| 			const y = Math.floor(pos / this.game.settings.map[0].length); | ||||
| 			const newPixel = | ||||
| 				pixel == ' ' ? '-' : | ||||
| 					pixel == '-' ? 'b' : | ||||
| 						pixel == 'b' ? 'w' : | ||||
| 							' '; | ||||
| 				pixel == '-' ? 'b' : | ||||
| 				pixel == 'b' ? 'w' : | ||||
| 				' '; | ||||
| 			const line = this.game.settings.map[y].split(''); | ||||
| 			line[x] = newPixel; | ||||
| 			this.$set(this.game.settings.map, y, line.join('')); | ||||
| @@ -252,11 +246,9 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .urbixznjwwuukfsckrwzwsqzsxornqij | ||||
| 	text-align center | ||||
| 	background isDark ? #191b22 : #f9f9f9 | ||||
| 	background var(--bg) | ||||
|  | ||||
| 	> header | ||||
| 		padding 8px | ||||
| @@ -273,10 +265,10 @@ root(isDark) | ||||
| 					> select | ||||
| 						width 100% | ||||
| 						padding 12px 14px | ||||
| 						background isDark ? #282C37 : #fff | ||||
| 						border 1px solid isDark ? #6a707d : #dcdfe6 | ||||
| 						background var(--face) | ||||
| 						border 1px solid var(--reversiMapSelectBorder) | ||||
| 						border-radius 4px | ||||
| 						color isDark ? #fff : #606266 | ||||
| 						color var(--text) | ||||
| 						cursor pointer | ||||
| 						transition border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) | ||||
| 						-webkit-appearance none | ||||
| @@ -284,17 +276,18 @@ root(isDark) | ||||
| 						appearance none | ||||
|  | ||||
| 						&:hover | ||||
| 							border-color isDark ? #a7aebd : #c0c4cc | ||||
| 							border-color var(--reversiMapSelectHoverBorder) | ||||
|  | ||||
| 						&:focus | ||||
| 						&:active | ||||
| 							border-color $theme-color | ||||
| 							border-color var(--primary) | ||||
|  | ||||
| 				> div | ||||
| 					> .random | ||||
| 						padding 32px 0 | ||||
| 						font-size 64px | ||||
| 						color isDark ? #4e5961 : #d8d8d8 | ||||
| 						color var(--text) | ||||
| 						opacity 0.7 | ||||
|  | ||||
| 					> .board | ||||
| 						display grid | ||||
| @@ -302,11 +295,11 @@ root(isDark) | ||||
| 						width 300px | ||||
| 						height 300px | ||||
| 						margin 0 auto | ||||
| 						color isDark ? #fff : #444 | ||||
| 						color var(--text) | ||||
|  | ||||
| 						> div | ||||
| 							background transparent | ||||
| 							border solid 2px isDark ? #6a767f : #ddd | ||||
| 							border solid 2px var(--faceDivider) | ||||
| 							border-radius 6px | ||||
| 							overflow hidden | ||||
| 							cursor pointer | ||||
| @@ -331,32 +324,26 @@ root(isDark) | ||||
| 		.card | ||||
| 			max-width 400px | ||||
| 			border-radius 4px | ||||
| 			background isDark ? #282C37 : #fff | ||||
| 			color isDark ? #fff : #303133 | ||||
| 			box-shadow 0 2px 12px 0 rgba(#000, isDark ? 0.7 : 0.1) | ||||
| 			background var(--face) | ||||
| 			color var(--text) | ||||
| 			box-shadow 0 2px 12px 0 var(--reversiRoomFormShadow) | ||||
|  | ||||
| 			> header | ||||
| 				padding 18px 20px | ||||
| 				border-bottom 1px solid isDark ? #1c2023 : #ebeef5 | ||||
| 				border-bottom 1px solid var(--faceDivider) | ||||
|  | ||||
| 			> div | ||||
| 				padding 20px | ||||
| 				color isDark ? #fff : #606266 | ||||
| 				color var(--text) | ||||
|  | ||||
| 	> footer | ||||
| 		position sticky | ||||
| 		bottom 0 | ||||
| 		padding 16px | ||||
| 		background rgba(isDark ? #191b22 : #fff, 0.9) | ||||
| 		border-top solid 1px isDark ? #606266 : #c4cdd4 | ||||
| 		background var(--reversiRoomFooterBg) | ||||
| 		border-top solid 1px var(--faceDivider) | ||||
|  | ||||
| 		> .status | ||||
| 			margin 0 0 16px 0 | ||||
|  | ||||
| .urbixznjwwuukfsckrwzwsqzsxornqij[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .urbixznjwwuukfsckrwzwsqzsxornqij:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -47,7 +47,6 @@ export default Vue.extend({ | ||||
| 			game: null, | ||||
| 			matching: null, | ||||
| 			connection: null, | ||||
| 			connectionId: null, | ||||
| 			pingClock: null | ||||
| 		}; | ||||
| 	}, | ||||
| @@ -66,15 +65,13 @@ export default Vue.extend({ | ||||
| 		this.fetch(); | ||||
|  | ||||
| 		if (this.$store.getters.isSignedIn) { | ||||
| 			this.connection = (this as any).os.streams.reversiStream.getConnection(); | ||||
| 			this.connectionId = (this as any).os.streams.reversiStream.use(); | ||||
| 			this.connection = (this as any).os.stream.useSharedConnection('gamesReversi'); | ||||
|  | ||||
| 			this.connection.on('matched', this.onMatched); | ||||
|  | ||||
| 			this.pingClock = setInterval(() => { | ||||
| 				if (this.matching) { | ||||
| 					this.connection.send({ | ||||
| 						type: 'ping', | ||||
| 					this.connection.send('ping', { | ||||
| 						id: this.matching.id | ||||
| 					}); | ||||
| 				} | ||||
| @@ -84,9 +81,7 @@ export default Vue.extend({ | ||||
|  | ||||
| 	beforeDestroy() { | ||||
| 		if (this.connection) { | ||||
| 			this.connection.off('matched', this.onMatched); | ||||
| 			(this as any).os.streams.reversiStream.dispose(this.connectionId); | ||||
|  | ||||
| 			this.connection.dispose(); | ||||
| 			clearInterval(this.pingClock); | ||||
| 		} | ||||
| 	}, | ||||
| @@ -156,11 +151,9 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| 	color isDark ? #fff : #677f84 | ||||
| 	background isDark ? #191b22 : #fff | ||||
| .vchtoekanapleubgzioubdtmlkribzfd | ||||
| 	color var(--text) | ||||
| 	background var(--bg) | ||||
|  | ||||
| 	> .matching | ||||
| 		> h1 | ||||
| @@ -177,10 +170,4 @@ root(isDark) | ||||
| 			text-align center | ||||
| 			border-top dashed 1px #c4cdd4 | ||||
|  | ||||
| .vchtoekanapleubgzioubdtmlkribzfd[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .vchtoekanapleubgzioubdtmlkribzfd:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -26,7 +26,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| root(isDark) | ||||
| .mk-google | ||||
| 	display flex | ||||
| 	margin 8px 0 | ||||
|  | ||||
| @@ -37,31 +37,25 @@ root(isDark) | ||||
| 		height 40px | ||||
| 		font-family sans-serif | ||||
| 		font-size 16px | ||||
| 		color isDark ? #dee4e8 : #55595c | ||||
| 		background isDark ? #191b22 : #fff | ||||
| 		border solid 1px isDark ? #495156 : #dadada | ||||
| 		color var(--googleSearchFg) | ||||
| 		background var(--googleSearchBg) | ||||
| 		border solid 1px var(--googleSearchBorder) | ||||
| 		border-radius 4px 0 0 4px | ||||
|  | ||||
| 		&:hover | ||||
| 			border-color isDark ? #777c86 : #b0b0b0 | ||||
| 			border-color var(--googleSearchHoverBorder) | ||||
|  | ||||
| 	> button | ||||
| 		flex-shrink 0 | ||||
| 		padding 0 16px | ||||
| 		border solid 1px isDark ? #495156 : #dadada | ||||
| 		border solid 1px var(--googleSearchBorder) | ||||
| 		border-left none | ||||
| 		border-radius 0 4px 4px 0 | ||||
|  | ||||
| 		&:hover | ||||
| 			background-color isDark ? #2e3440 : #eee | ||||
| 			background-color var(--googleSearchHoverButton) | ||||
|  | ||||
| 		&:active | ||||
| 			box-shadow 0 2px 4px rgba(#000, 0.15) inset | ||||
|  | ||||
| .mk-google[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-google:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| import theme from './theme.vue'; | ||||
| import instance from './instance.vue'; | ||||
| import cwButton from './cw-button.vue'; | ||||
| import tagCloud from './tag-cloud.vue'; | ||||
| import trends from './trends.vue'; | ||||
| @@ -29,7 +31,6 @@ import messagingRoom from './messaging-room.vue'; | ||||
| import urlPreview from './url-preview.vue'; | ||||
| import twitterSetting from './twitter-setting.vue'; | ||||
| import fileTypeIcon from './file-type-icon.vue'; | ||||
| import Switch from './switch.vue'; | ||||
| import Reversi from './games/reversi/reversi.vue'; | ||||
| import welcomeTimeline from './welcome-timeline.vue'; | ||||
| import uiInput from './ui/input.vue'; | ||||
| @@ -43,6 +44,8 @@ import uiSelect from './ui/select.vue'; | ||||
| import formButton from './ui/form/button.vue'; | ||||
| import formRadio from './ui/form/radio.vue'; | ||||
|  | ||||
| Vue.component('mk-theme', theme); | ||||
| Vue.component('mk-instance', instance); | ||||
| Vue.component('mk-cw-button', cwButton); | ||||
| Vue.component('mk-tag-cloud', tagCloud); | ||||
| Vue.component('mk-trends', trends); | ||||
| @@ -72,7 +75,6 @@ Vue.component('mk-messaging-room', messagingRoom); | ||||
| Vue.component('mk-url-preview', urlPreview); | ||||
| Vue.component('mk-twitter-setting', twitterSetting); | ||||
| Vue.component('mk-file-type-icon', fileTypeIcon); | ||||
| Vue.component('mk-switch', Switch); | ||||
| Vue.component('mk-reversi', Reversi); | ||||
| Vue.component('mk-welcome-timeline', welcomeTimeline); | ||||
| Vue.component('ui-input', uiInput); | ||||
|   | ||||
							
								
								
									
										51
									
								
								src/client/app/common/views/components/instance.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/client/app/common/views/components/instance.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| <template> | ||||
| <div class="nhasjydimbopojusarffqjyktglcuxjy" v-if="meta"> | ||||
| 	<div class="banner" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"></div> | ||||
|  | ||||
| 	<h1>{{ meta.name }}</h1> | ||||
| 	<p v-html="meta.description || '%i18n:common.about%'"></p> | ||||
| 	<router-link to="/">%i18n:@start%</router-link> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| export default Vue.extend({ | ||||
| 	data() { | ||||
| 		return { | ||||
| 			meta: null | ||||
| 		} | ||||
| 	}, | ||||
| 	created() { | ||||
| 		(this as any).os.getMeta().then(meta => { | ||||
| 			this.meta = meta; | ||||
| 		}); | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| .nhasjydimbopojusarffqjyktglcuxjy | ||||
| 	color var(--text) | ||||
| 	background var(--face) | ||||
| 	text-align center | ||||
|  | ||||
| 	> .banner | ||||
| 		height 100px | ||||
| 		background-position center | ||||
| 		background-size cover | ||||
|  | ||||
| 	> h1 | ||||
| 		margin 16px | ||||
| 		font-size 16px | ||||
|  | ||||
| 	> p | ||||
| 		margin 16px | ||||
| 		font-size 14px | ||||
|  | ||||
| 	> a | ||||
| 		display block | ||||
| 		padding-bottom 16px | ||||
|  | ||||
| </style> | ||||
| @@ -43,7 +43,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| root(isDark) | ||||
| .mk-media-banner | ||||
| 	width 100% | ||||
| 	border-radius 4px | ||||
| 	margin-top 4px | ||||
| @@ -71,7 +71,7 @@ root(isDark) | ||||
| 			font-size 1.6em | ||||
|  | ||||
| 	> .download | ||||
| 		background isDark ? #21242d : #f7f7f7 | ||||
| 		background var(--noteAttachedFile) | ||||
|  | ||||
| 	> .sensitive | ||||
| 		background #111 | ||||
| @@ -82,9 +82,4 @@ root(isDark) | ||||
| 			display block | ||||
| 			width 100% | ||||
|  | ||||
| .mk-media-banner[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-media-banner:not([data-darkmode]) | ||||
| 	root(false) | ||||
| </style> | ||||
|   | ||||
| @@ -2,9 +2,9 @@ | ||||
| <div class="onchrpzrvnoruiaenfcqvccjfuupzzwv"> | ||||
| 	<div class="backdrop" ref="backdrop" @click="close"></div> | ||||
| 	<div class="popover" :class="{ hukidasi }" ref="popover"> | ||||
| 		<template v-for="item in items"> | ||||
| 		<template v-for="item, i in items"> | ||||
| 			<div v-if="item === null"></div> | ||||
| 			<button v-if="item" @click="clicked(item.action)" v-html="item.icon ? item.icon + ' ' + item.text : item.text"></button> | ||||
| 			<button v-if="item" @click="clicked(item.action)" v-html="item.icon ? item.icon + ' ' + item.text : item.text" :tabindex="i"></button> | ||||
| 		</template> | ||||
| 	</div> | ||||
| </div> | ||||
| @@ -117,10 +117,8 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| 	$bg-color = isDark ? #2c303c : #fff | ||||
| .onchrpzrvnoruiaenfcqvccjfuupzzwv | ||||
| 	$bg-color = var(--popupBg) | ||||
| 	$border-color = rgba(27, 31, 35, 0.15) | ||||
|  | ||||
| 	position initial | ||||
| @@ -132,7 +130,7 @@ root(isDark) | ||||
| 		z-index 10000 | ||||
| 		width 100% | ||||
| 		height 100% | ||||
| 		background rgba(#000, isDark ? 0.5 : 0.1) | ||||
| 		background var(--modalBackdrop) | ||||
| 		opacity 0 | ||||
|  | ||||
| 	> .popover | ||||
| @@ -179,26 +177,20 @@ root(isDark) | ||||
| 			display block | ||||
| 			padding 8px 16px | ||||
| 			width 100% | ||||
| 			color isDark ? #d6dce2 : #111 | ||||
| 			color var(--popupFg) | ||||
|  | ||||
| 			&:hover | ||||
| 				color $theme-color-foreground | ||||
| 				background $theme-color | ||||
| 				color var(--primaryForeground) | ||||
| 				background var(--primary) | ||||
| 				text-decoration none | ||||
|  | ||||
| 			&:active | ||||
| 				color $theme-color-foreground | ||||
| 				background darken($theme-color, 10%) | ||||
| 				color var(--primaryForeground) | ||||
| 				background var(--primaryDarken10) | ||||
|  | ||||
| 		> div | ||||
| 			margin 8px 0 | ||||
| 			height 1px | ||||
| 			background isDark ? #1c2023 : #eee | ||||
|  | ||||
| .onchrpzrvnoruiaenfcqvccjfuupzzwv[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .onchrpzrvnoruiaenfcqvccjfuupzzwv:not([data-darkmode]) | ||||
| 	root(false) | ||||
| 			background var(--faceDivider) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -195,9 +195,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .mk-messaging-form | ||||
| 	> textarea | ||||
| 		cursor auto | ||||
| 		display block | ||||
| @@ -209,10 +207,10 @@ root(isDark) | ||||
| 		padding 8px | ||||
| 		resize none | ||||
| 		font-size 1em | ||||
| 		color isDark ? #fff : #000 | ||||
| 		color var(--inputText) | ||||
| 		outline none | ||||
| 		border none | ||||
| 		border-top solid 1px isDark ? #4b5056 : #eee | ||||
| 		border-top solid 1px var(--faceDivider) | ||||
| 		border-radius 0 | ||||
| 		box-shadow none | ||||
| 		background transparent | ||||
| @@ -234,10 +232,10 @@ root(isDark) | ||||
| 		transition color 0.1s ease | ||||
|  | ||||
| 		&:hover | ||||
| 			color $theme-color | ||||
| 			color var(--primary) | ||||
|  | ||||
| 		&:active | ||||
| 			color darken($theme-color, 10%) | ||||
| 			color var(--primaryDarken10) | ||||
| 			transition color 0s ease | ||||
|  | ||||
| 	.files | ||||
| @@ -293,19 +291,13 @@ root(isDark) | ||||
| 		transition color 0.1s ease | ||||
|  | ||||
| 		&:hover | ||||
| 			color $theme-color | ||||
| 			color var(--primary) | ||||
|  | ||||
| 		&:active | ||||
| 			color darken($theme-color, 10%) | ||||
| 			color var(--primaryDarken10) | ||||
| 			transition color 0s ease | ||||
|  | ||||
| 	input[type=file] | ||||
| 		display none | ||||
|  | ||||
| .mk-messaging-form[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-messaging-form:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -59,10 +59,8 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| 	$me-balloon-color = $theme-color | ||||
| .message | ||||
| 	$me-balloon-color = var(--primary) | ||||
|  | ||||
| 	padding 10px 12px 10px 12px | ||||
| 	background-color transparent | ||||
| @@ -179,7 +177,7 @@ root(isDark) | ||||
| 			display block | ||||
| 			margin 2px 0 0 0 | ||||
| 			font-size 10px | ||||
| 			color isDark ? rgba(#fff, 0.4) : rgba(#000, 0.4) | ||||
| 			color var(--messagingRoomMessageInfo) | ||||
|  | ||||
| 			> [data-fa] | ||||
| 				margin-left 4px | ||||
| @@ -192,7 +190,7 @@ root(isDark) | ||||
| 			padding-left 66px | ||||
|  | ||||
| 			> .balloon | ||||
| 				$color = isDark ? #2d3338 : #eee | ||||
| 				$color = var(--messagingRoomMessageBg) | ||||
| 				float left | ||||
| 				background $color | ||||
|  | ||||
| @@ -208,8 +206,7 @@ root(isDark) | ||||
|  | ||||
| 				> .content | ||||
| 					> .text | ||||
| 						if isDark | ||||
| 							color #fff | ||||
| 							color var(--messagingRoomMessageFg) | ||||
|  | ||||
| 			> footer | ||||
| 				text-align left | ||||
| @@ -250,18 +247,9 @@ root(isDark) | ||||
|  | ||||
| 				> .read | ||||
| 					user-select none | ||||
| 					margin 0 4px 0 0 | ||||
| 					color isDark ? rgba(#fff, 0.5) : rgba(#000, 0.5) | ||||
| 					font-size 11px | ||||
|  | ||||
| 	&[data-is-deleted] | ||||
| 		> .balloon | ||||
| 			opacity 0.5 | ||||
|  | ||||
| .message[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .message:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -30,7 +30,6 @@ | ||||
|  | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import { MessagingStream } from '../../scripts/streaming/messaging'; | ||||
| import XMessage from './messaging-room.message.vue'; | ||||
| import XForm from './messaging-room.form.vue'; | ||||
| import { url } from '../../../config'; | ||||
| @@ -72,7 +71,7 @@ export default Vue.extend({ | ||||
| 	}, | ||||
|  | ||||
| 	mounted() { | ||||
| 		this.connection = new MessagingStream((this as any).os, this.$store.state.i, this.user.id); | ||||
| 		this.connection =((this as any).os.stream.connectToChannel('messaging', { otherparty: this.user.id }); | ||||
|  | ||||
| 		this.connection.on('message', this.onMessage); | ||||
| 		this.connection.on('read', this.onRead); | ||||
| @@ -92,9 +91,7 @@ export default Vue.extend({ | ||||
| 	}, | ||||
|  | ||||
| 	beforeDestroy() { | ||||
| 		this.connection.off('message', this.onMessage); | ||||
| 		this.connection.off('read', this.onRead); | ||||
| 		this.connection.close(); | ||||
| 		this.connection.dispose(); | ||||
|  | ||||
| 		if (this.isNaked) { | ||||
| 			window.removeEventListener('scroll', this.onScroll); | ||||
| @@ -177,8 +174,7 @@ export default Vue.extend({ | ||||
|  | ||||
| 			this.messages.push(message); | ||||
| 			if (message.userId != this.$store.state.i.id && !document.hidden) { | ||||
| 				this.connection.send({ | ||||
| 					type: 'read', | ||||
| 				this.connection.send('read', { | ||||
| 					id: message.id | ||||
| 				}); | ||||
| 			} | ||||
| @@ -250,8 +246,7 @@ export default Vue.extend({ | ||||
| 			if (document.hidden) return; | ||||
| 			this.messages.forEach(message => { | ||||
| 				if (message.userId !== this.$store.state.i.id && !message.isRead) { | ||||
| 					this.connection.send({ | ||||
| 						type: 'read', | ||||
| 					this.connection.send('read', { | ||||
| 						id: message.id | ||||
| 					}); | ||||
| 				} | ||||
| @@ -262,14 +257,12 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .mk-messaging-room | ||||
| 	display flex | ||||
| 	flex 1 | ||||
| 	flex-direction column | ||||
| 	height 100% | ||||
| 	background isDark ? #191b22 : #fff | ||||
| 	background var(--messagingRoomBg) | ||||
|  | ||||
| 	> .body | ||||
| 		width 100% | ||||
| @@ -277,24 +270,15 @@ root(isDark) | ||||
| 		margin 0 auto | ||||
| 		flex 1 | ||||
|  | ||||
| 		> .init | ||||
| 			width 100% | ||||
| 			margin 0 | ||||
| 			padding 16px 8px 8px 8px | ||||
| 			text-align center | ||||
| 			font-size 0.8em | ||||
| 			color rgba(isDark ? #fff : #000, 0.4) | ||||
|  | ||||
| 			[data-fa] | ||||
| 				margin-right 4px | ||||
|  | ||||
| 		> .init, | ||||
| 		> .empty | ||||
| 			width 100% | ||||
| 			margin 0 | ||||
| 			padding 16px 8px 8px 8px | ||||
| 			text-align center | ||||
| 			font-size 0.8em | ||||
| 			color rgba(isDark ? #fff : #000, 0.4) | ||||
| 			color var(--messagingRoomInfo) | ||||
| 			opacity 0.5 | ||||
|  | ||||
| 			[data-fa] | ||||
| 				margin-right 4px | ||||
| @@ -305,7 +289,8 @@ root(isDark) | ||||
| 			padding 16px | ||||
| 			text-align center | ||||
| 			font-size 0.8em | ||||
| 			color rgba(isDark ? #fff : #000, 0.4) | ||||
| 			color var(--messagingRoomInfo) | ||||
| 			opacity 0.5 | ||||
|  | ||||
| 			[data-fa] | ||||
| 				margin-right 4px | ||||
| @@ -349,7 +334,7 @@ root(isDark) | ||||
| 				left 0 | ||||
| 				right 0 | ||||
| 				margin 0 auto | ||||
| 				background rgba(isDark ? #fff : #000, 0.1) | ||||
| 				background var(--messagingRoomDateDividerLine) | ||||
|  | ||||
| 			> span | ||||
| 				display inline-block | ||||
| @@ -357,8 +342,8 @@ root(isDark) | ||||
| 				padding 0 16px | ||||
| 				//font-weight bold | ||||
| 				line-height 32px | ||||
| 				color rgba(isDark ? #fff : #000, 0.3) | ||||
| 				background isDark ? #191b22 : #fff | ||||
| 				color var(--messagingRoomDateDividerText) | ||||
| 				background var(--messagingRoomBg) | ||||
|  | ||||
| 	> footer | ||||
| 		position -webkit-sticky | ||||
| @@ -369,7 +354,7 @@ root(isDark) | ||||
| 		max-width 600px | ||||
| 		margin 0 auto | ||||
| 		padding 0 | ||||
| 		background rgba(isDark ? #282c37 : #fff, 0.95) | ||||
| 		//background rgba(var(--face), 0.95) | ||||
| 		background-clip content-box | ||||
|  | ||||
| 		> .new-message | ||||
| @@ -386,15 +371,15 @@ root(isDark) | ||||
| 				cursor pointer | ||||
| 				line-height 32px | ||||
| 				font-size 12px | ||||
| 				color $theme-color-foreground | ||||
| 				background $theme-color | ||||
| 				color var(--primaryForeground) | ||||
| 				background var(--primary) | ||||
| 				border-radius 16px | ||||
|  | ||||
| 				&:hover | ||||
| 					background lighten($theme-color, 10%) | ||||
| 					background var(--primaryLighten10) | ||||
|  | ||||
| 				&:active | ||||
| 					background darken($theme-color, 10%) | ||||
| 					background var(--primaryDarken10) | ||||
|  | ||||
| 				> [data-fa] | ||||
| 					position absolute | ||||
| @@ -410,10 +395,4 @@ root(isDark) | ||||
| 	transition opacity 0.5s | ||||
| 	opacity 0 | ||||
|  | ||||
| .mk-messaging-room[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-messaging-room:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -71,13 +71,11 @@ export default Vue.extend({ | ||||
| 			messages: [], | ||||
| 			q: null, | ||||
| 			result: [], | ||||
| 			connection: null, | ||||
| 			connectionId: null | ||||
| 			connection: null | ||||
| 		}; | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		this.connection = (this as any).os.streams.messagingIndexStream.getConnection(); | ||||
| 		this.connectionId = (this as any).os.streams.messagingIndexStream.use(); | ||||
| 		this.connection = (this as any).os.stream.useSharedConnection('messagingIndex'); | ||||
|  | ||||
| 		this.connection.on('message', this.onMessage); | ||||
| 		this.connection.on('read', this.onRead); | ||||
| @@ -88,9 +86,7 @@ export default Vue.extend({ | ||||
| 		}); | ||||
| 	}, | ||||
| 	beforeDestroy() { | ||||
| 		this.connection.off('message', this.onMessage); | ||||
| 		this.connection.off('read', this.onRead); | ||||
| 		(this as any).os.streams.messagingIndexStream.dispose(this.connectionId); | ||||
| 		this.connection.dispose(); | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		getAcct, | ||||
| @@ -167,9 +163,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .mk-messaging | ||||
|  | ||||
| 	&[data-compact] | ||||
| 		font-size 0.8em | ||||
| @@ -204,12 +198,10 @@ root(isDark) | ||||
| 		left 0 | ||||
| 		z-index 1 | ||||
| 		width 100% | ||||
| 		background #fff | ||||
| 		box-shadow 0 0px 2px rgba(#000, 0.2) | ||||
|  | ||||
| 		> .form | ||||
| 			padding 8px | ||||
| 			background isDark ? #282c37 : #f7f7f7 | ||||
| 			background rgba(0, 0, 0, 0.02) | ||||
|  | ||||
| 			> label | ||||
| 				display block | ||||
| @@ -229,32 +221,22 @@ root(isDark) | ||||
| 					bottom 0 | ||||
| 					left 0 | ||||
| 					width 1em | ||||
| 					line-height 56px | ||||
| 					line-height 48px | ||||
| 					margin auto | ||||
| 					color #555 | ||||
|  | ||||
| 			> input | ||||
| 				margin 0 | ||||
| 				padding 0 0 0 32px | ||||
| 				padding 0 0 0 42px | ||||
| 				width 100% | ||||
| 				font-size 1em | ||||
| 				line-height 38px | ||||
| 				color #000 | ||||
| 				line-height 48px | ||||
| 				color var(--faceText) | ||||
| 				outline none | ||||
| 				background isDark ? #191b22 : #fff | ||||
| 				border solid 1px isDark ? #495156 : #eee | ||||
| 				background transparent | ||||
| 				border none | ||||
| 				border-radius 5px | ||||
| 				box-shadow none | ||||
| 				transition color 0.5s ease, border 0.5s ease | ||||
|  | ||||
| 				&:hover | ||||
| 					border solid 1px isDark ? #b0b0b0 : #ddd | ||||
| 					transition border 0.2s ease | ||||
|  | ||||
| 				&:focus | ||||
| 					color darken($theme-color, 20%) | ||||
| 					border solid 1px $theme-color | ||||
| 					transition color 0, border 0 | ||||
|  | ||||
| 		> .result | ||||
| 			display block | ||||
| @@ -287,7 +269,7 @@ root(isDark) | ||||
| 					&:hover | ||||
| 					&:focus | ||||
| 						color #fff | ||||
| 						background $theme-color | ||||
| 						background var(--primary) | ||||
|  | ||||
| 						.name | ||||
| 							color #fff | ||||
| @@ -297,7 +279,7 @@ root(isDark) | ||||
|  | ||||
| 					&:active | ||||
| 						color #fff | ||||
| 						background darken($theme-color, 10%) | ||||
| 						background var(--primaryDarken10) | ||||
|  | ||||
| 						.name | ||||
| 							color #fff | ||||
| @@ -329,21 +311,21 @@ root(isDark) | ||||
| 		> a | ||||
| 			display block | ||||
| 			text-decoration none | ||||
| 			background isDark ? #282c37 : #fff | ||||
| 			border-bottom solid 1px isDark ? #1c2023 : #eee | ||||
| 			background var(--face) | ||||
| 			border-bottom solid 1px var(--faceDivider) | ||||
|  | ||||
| 			* | ||||
| 				pointer-events none | ||||
| 				user-select none | ||||
|  | ||||
| 			&:hover | ||||
| 				background isDark ? #1e2129 : #fafafa | ||||
| 				box-shadow 0 0 0 100px inset rgba(0, 0, 0, 0.05) | ||||
|  | ||||
| 				> .avatar | ||||
| 				.avatar | ||||
| 					filter saturate(200%) | ||||
|  | ||||
| 			&:active | ||||
| 				background isDark ? #14161b : #eee | ||||
| 				box-shadow 0 0 0 100px inset rgba(0, 0, 0, 0.1) | ||||
|  | ||||
| 			&[data-is-read] | ||||
| 			&[data-is-me] | ||||
| @@ -383,17 +365,17 @@ root(isDark) | ||||
| 						overflow hidden | ||||
| 						text-overflow ellipsis | ||||
| 						font-size 1em | ||||
| 						color isDark ? #fff : rgba(#000, 0.9) | ||||
| 						color var(--noteHeaderName) | ||||
| 						font-weight bold | ||||
| 						transition all 0.1s ease | ||||
|  | ||||
| 					> .username | ||||
| 						margin 0 8px | ||||
| 						color isDark ? #606984 : rgba(#000, 0.5) | ||||
| 						color var(--noteHeaderAcct) | ||||
|  | ||||
| 					> .mk-time | ||||
| 						margin 0 0 0 auto | ||||
| 						color isDark ? #606984 : rgba(#000, 0.5) | ||||
| 						color var(--noteHeaderInfo) | ||||
| 						font-size 80% | ||||
|  | ||||
| 				> .avatar | ||||
| @@ -413,10 +395,10 @@ root(isDark) | ||||
| 						overflow hidden | ||||
| 						overflow-wrap break-word | ||||
| 						font-size 1.1em | ||||
| 						color isDark ? #fff : rgba(#000, 0.8) | ||||
| 						color var(--faceText) | ||||
|  | ||||
| 						.me | ||||
| 							color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.4) | ||||
| 							opacity 0.7 | ||||
|  | ||||
| 					> .image | ||||
| 						display block | ||||
| @@ -461,10 +443,4 @@ root(isDark) | ||||
| 					> .avatar | ||||
| 						margin 0 12px 0 0 | ||||
|  | ||||
| .mk-messaging[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-messaging:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -95,7 +95,8 @@ export default Vue.component('misskey-flavored-markdown', { | ||||
| 					return [createElement(MkUrl, { | ||||
| 						props: { | ||||
| 							url: token.content, | ||||
| 							target: '_blank' | ||||
| 							target: '_blank', | ||||
| 							style: 'color:var(--mfmLink);' | ||||
| 						} | ||||
| 					})]; | ||||
| 				} | ||||
| @@ -106,7 +107,8 @@ export default Vue.component('misskey-flavored-markdown', { | ||||
| 							class: 'link', | ||||
| 							href: token.url, | ||||
| 							target: '_blank', | ||||
| 							title: token.url | ||||
| 							title: token.url, | ||||
| 							style: 'color:var(--mfmLink);' | ||||
| 						} | ||||
| 					}, token.title)]; | ||||
| 				} | ||||
| @@ -116,7 +118,8 @@ export default Vue.component('misskey-flavored-markdown', { | ||||
| 						attrs: { | ||||
| 							href: `${url}/@${getAcct(token)}`, | ||||
| 							target: '_blank', | ||||
| 							dataIsMe: (this as any).i && getAcct((this as any).i) == getAcct(token) | ||||
| 							dataIsMe: (this as any).i && getAcct((this as any).i) == getAcct(token), | ||||
| 							style: 'color:var(--mfmMention);' | ||||
| 						}, | ||||
| 						directives: [{ | ||||
| 							name: 'user-preview', | ||||
| @@ -129,7 +132,8 @@ export default Vue.component('misskey-flavored-markdown', { | ||||
| 					return [createElement('a', { | ||||
| 						attrs: { | ||||
| 							href: `${url}/tags/${encodeURIComponent(token.hashtag)}`, | ||||
| 							target: '_blank' | ||||
| 							target: '_blank', | ||||
| 							style: 'color:var(--mfmHashtag);' | ||||
| 						} | ||||
| 					}, token.content)]; | ||||
| 				} | ||||
|   | ||||
| @@ -2,6 +2,8 @@ | ||||
| <span class="mk-nav"> | ||||
| 	<a :href="aboutUrl">%i18n:@about%</a> | ||||
| 	<i>・</i> | ||||
| 	<a href="/stats">%i18n:@stats%</a> | ||||
| 	<i>・</i> | ||||
| 	<a :href="repositoryUrl">%i18n:@repository%</a> | ||||
| 	<i>・</i> | ||||
| 	<a :href="feedbackUrl" target="_blank">%i18n:@feedback%</a> | ||||
|   | ||||
| @@ -42,9 +42,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .bvonvjxbwzaiskogyhbwgyxvcgserpmu | ||||
| 	display flex | ||||
| 	align-items baseline | ||||
| 	white-space nowrap | ||||
| @@ -61,7 +59,7 @@ root(isDark) | ||||
| 		margin 0 .5em 0 0 | ||||
| 		padding 0 | ||||
| 		overflow hidden | ||||
| 		color isDark ? #fff : #627079 | ||||
| 		color var(--noteHeaderName) | ||||
| 		font-size 1em | ||||
| 		font-weight bold | ||||
| 		text-decoration none | ||||
| @@ -82,19 +80,19 @@ root(isDark) | ||||
| 		margin 0 .5em 0 0 | ||||
| 		padding 1px 6px | ||||
| 		font-size 80% | ||||
| 		color isDark ? #758188 : #aaa | ||||
| 		border solid 1px isDark ? #57616f : #ddd | ||||
| 		color var(--noteHeaderBadgeFg) | ||||
| 		background var(--noteHeaderBadgeBg) | ||||
| 		border-radius 3px | ||||
|  | ||||
| 		&.is-admin | ||||
| 			border-color isDark ? #d42c41 : #f56a7b | ||||
| 			color isDark ? #d42c41 : #f56a7b | ||||
| 			background var(--noteHeaderAdminBg) | ||||
| 			color var(--noteHeaderAdminFg) | ||||
|  | ||||
| 	> .username | ||||
| 		margin 0 .5em 0 0 | ||||
| 		overflow hidden | ||||
| 		text-overflow ellipsis | ||||
| 		color isDark ? #606984 : #ccc | ||||
| 		color var(--noteHeaderAcct) | ||||
| 		flex-shrink 2147483647 | ||||
|  | ||||
| 	> .info | ||||
| @@ -102,7 +100,7 @@ root(isDark) | ||||
| 		font-size 0.9em | ||||
|  | ||||
| 		> * | ||||
| 			color isDark ? #606984 : #c0c0c0 | ||||
| 			color var(--noteHeaderInfo) | ||||
|  | ||||
| 		> .mobile | ||||
| 			margin-right 8px | ||||
| @@ -110,15 +108,9 @@ root(isDark) | ||||
| 		> .app | ||||
| 			margin-right 8px | ||||
| 			padding-right 8px | ||||
| 			border-right solid 1px isDark ? #1c2023 : #eaeaea | ||||
| 			border-right solid 1px var(--faceDivider) | ||||
|  | ||||
| 		> .visibility | ||||
| 			margin-left 8px | ||||
|  | ||||
| .bvonvjxbwzaiskogyhbwgyxvcgserpmu[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .bvonvjxbwzaiskogyhbwgyxvcgserpmu:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -28,17 +28,29 @@ export default Vue.extend({ | ||||
| 			}]; | ||||
|  | ||||
| 			if (this.note.userId == this.$store.state.i.id) { | ||||
| 				items.push({ | ||||
| 					icon: '%fa:thumbtack%', | ||||
| 					text: '%i18n:@pin%', | ||||
| 					action: this.pin | ||||
| 				}); | ||||
| 				if ((this.$store.state.i.pinnedNoteIds || []).includes(this.note.id)) { | ||||
| 					items.push({ | ||||
| 						icon: '%fa:thumbtack%', | ||||
| 						text: '%i18n:@unpin%', | ||||
| 						action: this.unpin | ||||
| 					}); | ||||
| 				} else { | ||||
| 					items.push({ | ||||
| 						icon: '%fa:thumbtack%', | ||||
| 						text: '%i18n:@pin%', | ||||
| 						action: this.pin | ||||
| 					}); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if (this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin) { | ||||
| 				items.push({ | ||||
| 					icon: '%fa:trash-alt R%', | ||||
| 					text: '%i18n:@delete%', | ||||
| 					action: this.del | ||||
| 				}); | ||||
| 			} | ||||
|  | ||||
| 			if (this.note.uri) { | ||||
| 				items.push({ | ||||
| 					icon: '%fa:external-link-square-alt%', | ||||
| @@ -48,9 +60,11 @@ export default Vue.extend({ | ||||
| 					} | ||||
| 				}); | ||||
| 			} | ||||
|  | ||||
| 			return items; | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		detail() { | ||||
| 			this.$router.push(`/notes/${ this.note.id }`); | ||||
| @@ -68,6 +82,14 @@ export default Vue.extend({ | ||||
| 			}); | ||||
| 		}, | ||||
|  | ||||
| 		unpin() { | ||||
| 			(this as any).api('i/unpin', { | ||||
| 				noteId: this.note.id | ||||
| 			}).then(() => { | ||||
| 				this.destroyDom(); | ||||
| 			}); | ||||
| 		}, | ||||
|  | ||||
| 		del() { | ||||
| 			if (!window.confirm('%i18n:@delete-confirm%')) return; | ||||
| 			(this as any).api('notes/delete', { | ||||
| @@ -81,6 +103,12 @@ export default Vue.extend({ | ||||
| 			(this as any).api('notes/favorites/create', { | ||||
| 				noteId: this.note.id | ||||
| 			}).then(() => { | ||||
| 				this.$swal({ | ||||
| 					type: 'success', | ||||
| 					showConfirmButton: false, | ||||
| 					timer: 1250, | ||||
| 					customClass: 'swal-icon-only' | ||||
| 				}); | ||||
| 				this.destroyDom(); | ||||
| 			}); | ||||
| 		}, | ||||
|   | ||||
| @@ -68,9 +68,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .mk-poll-editor | ||||
| 	padding 8px | ||||
|  | ||||
| 	> .caution | ||||
| @@ -103,49 +101,43 @@ root(isDark) | ||||
| 				padding 6px 8px | ||||
| 				width 300px | ||||
| 				font-size 14px | ||||
| 				color isDark ? #fff : #000 | ||||
| 				background isDark ? #191b22 : #fff | ||||
| 				border solid 1px rgba($theme-color, 0.1) | ||||
| 				color var(--inputText) | ||||
| 				background var(--pollEditorInputBg) | ||||
| 				border solid 1px var(--primaryAlpha01) | ||||
| 				border-radius 4px | ||||
|  | ||||
| 				&:hover | ||||
| 					border-color rgba($theme-color, 0.2) | ||||
| 					border-color var(--primaryAlpha02) | ||||
|  | ||||
| 				&:focus | ||||
| 					border-color rgba($theme-color, 0.5) | ||||
| 					border-color var(--primaryAlpha05) | ||||
|  | ||||
| 			> button | ||||
| 				padding 4px 8px | ||||
| 				color rgba($theme-color, 0.4) | ||||
| 				color var(--primaryAlpha04) | ||||
|  | ||||
| 				&:hover | ||||
| 					color rgba($theme-color, 0.6) | ||||
| 					color var(--primaryAlpha06) | ||||
|  | ||||
| 				&:active | ||||
| 					color darken($theme-color, 30%) | ||||
| 					color var(--primaryDarken30) | ||||
|  | ||||
| 	> .add | ||||
| 		margin 8px 0 0 0 | ||||
| 		vertical-align top | ||||
| 		color $theme-color | ||||
| 		color var(--primary) | ||||
|  | ||||
| 	> .destroy | ||||
| 		position absolute | ||||
| 		top 0 | ||||
| 		right 0 | ||||
| 		padding 4px 8px | ||||
| 		color rgba($theme-color, 0.4) | ||||
| 		color var(--primaryAlpha04) | ||||
|  | ||||
| 		&:hover | ||||
| 			color rgba($theme-color, 0.6) | ||||
| 			color var(--primaryAlpha06) | ||||
|  | ||||
| 		&:active | ||||
| 			color darken($theme-color, 30%) | ||||
|  | ||||
| .mk-poll-editor[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-poll-editor:not([data-darkmode]) | ||||
| 	root(false) | ||||
| 			color var(--primaryDarken30) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -67,10 +67,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
|  | ||||
| .mk-poll | ||||
| 	> ul | ||||
| 		display block | ||||
| 		margin 0 | ||||
| @@ -82,8 +79,8 @@ root(isDark) | ||||
| 			margin 4px 0 | ||||
| 			padding 4px 8px | ||||
| 			width 100% | ||||
| 			color isDark ? #fff : #000 | ||||
| 			border solid 1px isDark ? #5e636f : #eee | ||||
| 			color var(--pollChoiceText) | ||||
| 			border solid 1px var(--pollChoiceBorder) | ||||
| 			border-radius 4px | ||||
| 			overflow hidden | ||||
| 			cursor pointer | ||||
| @@ -99,7 +96,7 @@ root(isDark) | ||||
| 				top 0 | ||||
| 				left 0 | ||||
| 				height 100% | ||||
| 				background $theme-color | ||||
| 				background var(--primary) | ||||
| 				transition width 1s ease | ||||
|  | ||||
| 			> span | ||||
| @@ -110,7 +107,7 @@ root(isDark) | ||||
| 					margin-left 4px | ||||
|  | ||||
| 	> p | ||||
| 		color isDark ? #a3aebf : #000 | ||||
| 		color var(--text) | ||||
|  | ||||
| 		a | ||||
| 			color inherit | ||||
| @@ -125,10 +122,4 @@ root(isDark) | ||||
| 			&:active | ||||
| 				background transparent | ||||
|  | ||||
| .mk-poll[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-poll:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| <template> | ||||
| <div class="mk-reaction-picker"> | ||||
| <div class="mk-reaction-picker" v-hotkey.global="keymap"> | ||||
| 	<div class="backdrop" ref="backdrop" @click="close"></div> | ||||
| 	<div class="popover" :class="{ compact, big }" ref="popover"> | ||||
| 		<p v-if="!compact">{{ title }}</p> | ||||
| 		<div> | ||||
| 		<div ref="buttons" :class="{ showFocus }"> | ||||
| 			<button @click="react('like')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="1" title="%i18n:common.reactions.like%"><mk-reaction-icon reaction='like'/></button> | ||||
| 			<button @click="react('love')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="2" title="%i18n:common.reactions.love%"><mk-reaction-icon reaction='love'/></button> | ||||
| 			<button @click="react('laugh')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="3" title="%i18n:common.reactions.laugh%"><mk-reaction-icon reaction='laugh'/></button> | ||||
| @@ -31,30 +31,84 @@ export default Vue.extend({ | ||||
| 			type: Object, | ||||
| 			required: true | ||||
| 		}, | ||||
|  | ||||
| 		source: { | ||||
| 			required: true | ||||
| 		}, | ||||
|  | ||||
| 		compact: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: false | ||||
| 		}, | ||||
|  | ||||
| 		cb: { | ||||
| 			required: false | ||||
| 		}, | ||||
|  | ||||
| 		big: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: false | ||||
| 		}, | ||||
|  | ||||
| 		showFocus: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: false | ||||
| 		}, | ||||
|  | ||||
| 		animation: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: true | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			title: placeholder | ||||
| 			title: placeholder, | ||||
| 			focus: null | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	computed: { | ||||
| 		keymap(): any { | ||||
| 			return { | ||||
| 				'esc': this.close, | ||||
| 				'enter|space|plus': this.choose, | ||||
| 				'up|k': this.focusUp, | ||||
| 				'left|h|shift+tab': this.focusLeft, | ||||
| 				'right|l|tab': this.focusRight, | ||||
| 				'down|j': this.focusDown, | ||||
| 				'1': () => this.react('like'), | ||||
| 				'2': () => this.react('love'), | ||||
| 				'3': () => this.react('laugh'), | ||||
| 				'4': () => this.react('hmm'), | ||||
| 				'5': () => this.react('surprise'), | ||||
| 				'6': () => this.react('congrats'), | ||||
| 				'7': () => this.react('angry'), | ||||
| 				'8': () => this.react('confused'), | ||||
| 				'9': () => this.react('rip'), | ||||
| 				'0': () => this.react('pudding'), | ||||
| 			}; | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	watch: { | ||||
| 		focus(i) { | ||||
| 			this.$refs.buttons.children[i].focus(); | ||||
|  | ||||
| 			if (this.showFocus) { | ||||
| 				this.title = this.$refs.buttons.children[i].title; | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	mounted() { | ||||
| 		this.$nextTick(() => { | ||||
| 			this.focus = 0; | ||||
|  | ||||
| 			const popover = this.$refs.popover as any; | ||||
|  | ||||
| 			const rect = this.source.getBoundingClientRect(); | ||||
| @@ -76,7 +130,7 @@ export default Vue.extend({ | ||||
| 			anime({ | ||||
| 				targets: this.$refs.backdrop, | ||||
| 				opacity: 1, | ||||
| 				duration: 100, | ||||
| 				duration: this.animation ? 100 : 0, | ||||
| 				easing: 'linear' | ||||
| 			}); | ||||
|  | ||||
| @@ -84,10 +138,11 @@ export default Vue.extend({ | ||||
| 				targets: this.$refs.popover, | ||||
| 				opacity: 1, | ||||
| 				scale: [0.5, 1], | ||||
| 				duration: 500 | ||||
| 				duration: this.animation ? 500 : 0 | ||||
| 			}); | ||||
| 		}); | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		react(reaction) { | ||||
| 			(this as any).api('notes/reactions/create', { | ||||
| @@ -95,21 +150,25 @@ export default Vue.extend({ | ||||
| 				reaction: reaction | ||||
| 			}).then(() => { | ||||
| 				if (this.cb) this.cb(); | ||||
| 				this.$emit('closed'); | ||||
| 				this.destroyDom(); | ||||
| 			}); | ||||
| 		}, | ||||
|  | ||||
| 		onMouseover(e) { | ||||
| 			this.title = e.target.title; | ||||
| 		}, | ||||
|  | ||||
| 		onMouseout(e) { | ||||
| 			this.title = placeholder; | ||||
| 		}, | ||||
|  | ||||
| 		close() { | ||||
| 			(this.$refs.backdrop as any).style.pointerEvents = 'none'; | ||||
| 			anime({ | ||||
| 				targets: this.$refs.backdrop, | ||||
| 				opacity: 0, | ||||
| 				duration: 200, | ||||
| 				duration: this.animation ? 200 : 0, | ||||
| 				easing: 'linear' | ||||
| 			}); | ||||
|  | ||||
| @@ -118,21 +177,42 @@ export default Vue.extend({ | ||||
| 				targets: this.$refs.popover, | ||||
| 				opacity: 0, | ||||
| 				scale: 0.5, | ||||
| 				duration: 200, | ||||
| 				duration: this.animation ? 200 : 0, | ||||
| 				easing: 'easeInBack', | ||||
| 				complete: () => this.destroyDom() | ||||
| 				complete: () => { | ||||
| 					this.$emit('closed'); | ||||
| 					this.destroyDom(); | ||||
| 				} | ||||
| 			}); | ||||
| 		}, | ||||
|  | ||||
| 		focusUp() { | ||||
| 			this.focus = this.focus == 0 ? 9 : this.focus < 5 ? (this.focus + 4) : (this.focus - 5); | ||||
| 		}, | ||||
|  | ||||
| 		focusDown() { | ||||
| 			this.focus = this.focus == 9 ? 0 : this.focus >= 5 ? (this.focus - 4) : (this.focus + 5); | ||||
| 		}, | ||||
|  | ||||
| 		focusRight() { | ||||
| 			this.focus = this.focus == 9 ? 0 : (this.focus + 1); | ||||
| 		}, | ||||
|  | ||||
| 		focusLeft() { | ||||
| 			this.focus = this.focus == 0 ? 9 : (this.focus - 1); | ||||
| 		}, | ||||
|  | ||||
| 		choose() { | ||||
| 			this.$refs.buttons.childNodes[this.focus].click(); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| $border-color = rgba(27, 31, 35, 0.15) | ||||
|  | ||||
| root(isDark) | ||||
| .mk-reaction-picker | ||||
| 	position initial | ||||
|  | ||||
| 	> .backdrop | ||||
| @@ -142,11 +222,11 @@ root(isDark) | ||||
| 		z-index 10000 | ||||
| 		width 100% | ||||
| 		height 100% | ||||
| 		background isDark ? rgba(#000, 0.4) : rgba(#000, 0.1) | ||||
| 		background var(--modalBackdrop) | ||||
| 		opacity 0 | ||||
|  | ||||
| 	> .popover | ||||
| 		$bgcolor = isDark ? #2c303c : #fff | ||||
| 		$bgcolor = var(--popupBg) | ||||
| 		position absolute | ||||
| 		z-index 10001 | ||||
| 		background $bgcolor | ||||
| @@ -199,14 +279,29 @@ root(isDark) | ||||
| 			margin 0 | ||||
| 			padding 8px 10px | ||||
| 			font-size 14px | ||||
| 			color isDark ? #d6dce2 : #586069 | ||||
| 			border-bottom solid 1px isDark ? #1c2023 : #e1e4e8 | ||||
| 			color var(--popupFg) | ||||
| 			border-bottom solid 1px var(--faceDivider) | ||||
|  | ||||
| 		> div | ||||
| 			padding 4px | ||||
| 			width 240px | ||||
| 			text-align center | ||||
|  | ||||
| 			&.showFocus | ||||
| 				> button:focus | ||||
| 					z-index 1 | ||||
|  | ||||
| 					&:after | ||||
| 						content "" | ||||
| 						pointer-events none | ||||
| 						position absolute | ||||
| 						top 0 | ||||
| 						right 0 | ||||
| 						bottom 0 | ||||
| 						left 0 | ||||
| 						border 2px solid var(--primaryAlpha03) | ||||
| 						border-radius 4px | ||||
|  | ||||
| 			> button | ||||
| 				padding 0 | ||||
| 				width 40px | ||||
| @@ -215,16 +310,10 @@ root(isDark) | ||||
| 				border-radius 2px | ||||
|  | ||||
| 				&:hover | ||||
| 					background isDark ? #252731 : #eee | ||||
| 					background var(--reactionPickerButtonHoverBg) | ||||
|  | ||||
| 				&:active | ||||
| 					background $theme-color | ||||
| 					background var(--primary) | ||||
| 					box-shadow inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15) | ||||
|  | ||||
| .mk-reaction-picker[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-reaction-picker:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -1,16 +1,16 @@ | ||||
| <template> | ||||
| <div class="mk-reactions-viewer"> | ||||
| 	<template v-if="reactions"> | ||||
| 		<span :class="{notReacted}" @click="react('like')" v-if="reactions.like"><mk-reaction-icon reaction="like"/><span>{{ reactions.like }}</span></span> | ||||
| 		<span :class="{notReacted}" @click="react('love')" v-if="reactions.love"><mk-reaction-icon reaction="love"/><span>{{ reactions.love }}</span></span> | ||||
| 		<span :class="{notReacted}" @click="react('laugh')" v-if="reactions.laugh"><mk-reaction-icon reaction="laugh"/><span>{{ reactions.laugh }}</span></span> | ||||
| 		<span :class="{notReacted}" @click="react('hmm')" v-if="reactions.hmm"><mk-reaction-icon reaction="hmm"/><span>{{ reactions.hmm }}</span></span> | ||||
| 		<span :class="{notReacted}" @click="react('surprise')" v-if="reactions.surprise"><mk-reaction-icon reaction="surprise"/><span>{{ reactions.surprise }}</span></span> | ||||
| 		<span :class="{notReacted}" @click="react('congrats')" v-if="reactions.congrats"><mk-reaction-icon reaction="congrats"/><span>{{ reactions.congrats }}</span></span> | ||||
| 		<span :class="{notReacted}" @click="react('angry')" v-if="reactions.angry"><mk-reaction-icon reaction="angry"/><span>{{ reactions.angry }}</span></span> | ||||
| 		<span :class="{notReacted}" @click="react('confused')" v-if="reactions.confused"><mk-reaction-icon reaction="confused"/><span>{{ reactions.confused }}</span></span> | ||||
| 		<span :class="{notReacted}" @click="react('rip')" v-if="reactions.rip"><mk-reaction-icon reaction="rip"/><span>{{ reactions.rip }}</span></span> | ||||
| 		<span :class="{notReacted}" @click="react('pudding')" v-if="reactions.pudding"><mk-reaction-icon reaction="pudding"/><span>{{ reactions.pudding }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'like' }" @click="react('like')" v-if="reactions.like"><mk-reaction-icon reaction="like"/><span>{{ reactions.like }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'love' }" @click="react('love')" v-if="reactions.love"><mk-reaction-icon reaction="love"/><span>{{ reactions.love }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'laugh' }" @click="react('laugh')" v-if="reactions.laugh"><mk-reaction-icon reaction="laugh"/><span>{{ reactions.laugh }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'hmm' }" @click="react('hmm')" v-if="reactions.hmm"><mk-reaction-icon reaction="hmm"/><span>{{ reactions.hmm }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'surprise' }" @click="react('surprise')" v-if="reactions.surprise"><mk-reaction-icon reaction="surprise"/><span>{{ reactions.surprise }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'congrats' }" @click="react('congrats')" v-if="reactions.congrats"><mk-reaction-icon reaction="congrats"/><span>{{ reactions.congrats }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'angry' }" @click="react('angry')" v-if="reactions.angry"><mk-reaction-icon reaction="angry"/><span>{{ reactions.angry }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'confused' }" @click="react('confused')" v-if="reactions.confused"><mk-reaction-icon reaction="confused"/><span>{{ reactions.confused }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'rip' }" @click="react('rip')" v-if="reactions.rip"><mk-reaction-icon reaction="rip"/><span>{{ reactions.rip }}</span></span> | ||||
| 		<span :class="{ reacted: note.myReaction == 'pudding' }" @click="react('pudding')" v-if="reactions.pudding"><mk-reaction-icon reaction="pudding"/><span>{{ reactions.pudding }}</span></span> | ||||
| 	</template> | ||||
| </div> | ||||
| </template> | ||||
| @@ -22,9 +22,6 @@ export default Vue.extend({ | ||||
| 	computed: { | ||||
| 		reactions(): number { | ||||
| 			return this.note.reactionCounts; | ||||
| 		}, | ||||
| 		notReacted(): boolean { | ||||
| 			return this.note.myReaction == null; | ||||
| 		} | ||||
| 	}, | ||||
| 	methods: { | ||||
| @@ -39,33 +36,43 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| root(isDark) | ||||
| 	$borderColor = isDark ? #5e6673 : #eee | ||||
| 	border-top dashed 1px $borderColor | ||||
| 	border-bottom dashed 1px $borderColor | ||||
| 	margin 4px 0 | ||||
| .mk-reactions-viewer | ||||
| 	margin 6px 0 | ||||
|  | ||||
| 	&:empty | ||||
| 		display none | ||||
|  | ||||
| 	> span | ||||
| 		margin-right 8px | ||||
| 		display inline-block | ||||
| 		height 32px | ||||
| 		margin-right 6px | ||||
| 		padding 0 6px | ||||
| 		border-radius 4px | ||||
|  | ||||
| 		&.notReacted | ||||
| 		* | ||||
| 			user-select none | ||||
| 			pointer-events none | ||||
|  | ||||
| 		&.reacted | ||||
| 			background var(--primary) | ||||
|  | ||||
| 			> span | ||||
| 				color var(--primaryForeground) | ||||
|  | ||||
| 		&:not(.reacted) | ||||
| 			cursor pointer | ||||
| 			background var(--reactionViewerButtonBg) | ||||
|  | ||||
| 			&:hover | ||||
| 				background var(--reactionViewerButtonHoverBg) | ||||
|  | ||||
| 		> .mk-reaction-icon | ||||
| 			font-size 1.4em | ||||
|  | ||||
| 		> span | ||||
| 			margin-left 4px | ||||
| 			font-size 1.2em | ||||
| 			color isDark ? #d1d5dc : #444 | ||||
|  | ||||
| .mk-reactions-viewer[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-reactions-viewer:not([data-darkmode]) | ||||
| 	root(false) | ||||
| 			font-size 1.1em | ||||
| 			line-height 32px | ||||
| 			vertical-align middle | ||||
| 			color var(--text) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -1,16 +1,16 @@ | ||||
| <template> | ||||
| <form class="mk-signin" :class="{ signing }" @submit.prevent="onSubmit"> | ||||
| 	<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div> | ||||
| 	<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @input="onUsernameChange"> | ||||
| 	<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @input="onUsernameChange" styl="fill"> | ||||
| 		<span>%i18n:@username%</span> | ||||
| 		<span slot="prefix">@</span> | ||||
| 		<span slot="suffix">@{{ host }}</span> | ||||
| 	</ui-input> | ||||
| 	<ui-input v-model="password" type="password" required> | ||||
| 	<ui-input v-model="password" type="password" required styl="fill"> | ||||
| 		<span>%i18n:@password%</span> | ||||
| 		<span slot="prefix">%fa:lock%</span> | ||||
| 	</ui-input> | ||||
| 	<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required/> | ||||
| 	<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required styl="fill"/> | ||||
| 	<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button> | ||||
| 	<p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p> | ||||
| </form> | ||||
| @@ -56,7 +56,7 @@ export default Vue.extend({ | ||||
| 				username: this.username, | ||||
| 				password: this.password, | ||||
| 				token: this.user && this.user.twoFactorEnabled ? this.token : undefined | ||||
| 			}).then(() => { | ||||
| 			}, true).then(() => { | ||||
| 				location.reload(); | ||||
| 			}).catch(() => { | ||||
| 				alert('%i18n:@login-failed%'); | ||||
| @@ -68,7 +68,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
|  | ||||
| .mk-signin | ||||
| 	color #555 | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| <template> | ||||
| <form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()"> | ||||
| 	<template v-if="meta"> | ||||
| 		<ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required> | ||||
| 		<ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required styl="fill"> | ||||
| 			<span>%i18n:@invitation-code%</span> | ||||
| 			<span slot="prefix">%fa:id-card-alt%</span> | ||||
| 			<p slot="text" v-html="'%i18n:@invitation-info%'.replace('{}', meta.maintainer.url)"></p> | ||||
| 		</ui-input> | ||||
| 		<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername"> | ||||
| 		<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername" styl="fill"> | ||||
| 			<span>%i18n:@username%</span> | ||||
| 			<span slot="prefix">@</span> | ||||
| 			<span slot="suffix">@{{ host }}</span> | ||||
| @@ -18,7 +18,7 @@ | ||||
| 			<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p> | ||||
| 			<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p> | ||||
| 		</ui-input> | ||||
| 		<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true"> | ||||
| 		<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true" styl="fill"> | ||||
| 			<span>%i18n:@password%</span> | ||||
| 			<span slot="prefix">%fa:lock%</span> | ||||
| 			<div slot="text"> | ||||
| @@ -27,7 +27,7 @@ | ||||
| 				<p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p> | ||||
| 			</div> | ||||
| 		</ui-input> | ||||
| 		<ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype"> | ||||
| 		<ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype" styl="fill"> | ||||
| 			<span>%i18n:@password% (%i18n:@retype%)</span> | ||||
| 			<span slot="prefix">%fa:lock%</span> | ||||
| 			<div slot="text"> | ||||
| @@ -131,11 +131,11 @@ export default Vue.extend({ | ||||
| 				password: this.password, | ||||
| 				invitationCode: this.invitationCode, | ||||
| 				'g-recaptcha-response': this.meta.recaptchaSitekey != null ? (window as any).grecaptcha.getResponse() : null | ||||
| 			}).then(() => { | ||||
| 			}, true).then(() => { | ||||
| 				(this as any).api('signin', { | ||||
| 					username: this.username, | ||||
| 					password: this.password | ||||
| 				}).then(() => { | ||||
| 				}, true).then(() => { | ||||
| 					location.href = '/'; | ||||
| 				}); | ||||
| 			}).catch(() => { | ||||
| @@ -151,7 +151,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
|  | ||||
| .mk-signup | ||||
| 	min-width 302px | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| <template> | ||||
| <div class="mk-stream-indicator"> | ||||
| 	<p v-if=" stream.state == 'initializing' "> | ||||
| 	<p v-if="stream.state == 'initializing'"> | ||||
| 		%fa:spinner .pulse% | ||||
| 		<span>%i18n:@connecting%<mk-ellipsis/></span> | ||||
| 	</p> | ||||
| 	<p v-if=" stream.state == 'reconnecting' "> | ||||
| 	<p v-if="stream.state == 'reconnecting'"> | ||||
| 		%fa:spinner .pulse% | ||||
| 		<span>%i18n:@reconnecting%<mk-ellipsis/></span> | ||||
| 	</p> | ||||
| 	<p v-if=" stream.state == 'connected' "> | ||||
| 	<p v-if="stream.state == 'connected'"> | ||||
| 		%fa:check% | ||||
| 		<span>%i18n:@connected%</span> | ||||
| 	</p> | ||||
|   | ||||
| @@ -1,199 +0,0 @@ | ||||
| <template> | ||||
| <div | ||||
| 	class="mk-switch" | ||||
| 	:class="{ disabled, checked }" | ||||
| 	role="switch" | ||||
| 	:aria-checked="checked" | ||||
| 	:aria-disabled="disabled" | ||||
| 	@click="switchValue" | ||||
| 	@mouseover="mouseenter" | ||||
| > | ||||
| 	<input | ||||
| 		type="checkbox" | ||||
| 		@change="handleChange" | ||||
| 		ref="input" | ||||
| 		:disabled="disabled" | ||||
| 		@keydown.enter="switchValue" | ||||
| 	> | ||||
| 	<span class="button"> | ||||
| 		<span :style="{ transform }"></span> | ||||
| 	</span> | ||||
| 	<span class="label"> | ||||
| 		<span :aria-hidden="!checked">{{ text }}</span> | ||||
| 		<p :aria-hidden="!checked"> | ||||
| 			<slot></slot> | ||||
| 		</p> | ||||
| 	</span> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| export default Vue.extend({ | ||||
| 	props: { | ||||
| 		value: { | ||||
| 			type: Boolean, | ||||
| 			default: false | ||||
| 		}, | ||||
| 		disabled: { | ||||
| 			type: Boolean, | ||||
| 			default: false | ||||
| 		}, | ||||
| 		text: String | ||||
| 	},/* | ||||
| 	created() { | ||||
| 		if (!~[true, false].indexOf(this.value)) { | ||||
| 			this.$emit('input', false); | ||||
| 		} | ||||
| 	},*/ | ||||
| 	computed: { | ||||
| 		checked(): boolean { | ||||
| 			return this.value; | ||||
| 		}, | ||||
| 		transform(): string { | ||||
| 			return this.checked ? 'translate3d(20px, 0, 0)' : ''; | ||||
| 		} | ||||
| 	}, | ||||
| 	watch: { | ||||
| 		value() { | ||||
| 			(this.$el).style.transition = 'all 0.3s'; | ||||
| 			(this.$refs.input as any).checked = this.checked; | ||||
| 		} | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		(this.$refs.input as any).checked = this.checked; | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		mouseenter() { | ||||
| 			(this.$el).style.transition = 'all 0s'; | ||||
| 		}, | ||||
| 		handleChange() { | ||||
| 			(this.$el).style.transition = 'all 0.3s'; | ||||
| 			this.$emit('input', !this.checked); | ||||
| 			this.$emit('change', !this.checked); | ||||
| 			this.$nextTick(() => { | ||||
| 				// set input's checked property | ||||
| 				// in case parent refuses to change component's value | ||||
| 				(this.$refs.input as any).checked = this.checked; | ||||
| 			}); | ||||
| 		}, | ||||
| 		switchValue() { | ||||
| 			!this.disabled && this.handleChange(); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| 	display flex | ||||
| 	margin 12px 0 | ||||
| 	cursor pointer | ||||
| 	transition all 0.3s | ||||
|  | ||||
| 	> * | ||||
| 		user-select none | ||||
|  | ||||
| 	&.disabled | ||||
| 		opacity 0.6 | ||||
| 		cursor not-allowed | ||||
|  | ||||
| 	&.checked | ||||
| 		> .button | ||||
| 			background-color $theme-color | ||||
| 			border-color $theme-color | ||||
|  | ||||
| 		> .label | ||||
| 			> span | ||||
| 				color $theme-color | ||||
|  | ||||
| 		&:hover | ||||
| 			> .label | ||||
| 				> span | ||||
| 					color darken($theme-color, 10%) | ||||
|  | ||||
| 			> .button | ||||
| 				background darken($theme-color, 10%) | ||||
| 				border-color darken($theme-color, 10%) | ||||
|  | ||||
| 	&:hover | ||||
| 		> .label | ||||
| 			> span | ||||
| 				color isDark ? #fff : #2e3338 | ||||
|  | ||||
| 		> .button | ||||
| 			$color = isDark ? #15181d : #ced2da | ||||
| 			background $color | ||||
| 			border-color $color | ||||
|  | ||||
| 	> input | ||||
| 		position absolute | ||||
| 		width 0 | ||||
| 		height 0 | ||||
| 		opacity 0 | ||||
| 		margin 0 | ||||
|  | ||||
| 		&:focus + .button | ||||
| 			&:after | ||||
| 				content "" | ||||
| 				pointer-events none | ||||
| 				position absolute | ||||
| 				top -5px | ||||
| 				right -5px | ||||
| 				bottom -5px | ||||
| 				left -5px | ||||
| 				border 2px solid rgba($theme-color, 0.3) | ||||
| 				border-radius 14px | ||||
|  | ||||
| 	> .button | ||||
| 		$color = isDark ? #1c1f25 : #dcdfe6 | ||||
|  | ||||
| 		display inline-block | ||||
| 		margin 0 | ||||
| 		width 40px | ||||
| 		min-width 40px | ||||
| 		height 20px | ||||
| 		min-height 20px | ||||
| 		background $color | ||||
| 		border 1px solid $color | ||||
| 		outline none | ||||
| 		border-radius 10px | ||||
| 		transition inherit | ||||
|  | ||||
| 		> * | ||||
| 			position absolute | ||||
| 			top 1px | ||||
| 			left 1px | ||||
| 			border-radius 100% | ||||
| 			transition transform 0.3s | ||||
| 			width 16px | ||||
| 			height 16px | ||||
| 			background-color #fff | ||||
|  | ||||
| 	> .label | ||||
| 		margin-left 8px | ||||
| 		display block | ||||
| 		font-size 15px | ||||
| 		cursor pointer | ||||
| 		transition inherit | ||||
|  | ||||
| 		> span | ||||
| 			display block | ||||
| 			line-height 20px | ||||
| 			color isDark ? #c4ccd2 : #4a535a | ||||
| 			transition inherit | ||||
|  | ||||
| 		> p | ||||
| 			margin 0 | ||||
| 			//font-size 90% | ||||
| 			color isDark ? #78858e : #9daab3 | ||||
|  | ||||
| .mk-switch[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-switch:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
| @@ -63,7 +63,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| root(isDark) | ||||
| .jtivnzhfwquxpsfidertopbmwmchmnmo | ||||
| 	height 100% | ||||
| 	width 100% | ||||
|  | ||||
| @@ -81,10 +81,4 @@ root(isDark) | ||||
| 		height 100% | ||||
| 		width 100% | ||||
|  | ||||
| .jtivnzhfwquxpsfidertopbmwmchmnmo[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .jtivnzhfwquxpsfidertopbmwmchmnmo:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
							
								
								
									
										317
									
								
								src/client/app/common/views/components/theme.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								src/client/app/common/views/components/theme.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,317 @@ | ||||
| <template> | ||||
| <div class="nicnklzforebnpfgasiypmpdaaglujqm"> | ||||
| 	<label> | ||||
| 		<span>%i18n:@light-theme%</span> | ||||
| 		<ui-select v-model="light" placeholder="%i18n:@light-theme%"> | ||||
| 			<optgroup label="%i18n:@light-themes%"> | ||||
| 				<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option> | ||||
| 			</optgroup> | ||||
| 			<optgroup label="%i18n:@dark-themes%"> | ||||
| 				<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option> | ||||
| 			</optgroup> | ||||
| 		</ui-select> | ||||
| 	</label> | ||||
|  | ||||
| 	<label> | ||||
| 		<span>%i18n:@dark-theme%</span> | ||||
| 		<ui-select v-model="dark" placeholder="%i18n:@dark-theme%"> | ||||
| 			<optgroup label="%i18n:@dark-themes%"> | ||||
| 				<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option> | ||||
| 			</optgroup> | ||||
| 			<optgroup label="%i18n:@light-themes%"> | ||||
| 				<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option> | ||||
| 			</optgroup> | ||||
| 		</ui-select> | ||||
| 	</label> | ||||
|  | ||||
| 	<details class="creator"> | ||||
| 		<summary>%fa:palette% %i18n:@create-a-theme%</summary> | ||||
| 		<div> | ||||
| 			<span>%i18n:@base-theme%:</span> | ||||
| 			<ui-radio v-model="myThemeBase" value="light">%i18n:@base-theme-light%</ui-radio> | ||||
| 			<ui-radio v-model="myThemeBase" value="dark">%i18n:@base-theme-dark%</ui-radio> | ||||
| 		</div> | ||||
| 		<div> | ||||
| 			<ui-input v-model="myThemeName"> | ||||
| 				<span>%i18n:@theme-name%</span> | ||||
| 			</ui-input> | ||||
| 			<ui-textarea v-model="myThemeDesc"> | ||||
| 				<span>%i18n:@desc%</span> | ||||
| 			</ui-textarea> | ||||
| 		</div> | ||||
| 		<div> | ||||
| 			<div style="padding-bottom:8px;">%i18n:@primary-color%:</div> | ||||
| 			<color-picker v-model="myThemePrimary"/> | ||||
| 		</div> | ||||
| 		<div> | ||||
| 			<div style="padding-bottom:8px;">%i18n:@secondary-color%:</div> | ||||
| 			<color-picker v-model="myThemeSecondary"/> | ||||
| 		</div> | ||||
| 		<div> | ||||
| 			<div style="padding-bottom:8px;">%i18n:@text-color%:</div> | ||||
| 			<color-picker v-model="myThemeText"/> | ||||
| 		</div> | ||||
| 		<ui-button @click="preview()">%fa:eye% %i18n:@preview-created-theme%</ui-button> | ||||
| 		<ui-button primary @click="gen()">%fa:save R% %i18n:@save-created-theme%</ui-button> | ||||
| 	</details> | ||||
|  | ||||
| 	<details> | ||||
| 		<summary>%fa:download% %i18n:@install-a-theme%</summary> | ||||
| 		<ui-button @click="import_()">%fa:file-import% %i18n:@import%</ui-button> | ||||
| 		<input ref="file" type="file" accept=".misskeytheme" style="display:none;" @change="onUpdateImportFile"/> | ||||
| 		<p>%i18n:@import-by-code%:</p> | ||||
| 		<ui-textarea v-model="installThemeCode"> | ||||
| 			<span>%i18n:@theme-code%</span> | ||||
| 		</ui-textarea> | ||||
| 		<ui-button @click="() => install(this.installThemeCode)">%fa:check% %i18n:@install%</ui-button> | ||||
| 	</details> | ||||
|  | ||||
| 	<details> | ||||
| 		<summary>%fa:folder-open% %i18n:@manage-themes%</summary> | ||||
| 		<ui-select v-model="selectedThemeId" placeholder="%i18n:@select-theme%"> | ||||
| 			<optgroup label="%i18n:@builtin-themes%"> | ||||
| 				<option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option> | ||||
| 			</optgroup> | ||||
| 			<optgroup label="%i18n:@my-themes%"> | ||||
| 				<option v-for="x in installedThemes.filter(t => t.author == this.$store.state.i.username)" :value="x.id" :key="x.id">{{ x.name }}</option> | ||||
| 			</optgroup> | ||||
| 			<optgroup label="%i18n:@installed-themes%"> | ||||
| 				<option v-for="x in installedThemes.filter(t => t.author != this.$store.state.i.username)" :value="x.id" :key="x.id">{{ x.name }}</option> | ||||
| 			</optgroup> | ||||
| 		</ui-select> | ||||
| 		<template v-if="selectedTheme"> | ||||
| 			<ui-input readonly :value="selectedTheme.author"> | ||||
| 				<span>%i18n:@author%</span> | ||||
| 			</ui-input> | ||||
| 			<ui-textarea v-if="selectedTheme.desc" readonly :value="selectedTheme.desc"> | ||||
| 				<span>%i18n:@desc%</span> | ||||
| 			</ui-textarea> | ||||
| 			<ui-textarea readonly :value="selectedThemeCode"> | ||||
| 				<span>%i18n:@theme-code%</span> | ||||
| 			</ui-textarea> | ||||
| 			<ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export">%fa:box% %i18n:@export%</ui-button> | ||||
| 			<ui-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)">%fa:trash-alt R% %i18n:@uninstall%</ui-button> | ||||
| 		</template> | ||||
| 	</details> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import { lightTheme, darkTheme, builtinThemes, applyTheme, Theme } from '../../../theme'; | ||||
| import { Chrome } from 'vue-color'; | ||||
| import * as uuid from 'uuid'; | ||||
| import * as tinycolor from 'tinycolor2'; | ||||
| import * as JSON5 from 'json5'; | ||||
|  | ||||
| // 後方互換性のため | ||||
| function convertOldThemedefinition(t) { | ||||
| 	const t2 = { | ||||
| 		id: t.meta.id, | ||||
| 		name: t.meta.name, | ||||
| 		author: t.meta.author, | ||||
| 		base: t.meta.base, | ||||
| 		vars: t.meta.vars, | ||||
| 		props: t | ||||
| 	}; | ||||
| 	delete t2.props.meta; | ||||
| 	return t2; | ||||
| } | ||||
|  | ||||
| export default Vue.extend({ | ||||
| 	components: { | ||||
| 		ColorPicker: Chrome | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			builtinThemes: builtinThemes, | ||||
| 			installThemeCode: null, | ||||
| 			selectedThemeId: null, | ||||
| 			myThemeBase: 'light', | ||||
| 			myThemeName: '', | ||||
| 			myThemeDesc: '', | ||||
| 			myThemePrimary: lightTheme.vars.primary, | ||||
| 			myThemeSecondary: lightTheme.vars.secondary, | ||||
| 			myThemeText: lightTheme.vars.text | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	computed: { | ||||
| 		themes(): Theme[] { | ||||
| 			return builtinThemes.concat(this.$store.state.device.themes); | ||||
| 		}, | ||||
|  | ||||
| 		darkThemes(): Theme[] { | ||||
| 			return this.themes.filter(t => t.base == 'dark' || t.kind == 'dark'); | ||||
| 		}, | ||||
|  | ||||
| 		lightThemes(): Theme[] { | ||||
| 			return this.themes.filter(t => t.base == 'light' || t.kind == 'light'); | ||||
| 		}, | ||||
|  | ||||
| 		installedThemes(): Theme[] { | ||||
| 			return this.$store.state.device.themes; | ||||
| 		}, | ||||
|  | ||||
| 		light: { | ||||
| 			get() { return this.$store.state.device.lightTheme; }, | ||||
| 			set(value) { this.$store.commit('device/set', { key: 'lightTheme', value }); } | ||||
| 		}, | ||||
|  | ||||
| 		dark: { | ||||
| 			get() { return this.$store.state.device.darkTheme; }, | ||||
| 			set(value) { this.$store.commit('device/set', { key: 'darkTheme', value }); } | ||||
| 		}, | ||||
|  | ||||
| 		selectedTheme() { | ||||
| 			if (this.selectedThemeId == null) return null; | ||||
| 			return this.themes.find(x => x.id == this.selectedThemeId); | ||||
| 		}, | ||||
|  | ||||
| 		selectedThemeCode() { | ||||
| 			if (this.selectedTheme == null) return null; | ||||
| 			return JSON5.stringify(this.selectedTheme, null, '\t'); | ||||
| 		}, | ||||
|  | ||||
| 		myTheme(): any { | ||||
| 			return { | ||||
| 				name: this.myThemeName, | ||||
| 				author: this.$store.state.i.username, | ||||
| 				desc: this.myThemeDesc, | ||||
| 				base: this.myThemeBase, | ||||
| 				vars: { | ||||
| 					primary: tinycolor(typeof this.myThemePrimary == 'string' ? this.myThemePrimary : this.myThemePrimary.rgba).toRgbString(), | ||||
| 					secondary: tinycolor(typeof this.myThemeSecondary == 'string' ? this.myThemeSecondary : this.myThemeSecondary.rgba).toRgbString(), | ||||
| 					text: tinycolor(typeof this.myThemeText == 'string' ? this.myThemeText : this.myThemeText.rgba).toRgbString() | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	watch: { | ||||
| 		myThemeBase(v) { | ||||
| 			const theme = v == 'light' ? lightTheme : darkTheme; | ||||
| 			this.myThemePrimary = theme.vars.primary; | ||||
| 			this.myThemeSecondary = theme.vars.secondary; | ||||
| 			this.myThemeText = theme.vars.text; | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	beforeCreate() { | ||||
| 		// migrate old theme definitions | ||||
| 		// 後方互換性のため | ||||
| 		this.$store.commit('device/set', { | ||||
| 			key: 'themes', value: this.$store.state.device.themes.map(t => { | ||||
| 				if (t.id == null) { | ||||
| 					return convertOldThemedefinition(t); | ||||
| 				} else { | ||||
| 					return t; | ||||
| 				} | ||||
| 			}) | ||||
| 		}); | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		install(code) { | ||||
| 			let theme; | ||||
|  | ||||
| 			try { | ||||
| 				theme = JSON5.parse(code); | ||||
| 			} catch (e) { | ||||
| 				alert('%i18n:@invalid-theme%'); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			// 後方互換性のため | ||||
| 			if (theme.id == null && theme.meta != null) { | ||||
| 				theme = convertOldThemedefinition(theme); | ||||
| 			} | ||||
|  | ||||
| 			if (theme.id == null) { | ||||
| 				alert('%i18n:@invalid-theme%'); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			if (this.$store.state.device.themes.some(t => t.id == theme.id)) { | ||||
| 				alert('%i18n:@already-installed%'); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			const themes = this.$store.state.device.themes.concat(theme); | ||||
| 			this.$store.commit('device/set', { | ||||
| 				key: 'themes', value: themes | ||||
| 			}); | ||||
|  | ||||
| 			alert('%i18n:@installed%'.replace('{}', theme.name)); | ||||
| 		}, | ||||
|  | ||||
| 		uninstall() { | ||||
| 			const theme = this.selectedTheme; | ||||
| 			const themes = this.$store.state.device.themes.filter(t => t.id != theme.id); | ||||
| 			this.$store.commit('device/set', { | ||||
| 				key: 'themes', value: themes | ||||
| 			}); | ||||
| 			alert('%i18n:@uninstalled%'.replace('{}', theme.name)); | ||||
| 		}, | ||||
|  | ||||
| 		import_() { | ||||
| 			(this.$refs.file as any).click(); | ||||
| 		} | ||||
|  | ||||
| 		export_() { | ||||
| 			const blob = new Blob([this.selectedThemeCode], { | ||||
| 				type: 'application/json5' | ||||
| 			}); | ||||
| 			this.$refs.export.$el.href = window.URL.createObjectURL(blob); | ||||
| 		}, | ||||
|  | ||||
| 		onUpdateImportFile() { | ||||
| 			const f = (this.$refs.file as any).files[0]; | ||||
|  | ||||
| 			const reader = new FileReader(); | ||||
|  | ||||
| 			reader.onload = e => { | ||||
| 				this.install(e.target.result); | ||||
| 			}; | ||||
|  | ||||
| 			reader.readAsText(f); | ||||
| 		}, | ||||
|  | ||||
| 		preview() { | ||||
| 			applyTheme(this.myTheme, false); | ||||
| 		}, | ||||
|  | ||||
| 		gen() { | ||||
| 			const theme = this.myTheme; | ||||
| 			if (theme.name == null || theme.name.trim() == '') { | ||||
| 				alert('%i18n:@theme-name-required%'); | ||||
| 				return; | ||||
| 			} | ||||
| 			theme.id = uuid(); | ||||
| 			const themes = this.$store.state.device.themes.concat(theme); | ||||
| 			this.$store.commit('device/set', { | ||||
| 				key: 'themes', value: themes | ||||
| 			}); | ||||
| 			alert('%i18n:@saved%'); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| .nicnklzforebnpfgasiypmpdaaglujqm | ||||
| 	> details | ||||
| 		border-top solid 1px var(--faceDivider) | ||||
|  | ||||
| 		> summary | ||||
| 			padding 16px 0 | ||||
|  | ||||
| 		> *:last-child | ||||
| 			margin-bottom 16px | ||||
|  | ||||
| 	> .creator | ||||
| 		> div | ||||
| 			padding 16px 0 | ||||
| 			border-bottom solid 1px var(--faceDivider) | ||||
| </style> | ||||
| @@ -49,13 +49,14 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| root(isDark) | ||||
| .csqvmxybqbycalfhkxvyfrgbrdalkaoc | ||||
| 	> .fetching | ||||
| 	> .empty | ||||
| 		margin 0 | ||||
| 		padding 16px | ||||
| 		text-align center | ||||
| 		color #aaa | ||||
| 		color var(--text) | ||||
| 		opacity 0.7 | ||||
|  | ||||
| 		> [data-fa] | ||||
| 			margin-right 4px | ||||
| @@ -70,13 +71,13 @@ root(isDark) | ||||
| 			padding 14px 16px | ||||
|  | ||||
| 			&:not(:last-child) | ||||
| 				border-bottom solid 1px isDark ? #393f4f : #eee | ||||
| 				border-bottom solid 1px var(--faceDivider) | ||||
|  | ||||
| 			> .tag | ||||
| 				flex 1 | ||||
| 				overflow hidden | ||||
| 				font-size 14px | ||||
| 				color isDark ? #9baec8 : #65727b | ||||
| 				color var(--text) | ||||
|  | ||||
| 				> a | ||||
| 					display block | ||||
| @@ -94,10 +95,4 @@ root(isDark) | ||||
| 			> .chart | ||||
| 				height 30px | ||||
|  | ||||
| .csqvmxybqbycalfhkxvyfrgbrdalkaoc[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .csqvmxybqbycalfhkxvyfrgbrdalkaoc:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -1,9 +1,7 @@ | ||||
| <template> | ||||
| <div class="ui-button" :class="[styl]"> | ||||
| 	<button :type="type" @click="$emit('click')"> | ||||
| 		<slot></slot> | ||||
| 	</button> | ||||
| </div> | ||||
| <component class="dmtdnykelhudezerjlfpbhgovrgnqqgr" :is="link ? 'a' : 'button'" :class="[styl, { inline, primary }]" :type="type" @click="$emit('click')"> | ||||
| 	<slot></slot> | ||||
| </component> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| @@ -13,70 +11,100 @@ export default Vue.extend({ | ||||
| 		type: { | ||||
| 			type: String, | ||||
| 			required: false | ||||
| 		}, | ||||
| 		primary: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: false | ||||
| 		}, | ||||
| 		inline: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: false | ||||
| 		}, | ||||
| 		link: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: false | ||||
| 		} | ||||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
| 			styl: 'fill' | ||||
| 		}; | ||||
| 	}, | ||||
| 	inject: { | ||||
| 		isCardChild: { default: false } | ||||
| 	}, | ||||
| 	created() { | ||||
| 		if (this.isCardChild) { | ||||
| 			this.styl = 'line'; | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
| .dmtdnykelhudezerjlfpbhgovrgnqqgr | ||||
| 	display block | ||||
| 	width 100% | ||||
| 	margin 0 | ||||
| 	padding 8px | ||||
| 	text-align center | ||||
| 	font-weight normal | ||||
| 	font-size 16px | ||||
| 	border none | ||||
| 	border-radius 6px | ||||
| 	outline none | ||||
| 	box-shadow none | ||||
| 	text-decoration none | ||||
| 	user-select none | ||||
|  | ||||
| root(isDark, fill) | ||||
| 	> button | ||||
| 		display block | ||||
| 		width 100% | ||||
| 		margin 0 | ||||
| 		padding 0 | ||||
| 	* | ||||
| 		pointer-events none | ||||
|  | ||||
| 	&:focus | ||||
| 		&:after | ||||
| 			content "" | ||||
| 			pointer-events none | ||||
| 			position absolute | ||||
| 			top -5px | ||||
| 			right -5px | ||||
| 			bottom -5px | ||||
| 			left -5px | ||||
| 			border 2px solid var(--primaryAlpha03) | ||||
| 			border-radius 10px | ||||
|  | ||||
| 	&:not(.inline) + .dmtdnykelhudezerjlfpbhgovrgnqqgr | ||||
| 		margin-top 16px | ||||
|  | ||||
| 	&.inline | ||||
| 		display inline-block | ||||
| 		width auto | ||||
|  | ||||
| 	&.primary | ||||
| 		font-weight bold | ||||
| 		font-size 16px | ||||
| 		line-height 44px | ||||
| 		border none | ||||
| 		border-radius 6px | ||||
| 		outline none | ||||
| 		box-shadow none | ||||
|  | ||||
| 		if fill | ||||
| 			color $theme-color-foreground | ||||
| 			background $theme-color | ||||
| 	&.fill | ||||
| 		color var(--text) | ||||
| 		background var(--buttonBg) | ||||
|  | ||||
| 		&:hover | ||||
| 			background var(--buttonHoverBg) | ||||
|  | ||||
| 		&:active | ||||
| 			background var(--buttonActiveBg) | ||||
|  | ||||
| 		&.primary | ||||
| 			color var(--primaryForeground) | ||||
| 			background var(--primary) | ||||
|  | ||||
| 			&:hover | ||||
| 				background lighten($theme-color, 5%) | ||||
| 				background var(--primaryLighten5) | ||||
|  | ||||
| 			&:active | ||||
| 				background darken($theme-color, 5%) | ||||
| 		else | ||||
| 			color $theme-color | ||||
| 			background none | ||||
| 				background var(--primaryDarken5) | ||||
|  | ||||
| 			&:hover | ||||
| 				color darken($theme-color, 5%) | ||||
|  | ||||
| 			&:active | ||||
| 				background rgba($theme-color, 0.3) | ||||
|  | ||||
| .ui-button[data-darkmode] | ||||
| 	&.fill | ||||
| 		root(true, true) | ||||
| 	&:not(.fill) | ||||
| 		root(true, false) | ||||
| 		color var(--primary) | ||||
| 		background none | ||||
|  | ||||
| .ui-button:not([data-darkmode]) | ||||
| 	&.fill | ||||
| 		root(false, true) | ||||
| 	&:not(.fill) | ||||
| 		root(false, false) | ||||
| 		&:hover | ||||
| 			color var(--primaryDarken5) | ||||
|  | ||||
| 		&:active | ||||
| 			background var(--primaryAlpha03) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -20,26 +20,24 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .ui-card | ||||
| 	margin 16px | ||||
| 	color isDark ? #fff : #000 | ||||
| 	background isDark ? #282C37 : #fff | ||||
| 	color var(--faceText) | ||||
| 	background var(--face) | ||||
| 	box-shadow 0 3px 1px -2px rgba(#000, 0.2), 0 2px 2px 0 rgba(#000, 0.14), 0 1px 5px 0 rgba(#000, 0.12) | ||||
|  | ||||
| 	> header | ||||
| 		padding 16px | ||||
| 		font-weight bold | ||||
| 		font-size 20px | ||||
| 		color isDark ? #fff : #444 | ||||
| 		color var(--faceText) | ||||
|  | ||||
| 		@media (min-width 500px) | ||||
| 			padding 24px 32px | ||||
|  | ||||
| 	> section | ||||
| 		padding 20px 16px | ||||
| 		border-top solid 1px isDark ? rgba(#000, 0.3) : rgba(#000, 0.1) | ||||
| 		border-top solid 1px var(--faceDivider) | ||||
|  | ||||
| 		@media (min-width 500px) | ||||
| 			padding 32px | ||||
| @@ -50,12 +48,5 @@ root(isDark) | ||||
| 		> header | ||||
| 			margin-bottom 16px | ||||
| 			font-weight bold | ||||
| 			color isDark ? #fff : #444 | ||||
|  | ||||
| .ui-card[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .ui-card:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| 			color var(--faceText) | ||||
| </style> | ||||
|   | ||||
| @@ -19,7 +19,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
|  | ||||
| .ui-form | ||||
| 	> fieldset | ||||
|   | ||||
| @@ -25,9 +25,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .nvemkhtwcnnpkdrwfcbzuwhfulejhmzg | ||||
| 	display inline-block | ||||
|  | ||||
| 	& + .nvemkhtwcnnpkdrwfcbzuwhfulejhmzg | ||||
| @@ -38,11 +36,11 @@ root(isDark) | ||||
| 		margin 0 | ||||
| 		padding 12px 20px | ||||
| 		font-size 14px | ||||
| 		border 1px solid isDark ? #6d727d : #dcdfe6 | ||||
| 		border 1px solid var(--formButtonBorder) | ||||
| 		border-radius 4px | ||||
| 		outline none | ||||
| 		box-shadow none | ||||
| 		color isDark ? #fff : #606266 | ||||
| 		color var(--text) | ||||
| 		transition 0.1s | ||||
|  | ||||
| 		* | ||||
| @@ -50,40 +48,34 @@ root(isDark) | ||||
|  | ||||
| 		&:hover | ||||
| 		&:focus | ||||
| 			color $theme-color | ||||
| 			background rgba($theme-color, isDark ? 0.2 : 0.12) | ||||
| 			border-color rgba($theme-color, isDark ? 0.5 : 0.3) | ||||
| 			color var(--primary) | ||||
| 			background var(--formButtonHoverBg) | ||||
| 			border-color var(--formButtonHoverBorder) | ||||
|  | ||||
| 		&:active | ||||
| 			color darken($theme-color, 20%) | ||||
| 			background rgba($theme-color, 0.12) | ||||
| 			border-color $theme-color | ||||
| 			color var(--primaryDarken20) | ||||
| 			background var(--formButtonActiveBg) | ||||
| 			border-color var(--primary) | ||||
| 			transition all 0s | ||||
|  | ||||
| 	&.primary | ||||
| 		> button | ||||
| 			border 1px solid $theme-color | ||||
| 			background $theme-color | ||||
| 			color $theme-color-foreground | ||||
| 			border 1px solid var(--primary) | ||||
| 			background var(--primary) | ||||
| 			color var(--primaryForeground) | ||||
|  | ||||
| 			&:hover | ||||
| 			&:focus | ||||
| 				background lighten($theme-color, 20%) | ||||
| 				border-color lighten($theme-color, 20%) | ||||
| 				background var(--primaryLighten20) | ||||
| 				border-color var(--primaryLighten20) | ||||
|  | ||||
| 			&:active | ||||
| 				background darken($theme-color, 20%) | ||||
| 				border-color darken($theme-color, 20%) | ||||
| 				background var(--primaryDarken20) | ||||
| 				border-color var(--primaryDarken20) | ||||
| 				transition all 0s | ||||
|  | ||||
| 	&.round | ||||
| 		> button | ||||
| 			border-radius 64px | ||||
|  | ||||
| .nvemkhtwcnnpkdrwfcbzuwhfulejhmzg[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .nvemkhtwcnnpkdrwfcbzuwhfulejhmzg:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -49,9 +49,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .uywduthvrdnlpsvsjkqigicixgyfctto | ||||
| 	display inline-flex | ||||
| 	margin 0 16px 0 0 | ||||
| 	cursor pointer | ||||
| @@ -62,7 +60,7 @@ root(isDark) | ||||
|  | ||||
| 	&:hover | ||||
| 		> .button | ||||
| 			border solid 2px isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54) | ||||
| 			border solid 2px var(--inputLabel) | ||||
|  | ||||
| 	&.disabled | ||||
| 		opacity 0.6 | ||||
| @@ -70,15 +68,15 @@ root(isDark) | ||||
|  | ||||
| 	&.checked | ||||
| 		> .button | ||||
| 			border-color $theme-color | ||||
| 			border-color var(--primary) | ||||
|  | ||||
| 			&:after | ||||
| 				background-color $theme-color | ||||
| 				background-color var(--primary) | ||||
| 				transform scale(1) | ||||
| 				opacity 1 | ||||
|  | ||||
| 		> .label | ||||
| 			color $theme-color | ||||
| 			color var(--primary) | ||||
|  | ||||
| 	> input | ||||
| 		position absolute | ||||
| @@ -93,7 +91,7 @@ root(isDark) | ||||
| 		width 20px | ||||
| 		height 20px | ||||
| 		background none | ||||
| 		border solid 2px isDark ? rgba(#fff, 0.6) : rgba(#000, 0.4) | ||||
| 		border solid 2px var(--radioBorder) | ||||
| 		border-radius 100% | ||||
| 		transition inherit | ||||
|  | ||||
| @@ -117,10 +115,4 @@ root(isDark) | ||||
| 		line-height 20px | ||||
| 		cursor pointer | ||||
|  | ||||
| .uywduthvrdnlpsvsjkqigicixgyfctto[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .uywduthvrdnlpsvsjkqigicixgyfctto:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -71,14 +71,18 @@ export default Vue.extend({ | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: false | ||||
| 		}, | ||||
| 		styl: { | ||||
| 			type: String, | ||||
| 			required: false, | ||||
| 			default: 'line' | ||||
| 		} | ||||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
| 			v: this.value, | ||||
| 			focused: false, | ||||
| 			passwordStrength: '', | ||||
| 			styl: 'fill' | ||||
| 			passwordStrength: '' | ||||
| 		}; | ||||
| 	}, | ||||
| 	computed: { | ||||
| @@ -117,14 +121,6 @@ export default Vue.extend({ | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| 	inject: { | ||||
| 		isCardChild: { default: false } | ||||
| 	}, | ||||
| 	created() { | ||||
| 		if (this.isCardChild) { | ||||
| 			this.styl = 'line'; | ||||
| 		} | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		if (this.$refs.prefix) { | ||||
| 			this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px'; | ||||
| @@ -155,9 +151,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark, fill) | ||||
| root(fill) | ||||
| 	margin 32px 0 | ||||
|  | ||||
| 	> .icon | ||||
| @@ -167,7 +161,7 @@ root(isDark, fill) | ||||
| 		width 24px | ||||
| 		text-align center | ||||
| 		line-height 32px | ||||
| 		color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54) | ||||
| 		color var(--inputLabel) | ||||
|  | ||||
| 		&:not(:empty) + .input | ||||
| 			margin-left 28px | ||||
| @@ -183,7 +177,7 @@ root(isDark, fill) | ||||
| 				left 0 | ||||
| 				right 0 | ||||
| 				height 1px | ||||
| 				background isDark ? rgba(#fff, 0.7) : rgba(#000, 0.42) | ||||
| 				background var(--inputBorder) | ||||
|  | ||||
| 			&:after | ||||
| 				content '' | ||||
| @@ -193,7 +187,7 @@ root(isDark, fill) | ||||
| 				left 0 | ||||
| 				right 0 | ||||
| 				height 2px | ||||
| 				background $theme-color | ||||
| 				background var(--primary) | ||||
| 				opacity 0 | ||||
| 				transform scaleX(0.12) | ||||
| 				transition border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1) | ||||
| @@ -242,7 +236,7 @@ root(isDark, fill) | ||||
| 			transition-duration 0.3s | ||||
| 			font-size 16px | ||||
| 			line-height 32px | ||||
| 			color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54) | ||||
| 			color var(--inputLabel) | ||||
| 			pointer-events none | ||||
| 			//will-change transform | ||||
| 			transform-origin top left | ||||
| @@ -257,7 +251,7 @@ root(isDark, fill) | ||||
| 			font-weight fill ? bold : normal | ||||
| 			font-size 16px | ||||
| 			line-height 32px | ||||
| 			color isDark ? #fff : #000 | ||||
| 			color var(--inputText) | ||||
| 			background transparent | ||||
| 			border none | ||||
| 			border-radius 0 | ||||
| @@ -280,7 +274,7 @@ root(isDark, fill) | ||||
| 			top 0 | ||||
| 			font-size 16px | ||||
| 			line-height fill ? 44px : 32px | ||||
| 			color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54) | ||||
| 			color var(--inputLabel) | ||||
| 			pointer-events none | ||||
|  | ||||
| 			&:empty | ||||
| @@ -325,7 +319,7 @@ root(isDark, fill) | ||||
| 					transform scaleX(1) | ||||
|  | ||||
| 			> .label | ||||
| 				color $theme-color | ||||
| 				color var(--primary) | ||||
|  | ||||
| 	&.focused | ||||
| 	&.filled | ||||
| @@ -335,16 +329,10 @@ root(isDark, fill) | ||||
| 				left 0 !important | ||||
| 				transform scale(0.75) | ||||
|  | ||||
| .ui-input[data-darkmode] | ||||
| .ui-input | ||||
| 	&.fill | ||||
| 		root(true, true) | ||||
| 		root(true) | ||||
| 	&:not(.fill) | ||||
| 		root(true, false) | ||||
|  | ||||
| .ui-input:not([data-darkmode]) | ||||
| 	&.fill | ||||
| 		root(false, true) | ||||
| 	&:not(.fill) | ||||
| 		root(false, false) | ||||
| 		root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -51,9 +51,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .ui-radio | ||||
| 	display inline-block | ||||
| 	margin 0 32px 0 0 | ||||
| 	cursor pointer | ||||
| @@ -68,10 +66,10 @@ root(isDark) | ||||
|  | ||||
| 	&.checked | ||||
| 		> .button | ||||
| 			border-color $theme-color | ||||
| 			border-color var(--primary) | ||||
|  | ||||
| 			&:after | ||||
| 				background-color $theme-color | ||||
| 				background-color var(--primary) | ||||
| 				transform scale(1) | ||||
| 				opacity 1 | ||||
|  | ||||
| @@ -87,7 +85,7 @@ root(isDark) | ||||
| 		width 20px | ||||
| 		height 20px | ||||
| 		background none | ||||
| 		border solid 2px isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54) | ||||
| 		border solid 2px var(--inputLabel) | ||||
| 		border-radius 100% | ||||
| 		transition inherit | ||||
|  | ||||
| @@ -111,10 +109,4 @@ root(isDark) | ||||
| 		line-height 20px | ||||
| 		cursor pointer | ||||
|  | ||||
| .ui-radio[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .ui-radio:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -29,13 +29,17 @@ export default Vue.extend({ | ||||
| 		required: { | ||||
| 			type: Boolean, | ||||
| 			required: false | ||||
| 		}, | ||||
| 		styl: { | ||||
| 			type: String, | ||||
| 			required: false, | ||||
| 			default: 'line' | ||||
| 		} | ||||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
| 			v: this.value, | ||||
| 			focused: false, | ||||
| 			styl: 'fill' | ||||
| 			focused: false | ||||
| 		}; | ||||
| 	}, | ||||
| 	computed: { | ||||
| @@ -48,14 +52,6 @@ export default Vue.extend({ | ||||
| 			this.v = v; | ||||
| 		} | ||||
| 	}, | ||||
| 	inject: { | ||||
| 		isCardChild: { default: false } | ||||
| 	}, | ||||
| 	created() { | ||||
| 		if (this.isCardChild) { | ||||
| 			this.styl = 'line'; | ||||
| 		} | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		if (this.$refs.prefix) { | ||||
| 			this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px'; | ||||
| @@ -70,9 +66,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark, fill) | ||||
| root(fill) | ||||
| 	margin 32px 0 | ||||
|  | ||||
| 	> .icon | ||||
| @@ -103,7 +97,7 @@ root(isDark, fill) | ||||
| 				left 0 | ||||
| 				right 0 | ||||
| 				height 1px | ||||
| 				background isDark ? rgba(#fff, 0.7) : rgba(#000, 0.42) | ||||
| 				background var(--inputBorder) | ||||
|  | ||||
| 			&:after | ||||
| 				content '' | ||||
| @@ -113,7 +107,7 @@ root(isDark, fill) | ||||
| 				left 0 | ||||
| 				right 0 | ||||
| 				height 2px | ||||
| 				background $theme-color | ||||
| 				background var(--primary) | ||||
| 				opacity 0 | ||||
| 				transform scaleX(0.12) | ||||
| 				transition border 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 0.3s cubic-bezier(0.4, 0, 0.2, 1) | ||||
| @@ -143,7 +137,7 @@ root(isDark, fill) | ||||
| 			font-weight fill ? bold : normal | ||||
| 			font-size 16px | ||||
| 			height 32px | ||||
| 			color isDark ? #fff : #000 | ||||
| 			color var(--inputText) | ||||
| 			background transparent | ||||
| 			border none | ||||
| 			border-radius 0 | ||||
| @@ -190,7 +184,7 @@ root(isDark, fill) | ||||
| 					transform scaleX(1) | ||||
|  | ||||
| 			> .label | ||||
| 				color $theme-color | ||||
| 				color var(--primary) | ||||
|  | ||||
| 	&.focused | ||||
| 	&.filled | ||||
| @@ -200,16 +194,10 @@ root(isDark, fill) | ||||
| 				left 0 !important | ||||
| 				transform scale(0.75) | ||||
|  | ||||
| .ui-select[data-darkmode] | ||||
| .ui-select | ||||
| 	&.fill | ||||
| 		root(true, true) | ||||
| 		root(true) | ||||
| 	&:not(.fill) | ||||
| 		root(true, false) | ||||
|  | ||||
| .ui-select:not([data-darkmode]) | ||||
| 	&.fill | ||||
| 		root(false, true) | ||||
| 	&:not(.fill) | ||||
| 		root(false, false) | ||||
| 		root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
| 	<span class="label"> | ||||
| 		<span :aria-hidden="!checked"><slot></slot></span> | ||||
| 		<p :aria-hidden="!checked"> | ||||
| 			<slot name="text"></slot> | ||||
| 			<slot name="desc"></slot> | ||||
| 		</p> | ||||
| 	</span> | ||||
| </div> | ||||
| @@ -56,9 +56,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark) | ||||
| .ui-switch | ||||
| 	display flex | ||||
| 	margin 32px 0 | ||||
| 	cursor pointer | ||||
| @@ -79,11 +77,11 @@ root(isDark) | ||||
|  | ||||
| 	&.checked | ||||
| 		> .button | ||||
| 			background-color rgba($theme-color, 0.4) | ||||
| 			border-color rgba($theme-color, 0.4) | ||||
| 			background-color var(--primaryAlpha04) | ||||
| 			border-color var(--primaryAlpha04) | ||||
|  | ||||
| 			> * | ||||
| 				background-color $theme-color | ||||
| 				background-color var(--primary) | ||||
| 				transform translateX(14px) | ||||
|  | ||||
| 	> input | ||||
| @@ -99,7 +97,7 @@ root(isDark) | ||||
| 		margin 3px 0 0 0 | ||||
| 		width 34px | ||||
| 		height 14px | ||||
| 		background isDark ? rgba(#fff, 0.15) : rgba(#000, 0.25) | ||||
| 		background var(--switchTrack) | ||||
| 		outline none | ||||
| 		border-radius 14px | ||||
| 		transition inherit | ||||
| @@ -125,18 +123,11 @@ root(isDark) | ||||
| 		> span | ||||
| 			display block | ||||
| 			line-height 20px | ||||
| 			color isDark ? #c4ccd2 : rgba(#000, 0.75) | ||||
| 			color currentColor | ||||
| 			transition inherit | ||||
|  | ||||
| 		> p | ||||
| 			margin 0 | ||||
| 			//font-size 90% | ||||
| 			color isDark ? #78858e : #9daab3 | ||||
|  | ||||
| .ui-switch[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .ui-switch:not([data-darkmode]) | ||||
| 	root(false) | ||||
| 			opacity 0.7 | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -63,9 +63,7 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| root(isDark, fill) | ||||
| root(fill) | ||||
| 	margin 42px 0 32px 0 | ||||
|  | ||||
| 	> .input | ||||
| @@ -84,7 +82,7 @@ root(isDark, fill) | ||||
| 				left 0 | ||||
| 				right 0 | ||||
| 				background none | ||||
| 				border solid 1px isDark ? rgba(#fff, 0.7) : rgba(#000, 0.42) | ||||
| 				border solid 1px var(--inputBorder) | ||||
| 				border-radius 3px | ||||
| 				pointer-events none | ||||
|  | ||||
| @@ -97,7 +95,7 @@ root(isDark, fill) | ||||
| 				left 0 | ||||
| 				right 0 | ||||
| 				background none | ||||
| 				border solid 2px $theme-color | ||||
| 				border solid 2px var(--primary) | ||||
| 				border-radius 3px | ||||
| 				opacity 0 | ||||
| 				transition opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1) | ||||
| @@ -112,7 +110,7 @@ root(isDark, fill) | ||||
| 			transition-duration 0.3s | ||||
| 			font-size 16px | ||||
| 			line-height 32px | ||||
| 			color isDark ? rgba(#fff, 0.7) : rgba(#000, 0.54) | ||||
| 			color var(--inputLabel) | ||||
| 			pointer-events none | ||||
| 			//will-change transform | ||||
| 			transform-origin top left | ||||
| @@ -126,7 +124,7 @@ root(isDark, fill) | ||||
| 			font inherit | ||||
| 			font-weight fill ? bold : normal | ||||
| 			font-size 16px | ||||
| 			color isDark ? #fff : #000 | ||||
| 			color var(--inputText) | ||||
| 			background transparent | ||||
| 			border none | ||||
| 			border-radius 0 | ||||
| @@ -149,7 +147,7 @@ root(isDark, fill) | ||||
| 					opacity 1 | ||||
|  | ||||
| 			> .label | ||||
| 				color $theme-color | ||||
| 				color var(--primary) | ||||
|  | ||||
| 	&.focused | ||||
| 	&.filled | ||||
| @@ -159,16 +157,10 @@ root(isDark, fill) | ||||
| 				left 0 !important | ||||
| 				transform scale(0.75) | ||||
|  | ||||
| .ui-textarea[data-darkmode] | ||||
| 	&.fill | ||||
| 		root(true, true) | ||||
| 	&:not(.fill) | ||||
| 		root(true, false) | ||||
| .ui-textarea.fill | ||||
| 	root(true) | ||||
|  | ||||
| .ui-textarea:not([data-darkmode]) | ||||
| 	&.fill | ||||
| 		root(false, true) | ||||
| 	&:not(.fill) | ||||
| 		root(false, false) | ||||
| .ui-textarea:not(.fill) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import { apiUrl } from '../../../config'; | ||||
| import getMD5 from '../../scripts/get-md5'; | ||||
|  | ||||
| export default Vue.extend({ | ||||
| 	data() { | ||||
| @@ -28,61 +29,83 @@ export default Vue.extend({ | ||||
| 		}; | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		upload(file, folder) { | ||||
| 		checkExistence(fileData: ArrayBuffer): Promise<any> { | ||||
| 			return new Promise((resolve, reject) => { | ||||
| 				const data = new FormData(); | ||||
| 				data.append('md5', getMD5(fileData)); | ||||
|  | ||||
| 				(this as any).api('drive/files/check_existence', { | ||||
| 					md5: getMD5(fileData) | ||||
| 				}).then(resp => { | ||||
| 					resolve(resp.file); | ||||
| 				}); | ||||
| 			}); | ||||
| 		}, | ||||
|  | ||||
| 		upload(file: File, folder: any) { | ||||
| 			if (folder && typeof folder == 'object') folder = folder.id; | ||||
|  | ||||
| 			const id = Math.random(); | ||||
|  | ||||
| 			const ctx = { | ||||
| 				id: id, | ||||
| 				name: file.name || 'untitled', | ||||
| 				progress: undefined, | ||||
| 				img: undefined | ||||
| 			}; | ||||
|  | ||||
| 			this.uploads.push(ctx); | ||||
| 			this.$emit('change', this.uploads); | ||||
|  | ||||
| 			const reader = new FileReader(); | ||||
| 			reader.onload = (e: any) => { | ||||
| 				ctx.img = e.target.result; | ||||
| 			}; | ||||
| 			reader.readAsDataURL(file); | ||||
| 				this.checkExistence(e.target.result).then(result => { | ||||
| 					if (result !== null) { | ||||
| 						this.$emit('uploaded', result); | ||||
| 						return; | ||||
| 					} | ||||
|  | ||||
| 			const data = new FormData(); | ||||
| 			data.append('i', this.$store.state.i.token); | ||||
| 			data.append('file', file); | ||||
| 					// Upload if the file didn't exist yet | ||||
| 					const buf = new Uint8Array(e.target.result); | ||||
| 					let bin = ''; | ||||
| 					// We use for-of loop instead of apply() to avoid RangeError | ||||
| 					// SEE: https://stackoverflow.com/questions/9267899/arraybuffer-to-base64-encoded-string | ||||
| 					for (const byte of buf) bin += String.fromCharCode(byte); | ||||
| 					const ctx = { | ||||
| 						id: id, | ||||
| 						name: file.name || 'untitled', | ||||
| 						progress: undefined, | ||||
| 						img: 'data:*/*;base64,' + btoa(bin) | ||||
| 					}; | ||||
|  | ||||
| 			if (folder) data.append('folderId', folder); | ||||
| 					this.uploads.push(ctx); | ||||
| 					this.$emit('change', this.uploads); | ||||
|  | ||||
| 			const xhr = new XMLHttpRequest(); | ||||
| 			xhr.open('POST', apiUrl + '/drive/files/create', true); | ||||
| 			xhr.onload = (e: any) => { | ||||
| 				const driveFile = JSON.parse(e.target.response); | ||||
| 					const data = new FormData(); | ||||
| 					data.append('i', this.$store.state.i.token); | ||||
| 					data.append('file', file); | ||||
|  | ||||
| 				this.$emit('uploaded', driveFile); | ||||
| 					if (folder) data.append('folderId', folder); | ||||
|  | ||||
| 				this.uploads = this.uploads.filter(x => x.id != id); | ||||
| 				this.$emit('change', this.uploads); | ||||
| 			}; | ||||
| 					const xhr = new XMLHttpRequest(); | ||||
| 					xhr.open('POST', apiUrl + '/drive/files/create', true); | ||||
| 					xhr.onload = (e: any) => { | ||||
| 						const driveFile = JSON.parse(e.target.response); | ||||
|  | ||||
| 			xhr.upload.onprogress = e => { | ||||
| 				if (e.lengthComputable) { | ||||
| 					if (ctx.progress == undefined) ctx.progress = {}; | ||||
| 					ctx.progress.max = e.total; | ||||
| 					ctx.progress.value = e.loaded; | ||||
| 				} | ||||
| 			}; | ||||
| 						this.$emit('uploaded', driveFile); | ||||
|  | ||||
| 			xhr.send(data); | ||||
| 						this.uploads = this.uploads.filter(x => x.id != id); | ||||
| 						this.$emit('change', this.uploads); | ||||
| 					}; | ||||
|  | ||||
| 					xhr.upload.onprogress = e => { | ||||
| 						if (e.lengthComputable) { | ||||
| 							if (ctx.progress == undefined) ctx.progress = {}; | ||||
| 							ctx.progress.max = e.total; | ||||
| 							ctx.progress.value = e.loaded; | ||||
| 						} | ||||
| 					}; | ||||
|  | ||||
| 					xhr.send(data); | ||||
| 				}) | ||||
| 			} | ||||
| 			reader.readAsArrayBuffer(file); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| .mk-uploader | ||||
| 	overflow auto | ||||
|  | ||||
| @@ -100,7 +123,7 @@ export default Vue.extend({ | ||||
| 			margin 8px 0 0 0 | ||||
| 			padding 0 | ||||
| 			height 36px | ||||
| 			box-shadow 0 -1px 0 rgba($theme-color, 0.1) | ||||
| 			box-shadow 0 -1px 0 var(--primaryAlpha01) | ||||
| 			border-top solid 8px transparent | ||||
|  | ||||
| 			&:first-child | ||||
| @@ -127,7 +150,7 @@ export default Vue.extend({ | ||||
| 				padding 0 | ||||
| 				max-width 256px | ||||
| 				font-size 0.8em | ||||
| 				color rgba($theme-color, 0.7) | ||||
| 				color var(--primaryAlpha07) | ||||
| 				white-space nowrap | ||||
| 				text-overflow ellipsis | ||||
| 				overflow hidden | ||||
| @@ -145,17 +168,17 @@ export default Vue.extend({ | ||||
| 				font-size 0.8em | ||||
|  | ||||
| 				> .initing | ||||
| 					color rgba($theme-color, 0.5) | ||||
| 					color var(--primaryAlpha05) | ||||
|  | ||||
| 				> .kb | ||||
| 					color rgba($theme-color, 0.5) | ||||
| 					color var(--primaryAlpha05) | ||||
|  | ||||
| 				> .percentage | ||||
| 					display inline-block | ||||
| 					width 48px | ||||
| 					text-align right | ||||
|  | ||||
| 					color rgba($theme-color, 0.7) | ||||
| 					color var(--primaryAlpha07) | ||||
|  | ||||
| 					&:after | ||||
| 						content '%' | ||||
| @@ -174,10 +197,10 @@ export default Vue.extend({ | ||||
| 				overflow hidden | ||||
|  | ||||
| 				&::-webkit-progress-value | ||||
| 					background $theme-color | ||||
| 					background var(--primary) | ||||
|  | ||||
| 				&::-webkit-progress-bar | ||||
| 					background rgba($theme-color, 0.1) | ||||
| 					background var(--primaryAlpha01) | ||||
|  | ||||
| 			> .progress | ||||
| 				display block | ||||
| @@ -191,13 +214,13 @@ export default Vue.extend({ | ||||
| 				border-radius 4px | ||||
| 				background linear-gradient( | ||||
| 					45deg, | ||||
| 					lighten($theme-color, 30%) 25%, | ||||
| 					$theme-color               25%, | ||||
| 					$theme-color               50%, | ||||
| 					lighten($theme-color, 30%) 50%, | ||||
| 					lighten($theme-color, 30%) 75%, | ||||
| 					$theme-color               75%, | ||||
| 					$theme-color | ||||
| 					var(--primaryLighten30) 25%, | ||||
| 					var(--primary)               25%, | ||||
| 					var(--primary)               50%, | ||||
| 					var(--primaryLighten30) 50%, | ||||
| 					var(--primaryLighten30) 75%, | ||||
| 					var(--primary)               75%, | ||||
| 					var(--primary) | ||||
| 				) | ||||
| 				background-size 32px 32px | ||||
| 				animation bg 1.5s linear infinite | ||||
|   | ||||
| @@ -200,17 +200,17 @@ export default Vue.extend({ | ||||
| 		top 0 | ||||
| 		width 100% | ||||
|  | ||||
| root(isDark) | ||||
| .mk-url-preview | ||||
| 	> a | ||||
| 		display block | ||||
| 		font-size 14px | ||||
| 		border solid 1px isDark ? #191b1f : #eee | ||||
| 		border solid 1px var(--urlPreviewBorder) | ||||
| 		border-radius 4px | ||||
| 		overflow hidden | ||||
|  | ||||
| 		&:hover | ||||
| 			text-decoration none | ||||
| 			border-color isDark ? #4f5561 : #ddd | ||||
| 			border-color var(--urlPreviewBorderHover) | ||||
|  | ||||
| 			> article > header > h1 | ||||
| 				text-decoration underline | ||||
| @@ -235,11 +235,11 @@ root(isDark) | ||||
| 				> h1 | ||||
| 					margin 0 | ||||
| 					font-size 1em | ||||
| 					color isDark ? #d6dae0 : #555 | ||||
| 					color var(--urlPreviewTitle) | ||||
|  | ||||
| 			> p | ||||
| 				margin 0 | ||||
| 				color isDark ? #a4aab3 : #777 | ||||
| 				color var(--urlPreviewText) | ||||
| 				font-size 0.8em | ||||
|  | ||||
| 			> footer | ||||
| @@ -256,7 +256,7 @@ root(isDark) | ||||
| 				> p | ||||
| 					display inline-block | ||||
| 					margin 0 | ||||
| 					color isDark ? #b0b4bf : #666 | ||||
| 					color var(--urlPreviewInfo) | ||||
| 					font-size 0.8em | ||||
| 					line-height 16px | ||||
| 					vertical-align top | ||||
| @@ -322,10 +322,4 @@ root(isDark) | ||||
| 						width 12px | ||||
| 						height 12px | ||||
|  | ||||
| .mk-url-preview[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-url-preview:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -127,11 +127,9 @@ export default Vue.extend({ | ||||
| </script> | ||||
|  | ||||
| <style lang="stylus" scoped> | ||||
| @import '~const.styl' | ||||
|  | ||||
| $border-color = rgba(27, 31, 35, 0.15) | ||||
|  | ||||
| root(isDark) | ||||
| .mk-visibility-chooser | ||||
| 	position initial | ||||
|  | ||||
| 	> .backdrop | ||||
| @@ -141,11 +139,11 @@ root(isDark) | ||||
| 		z-index 10000 | ||||
| 		width 100% | ||||
| 		height 100% | ||||
| 		background isDark ? rgba(#000, 0.4) : rgba(#000, 0.1) | ||||
| 		background var(--modalBackdrop) | ||||
| 		opacity 0 | ||||
|  | ||||
| 	> .popover | ||||
| 		$bgcolor = isDark ? #2c303c : #fff | ||||
| 		$bgcolor = var(--popupBg) | ||||
| 		position absolute | ||||
| 		z-index 10001 | ||||
| 		width 240px | ||||
| @@ -189,18 +187,18 @@ root(isDark) | ||||
| 			display flex | ||||
| 			padding 8px 14px | ||||
| 			font-size 12px | ||||
| 			color isDark ? #fff : #666 | ||||
| 			color var(--popupFg) | ||||
| 			cursor pointer | ||||
|  | ||||
| 			&:hover | ||||
| 				background isDark ? #252731 : #eee | ||||
| 				background var(--faceClearButtonHover) | ||||
|  | ||||
| 			&:active | ||||
| 				background isDark ? #21242b : #ddd | ||||
| 				background var(--faceClearButtonActive) | ||||
|  | ||||
| 			&.active | ||||
| 				color $theme-color-foreground | ||||
| 				background $theme-color | ||||
| 				color var(--primaryForeground) | ||||
| 				background var(--primary) | ||||
|  | ||||
| 			> * | ||||
| 				user-select none | ||||
| @@ -222,11 +220,4 @@ root(isDark) | ||||
|  | ||||
| 				> span:last-child:not(:first-child) | ||||
| 					opacity 0.6 | ||||
|  | ||||
| .mk-visibility-chooser[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-visibility-chooser:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -38,23 +38,20 @@ export default Vue.extend({ | ||||
| 		return { | ||||
| 			fetching: true, | ||||
| 			notes: [], | ||||
| 			connection: null, | ||||
| 			connectionId: null | ||||
| 			connection: null | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	mounted() { | ||||
| 		this.fetch(); | ||||
|  | ||||
| 		this.connection = (this as any).os.streams.localTimelineStream.getConnection(); | ||||
| 		this.connectionId = (this as any).os.streams.localTimelineStream.use(); | ||||
| 		this.connection = (this as any).os.stream.useSharedConnection('localTimeline'); | ||||
|  | ||||
| 		this.connection.on('note', this.onNote); | ||||
| 	}, | ||||
|  | ||||
| 	beforeDestroy() { | ||||
| 		this.connection.off('note', this.onNote); | ||||
| 		(this as any).os.streams.localTimelineStream.dispose(this.connectionId); | ||||
| 		this.connection.dispose(); | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| @@ -90,8 +87,8 @@ export default Vue.extend({ | ||||
| 	opacity 0 | ||||
| 	transform translateY(-30px) | ||||
|  | ||||
| root(isDark) | ||||
| 	background isDark ? #282C37 : #fff | ||||
| .mk-welcome-timeline | ||||
| 	background var(--face) | ||||
|  | ||||
| 	> div | ||||
| 		> * | ||||
| @@ -101,8 +98,8 @@ root(isDark) | ||||
| 			padding 16px | ||||
| 			overflow-wrap break-word | ||||
| 			font-size .9em | ||||
| 			color isDark ? #fff : #4C4C4C | ||||
| 			border-bottom 1px solid isDark ? rgba(#000, 0.1) : rgba(#000, 0.05) | ||||
| 			color var(--noteText) | ||||
| 			border-bottom 1px solid var(--faceDivider) | ||||
|  | ||||
| 			&:after | ||||
| 				content "" | ||||
| @@ -137,26 +134,20 @@ root(isDark) | ||||
| 						overflow hidden | ||||
| 						font-weight bold | ||||
| 						text-overflow ellipsis | ||||
| 						color isDark ? #fff : #627079 | ||||
| 						color var(--noteHeaderName) | ||||
|  | ||||
| 					> .username | ||||
| 						margin 0 .5em 0 0 | ||||
| 						color isDark ? #606984 : #ccc | ||||
| 						color var(--noteHeaderAcct) | ||||
|  | ||||
| 					> .info | ||||
| 						margin-left auto | ||||
| 						font-size 0.9em | ||||
|  | ||||
| 						> .created-at | ||||
| 							color isDark ? #606984 : #c0c0c0 | ||||
| 							color var(--noteHeaderInfo) | ||||
|  | ||||
| 				> .text | ||||
| 					text-align left | ||||
|  | ||||
| .mk-welcome-timeline[data-darkmode] | ||||
| 	root(true) | ||||
|  | ||||
| .mk-welcome-timeline:not([data-darkmode]) | ||||
| 	root(false) | ||||
|  | ||||
| </style> | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user