Compare commits

..

179 Commits

Author SHA1 Message Date
syuilo
d10ad1b413 12.56.0 2020-11-14 14:34:04 +09:00
syuilo
34063a0b84 New Crowdin updates (#6820)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)
2020-11-14 14:32:53 +09:00
syuilo
8c9d975d69 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-11-14 14:32:04 +09:00
syuilo
03b072b894 Resolve #6704 2020-11-14 14:32:01 +09:00
futchitwo
d1bed49808 typoらしきものを修正 (#6825)
mame→name
2020-11-14 12:52:38 +09:00
syuilo
6c3417d9b5 Improve MkRadios 2020-11-14 12:50:24 +09:00
syuilo
ba65226460 UI整理 2020-11-14 12:16:28 +09:00
syuilo
9c9cd168ee Improve emoji picker 2020-11-14 11:47:30 +09:00
syuilo
abb3d2a8d9 Fix #6832 2020-11-14 09:59:33 +09:00
syuilo
637fe8a04b Update dependencies 🚀 2020-11-14 09:54:39 +09:00
syuilo
be321e95e5 Bump node version 2020-11-14 09:45:16 +09:00
MeiMei
ed46c1486c Fix toHtml (#6824) 2020-11-10 21:00:14 +09:00
syuilo
c9fcfc6862 Better MFM rendering 2020-11-09 22:32:01 +09:00
syuilo
8495e37566 Fix router 2020-11-09 22:31:50 +09:00
syuilo
247bd43ae2 12.55.0 2020-11-08 17:10:43 +09:00
syuilo
a6685b1559 New Crowdin updates (#6814)
* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Kabyle)

* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Uyghur)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)
2020-11-08 17:09:42 +09:00
syuilo
66c4e8064b Add bounce MFM animation 2020-11-08 17:08:51 +09:00
syuilo
9d1fa3f202 autoWatch機能を削除 2020-11-08 16:49:23 +09:00
syuilo
a6985d7dc7 Fix #6819 2020-11-08 16:37:51 +09:00
syuilo
027c021ac9 アスタリスク3つでのtadaアニメーションを復活 2020-11-08 16:35:22 +09:00
syuilo
604205ec09 MFMチートシートに数式追加 2020-11-08 16:14:49 +09:00
syuilo
77db016866 MFMチートシート 2020-11-08 15:24:46 +09:00
syuilo
c6a009dbae 最近使用した絵文字からリアクションピッカーに設定してある絵文字は除外するように 2020-11-08 13:58:16 +09:00
syuilo
4299e3f90c スマホでデスクトップモードにできないように 2020-11-08 13:39:36 +09:00
syuilo
19f4812c03 Remove outdated test 2020-11-08 12:42:13 +09:00
syuilo
d01c465a8d ユーザーピッカーに最近使用したユーザーを表示するように 2020-11-08 12:40:56 +09:00
syuilo
4f1409601e Respect order when userIds specified 2020-11-08 12:40:31 +09:00
syuilo
52cffe0864 絵文字ピッカーで最近使用した絵文字がバグっているのを修正
あとMkEmojiをリファクタリング
2020-11-08 12:08:07 +09:00
syuilo
0866d5c055 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-11-08 11:25:31 +09:00
syuilo
78c08f6503 Clean up 2020-11-08 11:25:28 +09:00
MeiMei
27d0ac3d75 In HTML to MFM, use angle bracket if needed (#6817) 2020-11-08 00:38:50 +09:00
syuilo
a8776002f3 Improve readability 2020-11-07 23:43:03 +09:00
syuilo
31aa008566 Improve MFM
MFMの構文を調整 + 新しいアニメーション追加
Resolve #6816
2020-11-07 23:41:21 +09:00
syuilo
9d405b4581 絵文字ピッカーでエンターしたときに検索結果の先頭のもので確定できるように 2020-11-07 21:33:36 +09:00
syuilo
80c490a18b 絵文字ピッカーでAND検索に対応 2020-11-07 21:28:28 +09:00
syuilo
30c9c3739f 12.54.0 2020-11-07 12:49:16 +09:00
syuilo
ee0e7a09e0 Update dependencies 🚀 2020-11-07 12:48:21 +09:00
syuilo
bfd9577f0d 絵文字ピッカーを開いたときに画面がスクロールするのを修正 2020-11-07 12:35:12 +09:00
syuilo
0cada4ca76 New Crowdin updates (#6801)
* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)
2020-11-07 12:32:27 +09:00
RaZZlom
a718ccc0b6 Add missing languages (#6800) 2020-11-07 12:32:08 +09:00
Xeltica
1fcfd8e645 ヘッダーに表示されるタブのインジケーターを実装 (#6803)
* Implemente indicators on headers

* 微調整
2020-11-07 12:31:23 +09:00
syuilo
c6dd932a0b Improve usability 2020-11-07 12:30:16 +09:00
syuilo
b79eed01e0 絵文字ピッカーの検索に絵文字をペーストしたとき確定されないのを修正 2020-11-07 10:56:38 +09:00
syuilo
3a7dbe9764 UI切り替えがサイドバーに設置されていない場合に機能しない問題を修正 2020-11-07 10:54:52 +09:00
syuilo
bef2534fa8 絵文字ピッカーを強化 + 絵文字ピッカーをリアクションピッカーとして使えるように
Resolve #5079
Resolve #3219
2020-11-07 10:43:27 +09:00
syuilo
888dcd2559 画像ダイアログでスクロールが発生しないように 2020-11-07 05:00:42 +09:00
syuilo
2b69fca6bd 12.53.0 2020-11-03 22:11:23 +09:00
syuilo
7d088d42b4 New Crowdin updates (#6797)
* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)
2020-11-03 22:08:29 +09:00
syuilo
f8ad303b13 リバーシの観戦者を表示するように
Resolve #1830
2020-11-03 22:02:55 +09:00
syuilo
3c59c6fc9b Refactoring 2020-11-03 20:36:12 +09:00
syuilo
7353d729d7 ヘッダーの戻るボタンがページリロードすると消える問題を修正 2020-11-03 20:28:38 +09:00
syuilo
62591e0e7a リバーシのリプレイ
Related #1240
2020-11-03 19:54:35 +09:00
syuilo
012f15d84b chore: β 2020-11-03 17:17:42 +09:00
syuilo
e57c6f94d2 Clean up 2020-11-03 17:16:38 +09:00
syuilo
40b27e8ad8 Clean up 2020-11-03 17:13:21 +09:00
syuilo
055e9f21b7 wip: Desktop UI 2020-11-03 17:04:37 +09:00
syuilo
d7085b17fe wip: Desktop UI 2020-11-03 17:00:47 +09:00
syuilo
0d4d7c9c0c Tweak page window initial size 2020-11-03 17:00:18 +09:00
syuilo
99209d36e1 Fix bug of c3ae6f3a4 2020-11-03 16:21:28 +09:00
syuilo
e2a9a0ff3d Refactoring 2020-11-03 15:59:13 +09:00
syuilo
ab166959a4 Improve performance 2020-11-03 15:49:31 +09:00
syuilo
ab692cfa3d Fix bug of c3ae6f3a4 2020-11-03 15:44:56 +09:00
syuilo
c3ae6f3a4a Refactor 2020-11-03 15:22:55 +09:00
syuilo
5ef4a52bbd Improve usability of emoji page 2020-11-03 10:54:41 +09:00
syuilo
582768a5e4 チャットリンクの挙動を改善 2020-11-03 10:43:50 +09:00
syuilo
1852d1cc6f Improve drive 2020-11-03 10:27:00 +09:00
syuilo
7a5a541a4e 相対パスでコピーされるのを修正 2020-11-03 10:06:19 +09:00
syuilo
72b03e009c 12.52.0 2020-11-02 17:28:13 +09:00
syuilo
1b113c1045 Update webpack 🚀 2020-11-02 17:28:02 +09:00
syuilo
54959557ea New Crowdin updates (#6766)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)
2020-11-02 17:27:42 +09:00
syuilo
d44cb7f256 Add new MFM animation syntax 2020-11-02 15:37:42 +09:00
syuilo
3d063c95d1 🎨 2020-11-02 15:16:48 +09:00
syuilo
09cab605fc Add new MFM animation 2020-11-02 15:16:37 +09:00
syuilo
666c8c0498 Improve usability 2020-11-01 22:49:08 +09:00
syuilo
d3e764d7f9 Improve task manager 2020-11-01 22:43:19 +09:00
syuilo
7060625adf Improve task manager etc 2020-11-01 22:09:16 +09:00
syuilo
21b6e23e98 メモリリークの一因になってそうだったのでrefを渡すのを削除 2020-11-01 15:04:46 +09:00
syuilo
a0f794e372 Improve task manager 2020-11-01 14:05:06 +09:00
syuilo
9195504329 Improve task manager 2020-11-01 13:38:48 +09:00
syuilo
8c5d9dd549 🎨 2020-11-01 12:32:34 +09:00
syuilo
580f6a5b6c Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-11-01 12:26:58 +09:00
syuilo
74e76b460b 🎨 2020-11-01 12:26:46 +09:00
syuilo
c4570b37b7 🎨 2020-11-01 12:26:38 +09:00
MeiMei
cd0b0012d9 メッセージ (トーク/チャット) 削除の連合 (#6789) 2020-11-01 12:14:42 +09:00
syuilo
c055b4d32d fix(client): ストリーミングのメモリリークを修正
SharedConnection や NonSharedConnection のインスタンスを Vue コンポーネントの data に含むと、Vue が Proxy に変換するため、Stream クラス内部でインスタンス同士の比較をしても false になり、使われなくなったインスタンスがメモリ上に残り続ける。
なお、チャンネルへの接続/切断は頻繁に行うものではないため、メモリリークといっても影響は軽微とみられる。
2020-11-01 11:57:34 +09:00
syuilo
75a9ff832a タスクマネージャー(wip) 2020-11-01 11:39:38 +09:00
syuilo
b64d3af1f3 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-10-31 10:19:12 +09:00
syuilo
fb6605bb40 API: blocking/create or deleteで完全なUser情報を返すように 2020-10-31 10:19:10 +09:00
takonomura
3bfae80fa7 補完でタブが効かなくなるケースを修正 (#6779) 2020-10-31 09:43:28 +09:00
syuilo
cb16cb0610 Fix #6781 2020-10-31 09:39:22 +09:00
takonomura
0baed1a275 チャンネル一覧でバナーが無いとチャンネル名が出ないのを修正 (#6778) 2020-10-31 05:53:02 +09:00
mintphin
42162c8015 TOOLS: Created demote tool based on mark-admin.ts (#6776)
* TOOLS: Created demote tool based on mark-admin.ts

* TOOLS: Removed trailing whitespace on demote-admin.ts
2020-10-31 00:21:02 +09:00
takonomura
0fab0c416d リバーシで相手のターンでも置くことができるのを修正 (#6777) 2020-10-30 22:39:33 +09:00
sobadon
e2e262c8ce リンクをコピーでパスしかコピーされない問題を修正 (#6785) 2020-10-30 18:22:14 +09:00
takonomura
cf6596203b 検索ショートカットが使えないのを修正 (#6783) 2020-10-30 16:42:51 +09:00
syuilo
471911a54f Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-10-28 22:48:25 +09:00
syuilo
9394f4f540 Fix error dialog 2020-10-28 22:47:57 +09:00
MeiMei
4e968216ad ドライブファイル参照がシステムユーザーで落ちるのを修正 (#6774) 2020-10-28 22:24:16 +09:00
syuilo
84a7a9555f ウィンドウ右クリックでサイドビューで開けるように 2020-10-28 22:21:53 +09:00
syuilo
8d12fd152b ウィンドウ内のリンクを右クリックしたときに「サイドビューで開く」が無いのを修正 2020-10-28 22:21:40 +09:00
syuilo
629b765abc 12.51.0 2020-10-27 18:29:15 +09:00
syuilo
63a89fa84a カスタム絵文字がつぶれる問題を修正 2020-10-27 18:28:37 +09:00
syuilo
a3f89236a0 New Crowdin updates (#6760)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Japanese, Kansai)
2020-10-27 18:24:34 +09:00
syuilo
01560abafb Fix #6762 2020-10-27 18:21:52 +09:00
MeiMei
b5698026ba Fix emojilist.json (#6764) 2020-10-27 18:15:35 +09:00
syuilo
6258ce75b7 Reversi (#6765)
* wip

* wip

* wip

* wip

* Update game.setting.vue

* wip

* wip

* Update game.setting.vue

* wip

* Update game.board.vue

* wip

* Update sidebar.ts
2020-10-27 18:11:41 +09:00
syuilo
6f34c74027 Update popup animation 2020-10-27 16:46:13 +09:00
syuilo
8add4f359b 🎨 2020-10-27 16:45:24 +09:00
syuilo
d8933c135f リモートインスタンス情報を強制更新するAPIを追加 2020-10-27 16:45:14 +09:00
syuilo
eb350e8d6c Better favicon detection 2020-10-27 16:44:54 +09:00
syuilo
615fedd64d Instance Ticker 2020-10-27 16:16:59 +09:00
syuilo
25bd82ecaa Default behavior option for MkA component 2020-10-27 13:53:47 +09:00
syuilo
e0938e5e3a Add animation of context menu 2020-10-25 23:22:27 +09:00
syuilo
ec5e6c8443 APIコンソール 2020-10-25 16:11:08 +09:00
syuilo
25d8077474 Fix bug 2020-10-25 15:45:47 +09:00
syuilo
06083f40d9 🎨 2020-10-25 12:47:40 +09:00
syuilo
ec203f7f79 Use MFM instead of v-html to avoid XSS 2020-10-25 12:25:13 +09:00
syuilo
1b30d7d47a Clean up 2020-10-25 11:29:10 +09:00
syuilo
d9be9c958f 投稿失敗したときにエラー表示するように 2020-10-25 11:19:20 +09:00
syuilo
ed09796e0d 🎨 2020-10-25 11:06:55 +09:00
syuilo
4bfa29c0ab コンテキストメニューの位置計算を改善 2020-10-25 11:01:03 +09:00
syuilo
4804bbb211 インポート/エクスポート設定を復活 2020-10-25 10:48:33 +09:00
syuilo
749102f9c2 ヘッダーにもコンテキストメニュー追加 2020-10-25 09:26:19 +09:00
syuilo
0bcb1434b0 Refactor 2020-10-25 09:15:20 +09:00
syuilo
2e537e618c 12.50.0 2020-10-25 01:30:38 +09:00
syuilo
fe3b7a2ad3 New Crowdin updates (#6756)
* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)
2020-10-25 01:24:55 +09:00
syuilo
90db793fd0 regesit 2020-10-25 01:24:01 +09:00
syuilo
7bd2a6ad61 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-10-25 01:23:41 +09:00
syuilo
745f4d2439 regedit 2020-10-25 01:23:23 +09:00
syuilo
254cfaea28 自前ルーティング (#6759)
* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip
2020-10-25 01:21:41 +09:00
syuilo
d4da5a1eea Update dependencies 🚀 2020-10-24 11:12:29 +09:00
syuilo
c0f8297414 Fix migration bug 2020-10-23 17:46:31 +09:00
syuilo
834cb2ea1a 12.49.1 2020-10-22 23:31:11 +09:00
syuilo
d82769abd4 New Crowdin updates (#6748)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)
2020-10-22 23:30:54 +09:00
syuilo
adf01ed4a4 Fix #6749 (#6754) 2020-10-22 23:10:23 +09:00
syuilo
09c007b3aa Update dependencies 🚀 2020-10-22 23:09:03 +09:00
syuilo
526ff177aa Update dependenceis 🚀 2020-10-21 22:20:03 +09:00
syuilo
0e40d4e796 Clean up 2020-10-21 21:50:24 +09:00
syuilo
172ebab7bd Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-10-21 19:36:50 +09:00
syuilo
aa4493fe5c Fix api permission definition 2020-10-21 19:36:47 +09:00
syuilo
a68a88f79e Update README.md 2020-10-20 17:32:17 +09:00
syuilo
1de7dc94e1 Delete CHANGELOG.md 2020-10-19 20:55:19 +09:00
syuilo
59cb7992e2 12.49.0 2020-10-19 19:43:30 +09:00
syuilo
87b15df47b Auto adjust window size 2020-10-19 19:42:55 +09:00
syuilo
6932d86240 New Crowdin updates (#6667)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (English)
2020-10-19 19:39:05 +09:00
syuilo
87f61e714a Resolve #6087 2020-10-19 19:29:04 +09:00
syuilo
5762e2d9ba 12.48.3 2020-10-19 15:06:26 +09:00
syuilo
f0691c8a4f 🎨 2020-10-19 15:05:29 +09:00
syuilo
6d7e4fe2a1 fb07116a4 のコミット忘れ 2020-10-19 14:50:57 +09:00
MeiMei
7dc789f470 Fix: optional chaining (#6747) 2020-10-19 14:48:56 +09:00
syuilo
190d1bbf3c デフォルト公開範囲が機能していない問題を修正 2020-10-19 14:46:55 +09:00
syuilo
a755dd5f9e Add note 2020-10-19 14:46:32 +09:00
syuilo
0846a7b94e Remove unused themes 2020-10-19 13:20:00 +09:00
syuilo
d8be0511f1 Fix design 2020-10-19 13:17:37 +09:00
syuilo
fb07116a4c 🎨 2020-10-19 13:17:11 +09:00
takonomura
fe453c15e3 ページのセクション内などが表示されない問題を修正 (#6746) 2020-10-19 09:30:21 +09:00
takonomura
059aeef6a0 MFM のバッククオートで囲ったコードが表示されないのを修正 (#6741) 2020-10-19 08:37:07 +09:00
okpierre
30e25451d6 Update settings.vue (#6742) 2020-10-19 08:23:50 +09:00
syuilo
7f0fd55c9a 12.48.2 2020-10-18 22:23:36 +09:00
syuilo
0e9b496deb 🎨 2020-10-18 21:21:52 +09:00
syuilo
8294c18e70 🎨 2020-10-18 18:50:45 +09:00
syuilo
39575b4696 🎨 2020-10-18 16:58:20 +09:00
syuilo
29e9801d5c Resolve #6684
Co-Authored-By: sobadon <37328795+sobadon@users.noreply.github.com>
2020-10-18 16:43:22 +09:00
syuilo
eaf83bffb0 LTL / GTLが無効でもボタンが表示されるのを修正 2020-10-18 16:33:23 +09:00
syuilo
bb25ece745 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-10-18 16:10:38 +09:00
syuilo
754b5629e4 🎨 2020-10-18 16:10:28 +09:00
MeiMei
ee63403548 Fix test (#6733) 2020-10-18 16:03:51 +09:00
syuilo
87edeb41da Fix poll editor bug 2020-10-18 15:52:34 +09:00
syuilo
41fe804587 Clean up 2020-10-18 15:52:26 +09:00
syuilo
02466acc4b Fix page bug 2020-10-18 12:55:26 +09:00
syuilo
8470a64e6b Fix page editor bug 2020-10-18 12:30:54 +09:00
syuilo
9939e0f9a9 Clean up 2020-10-18 12:30:47 +09:00
syuilo
ce5f552d0c Fix channel design 2020-10-18 12:16:42 +09:00
syuilo
7d4c535233 Make unrenote button danger 2020-10-18 10:38:35 +09:00
syuilo
57cd0fb93f Fix user page bug 2020-10-18 10:28:17 +09:00
syuilo
a15299ae53 Improve api error dialog 2020-10-18 10:21:02 +09:00
syuilo
1df7abfbb9 Improve waiting dialog 2020-10-18 10:11:34 +09:00
MeiMei
85a0f696bc ActivityPubでリモートのオブジェクトをGETするときのリクエストをHTTP Signatureで署名するオプション (#6731)
* Sign ActivityPub GET

* Fix v12, v12.48.0 UI bug
2020-10-18 01:46:40 +09:00
MeiMei
ba3c62bf9c Fix lint (#6732)
* Fix lint

* nl
2020-10-18 01:23:46 +09:00
286 changed files with 10144 additions and 6169 deletions

View File

@@ -154,3 +154,6 @@ id: 'aid'
# Media Proxy
#mediaProxy: https://example.com/proxy
# Sign to ActivityPub GET request (default: false)
#signToActivityPubGet: true

View File

@@ -1 +1 @@
v14.4.0
v15.0.1

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
FROM node:14.4.0-alpine AS base
FROM node:15.0.1-alpine AS base
ENV NODE_ENV=production

View File

@@ -8,7 +8,7 @@
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge&logo=github)](http://makeapullrequest.com)
[![Awesome Humane Tech](https://raw.githubusercontent.com/humanetech-community/awesome-humane-tech/main/humane-tech-badge.svg?sanitize=true)](https://github.com/humanetech-community/awesome-humane-tech)
**A forever evolving, sophisticated microblogging platform.**
**A forever evolving, professional microblogging platform.**
<p align="justify">
<a href="https://join.misskey.page/">Misskey</a> is a decentralized microblogging platform born on Earth.

View File

@@ -1,5 +1,6 @@
---
_lang_: "العربية"
introMisskey: "اهلا بك! ميسكي هو منصة تدوين مصغر لا مركزية ومفتوحة المصدر.\nيمكنك مشاركة \"ملاحظات\" عن ما يجري حولك، وإخبار الجميع عن نفسك 📡\nتسمح لك \"الانفعالات\" بتعبير عن شعورك حول ملاحظات الآخرين 👍\nاكتشف عالمًا جديدًا 🚀"
monthAndDay: "{day}/{month}"
search: "البحث"
notifications: "الإشعارات"
@@ -14,8 +15,12 @@ noNotes: "لم يتم العثور على أية ملاحظات"
noNotifications: "ليس هناك أية اشعارات"
instance: "مثيل الخادم"
settings: "الاعدادات"
basicSettings: "الاعدادات الأساسية"
otherSettings: "إعدادات أخرى"
openInWindow: "افتح في نافذة جديدة"
profile: "الملف التعريفي"
timeline: "الخيط الزمني"
noAccountDescription: "لم يكتب هذا المستخدم سيرته بعد."
login: "لِج"
loggingIn: "جارٍ تسجيل الدخول"
logout: "الخروج"
@@ -28,22 +33,31 @@ favorite: "إضافة إلى المفضلة"
favorites: "المفضلات"
unfavorite: "إزالة من المفضلة"
pin: "دبّسها على الصفحة الشخصية"
unpin: "ألغ تثبيتها من ملفك الشخصي"
copyContent: "انسخ المحتوى"
copyLink: "انسخ الرابط"
delete: "حذف"
deleteAndEdit: "إزالة وإعادة الصياغة"
deleteAndEditConfirm: "أمتأكد من حذف الملاحظة؟ ستفقد كل مشاركاتها، والتفاعلات، والردود عليها."
addToList: "أضفه إلى قائمة"
sendMessage: "أرسل رسالة"
copyUsername: "انسخ اسم المستخدم"
searchUser: "ابحث عن مستخدمين"
reply: "رد"
loadMore: "عرض المزيد"
youGotNewFollower: "يتابعك"
receiveFollowRequest: "تلقيت طلب متابعة"
followRequestAccepted: "قُبل طلب المتابعة"
mention: "أشر الى"
mentions: "الإشارات"
directNotes: "الملاحظات المباشرة"
importAndExport: "إستورد / صدر"
import: "استيراد"
export: "تصدير"
files: "الملفات"
download: "تنزيل"
driveFileDeleteConfirm: "أمتأكد من حذف ملف {name}؟ كل الملاحظات المُرفق بها هذا الملف ستحذف."
unfollowConfirm: "أمتأكد من إلغاء متابعة {name}؟"
lists: "القوائم"
noLists: "ليس لديك أية قائمة"
note: "ملاحظة"
@@ -53,8 +67,10 @@ followers: "المتابِعين"
followsYou: "يتابعك"
createList: "إنشاء قائمة"
manageLists: "إدارة القوائم"
error: "حدث خطأ ما"
error: "خطأ"
somethingHappened: "حدث خطأ"
retry: "حاول مجددًا"
pageLoadError: "فشل تحميل الصفحة"
enterListName: "اسم القائمة"
privacy: "الخصوصية"
makeFollowManuallyApprove: "القبول يدويا طلبات الإشتراك"
@@ -64,6 +80,7 @@ followRequest: "طلب اشتراك"
followRequests: "طلبات الإشتراك"
unfollow: "إلغاء الاشتراك"
followRequestPending: "طلبات الإشتراك المعلّقة"
enterEmoji: "أدخل إيموجي"
unrenote: "إلغاء مشاركة الملاحظة"
quote: "اقتبس"
pinnedNote: "ملاحظة مدبسة"
@@ -71,16 +88,26 @@ you: "أنت"
clickToShow: "اضغط للعرض"
sensitive: "محتوى حساس"
add: "إضافة"
reaction: "تفاعل"
rememberNoteVisibility: "تذكر إعدادت مدى رؤية الملاحظات"
attachCancel: "أزل المرفق"
enterFileName: "ادخل اسم الملف"
mute: "اكتم"
unmute: "إلغاء الكتم"
block: "احجب"
unblock: "إلغاء الحجب"
suspend: "علِق"
unsuspend: "ألغ التعليق"
blockConfirm: "أمتأكد من حجب هذا الحساب؟"
unblockConfirm: "أمتأكد من إلغاء حجب هذا الحساب؟"
selectList: "اختر قائمة"
editWidgetsExit: "تم"
customEmojis: "إيموجي مخصص"
addEmoji: "إضافة إيموجي"
cacheRemoteFiles: "خزن مؤقتا الملفات البعيدة"
autoAcceptFollowed: "اقبل طلبات المتابعة تلقائيا من الحسابات المتابَعة"
addAcount: "إضافة حساب"
loginFailed: "فشل الولوج"
showOnRemote: "رؤيته على مثيل الخادم البُعدي"
general: "الرئيسية"
wallpaper: "خلفية الشاشة"
@@ -88,6 +115,7 @@ setWallpaper: "استخدم خلفية الشاشة"
removeWallpaper: "إزالة خلفية الشاشة"
searchWith: "البحث: {q}"
youHaveNoLists: "لا تمتلك أية قائمة"
followConfirm: "أتريد متابعة {name}؟"
proxyAccount: "حساب وكيل البروكسي"
host: "المضيف"
selectUser: "حدّد مستخدمًا"
@@ -96,6 +124,8 @@ annotation: "التعليقات"
federation: "الفديرالية"
instances: "مثيل الخادم"
latestRequestSentAt: "آخر طلب أرسِل في"
latestRequestReceivedAt: "آخر طلب تُلقي في"
storageUsage: "مساحة التخزين المستخدمة"
charts: "المنحنيات البيانية"
perHour: "في الساعة"
perDay: "في اليوم"
@@ -127,7 +157,6 @@ processing: "المعالجة جارية"
preview: "معاينة"
default: "افتراضي"
noCustomEmojis: "ليس هناك إيموجيات"
customEmojisOfRemote: "الإيموجيات القادمة مِن مثيلات الخوادم الأخرى"
federating: "الفديرالية جارية"
blocked: "محجوب"
suspended: "مُعلّق"
@@ -256,7 +285,6 @@ unregister: "إلغاء التسجيل"
passwordLessLogin: "لِج مِن دون كلمة سرية"
resetPassword: "أعد تعيين كلمتك السرية"
newPasswordIs: "كلمتك السرية الجديدة هي {password}"
autoNoteWatch: "راقب الملاحظات تلقائيا"
share: "شارِك"
notFound: "غير موجود"
help: "المساعدة"
@@ -280,6 +308,7 @@ noteOf: "ملاحظات {user}"
inviteToGroup: "دعوة إلى فريق"
noMessagesYet: "ليس هناك رسائل بعد"
newMessageExists: "لقد تلقيت رسالة جديدة"
invitations: "دعوة"
invitationCode: "رمز الدعوة"
checking: "التحقق جارٍ"
available: "متوفر"
@@ -313,7 +342,6 @@ total: "المجموع"
weekOverWeekChanges: "أسبوعيا"
dayOverDayChanges: "يوميا"
appearance: "المظهر"
clinetSettings: "إعدادات التطبيق"
accountSettings: "إعدادات الحساب"
promotion: "ترقية"
promote: "روِّج"
@@ -351,6 +379,13 @@ smtpHost: "المضيف"
smtpUser: "اسم المستخدم"
smtpPass: "الكلمة السرية"
display: "المظهر"
_mfm:
mention: "أشر الى"
quote: "اقتبس"
emoji: "إيموجي مخصص"
search: "البحث"
_reversi:
total: "المجموع"
_channel:
featured: "المتداوَلة"
_sidebar:
@@ -366,6 +401,7 @@ _theme:
make: "إنشاء قالب"
alpha: "الشفافية"
keys:
mention: "أشر الى"
messageBg: "خلفية الدردشة"
_sfx:
note: "الملاحظات"
@@ -508,7 +544,9 @@ _notification:
youWereFollowed: "يتابعك"
_types:
follow: "المتابَعون"
mention: "أشر الى"
quote: "اقتبس"
reaction: "تفاعل"
_deck:
_columns:
notifications: "الإشعارات"

View File

@@ -16,6 +16,9 @@ noNotes: "Keine Notizen"
noNotifications: "Keine Benachrichtigungen"
instance: "Instanz"
settings: "Einstellungen"
basicSettings: "Allgemeine Einstellungen"
otherSettings: "Andere Einstellungen"
openInWindow: "In Fenster öffnen"
profile: "Profil"
timeline: "Chronik"
noAccountDescription: "Dieser Nutzer hat seine Profilbeschreibung noch nicht ausgefüllt."
@@ -40,6 +43,7 @@ deleteAndEditConfirm: "Möchtest du diese Notiz wirklich löschen und bearbeiten
addToList: "Zu Liste hinzufügen"
sendMessage: "Nachricht senden"
copyUsername: "Benutzernamen kopieren"
searchUser: "Benutzersuche"
reply: "Antworten"
loadMore: "Mehr anzeigen"
youGotNewFollower: "Du hast einen neuen Follower"
@@ -66,8 +70,11 @@ followers: "Gefolgt von"
followsYou: "Folgt dir"
createList: "Liste erstellen"
manageLists: "Listen verwalten"
error: "Ein Problem ist aufgetreten"
error: "Fehler"
somethingHappened: "Ein Fehler ist aufgetreten"
retry: "Wiederholen"
pageLoadError: "Laden der Seite fehlgeschlagen."
pageLoadErrorDescription: "Dieser Fehler wird meist durch Netzwerkfehler oder den Browser-Cache verursacht. Versuche den Browser-Cache zu leeren und es nach kurzer Zeit noch einmal zu probieren."
enterListName: "Listennamen eingeben"
privacy: "Privatsphäre"
makeFollowManuallyApprove: "Follow-Anfragen benötigen Bestätigung"
@@ -106,6 +113,8 @@ unsuspendConfirm: "Möchtest du die Sperrung dieses Benutzers wirklich aufheben?
selectList: "Wähle eine Liste aus"
selectAntenna: "Antenne auswählen"
selectWidget: "Widget auswählen"
editWidgets: "Widgets bearbeiten"
editWidgetsExit: "Fertig"
customEmojis: "Benutzerdefinierte Emojis"
emoji: "Emoji"
emojiName: "Emojiname"
@@ -177,7 +186,6 @@ processing: "In Bearbeitung"
preview: "Vorschau"
default: "Standard"
noCustomEmojis: "Es existieren keine Emojis"
customEmojisOfRemote: "Emojis von anderen Instanzen"
noJobs: "Es gibt keine Jobs"
federating: "Föderiert"
blocked: "Blockiert"
@@ -365,8 +373,6 @@ unregister: "Deaktivieren"
passwordLessLogin: "Passwortloses Anmelden einrichten"
resetPassword: "Passwort zurücksetzen"
newPasswordIs: "Das neue Passwort ist \"{password}\""
autoNoteWatch: "Notizen automatisch beobachten"
autoNoteWatchDescription: "Werde über Notizen, auf die du reagiert oder geantwortet hast, informiert"
reduceUiAnimation: "Animationen der Benutzeroberfläche reduzieren"
share: "Teilen"
notFound: "Nicht gefunden"
@@ -404,6 +410,7 @@ noMessagesYet: "Noch keine Nachrichten"
newMessageExists: "Du hast eine neue Nachricht"
onlyOneFileCanBeAttached: "Es kann pro Nachricht nur eine Datei angehängt werden"
signinRequired: "Anmeldung erforderlich"
invitations: "Einladungen"
invitationCode: "Einladungscode"
checking: "Wird überprüft..."
available: "Verfügbar"
@@ -445,7 +452,7 @@ total: "Gesamt"
weekOverWeekChanges: "Wöchentlich"
dayOverDayChanges: "Täglich"
appearance: "Aussehen"
clinetSettings: "Client-Einstellungen"
clientSettings: "Client-Einstellungen"
accountSettings: "Benutzerkonto-Einstellungen"
promotion: "Hervorgehoben"
promote: "Hervorheben"
@@ -476,6 +483,8 @@ newNoteRecived: "Es gibt neue Notizen"
sounds: "Töne"
listen: "Anhören"
none: "Keine"
showInPage: "In Seite anzeigen"
popout: "Pop-Up"
volume: "Lautstärke"
details: "Details"
chooseEmoji: "Wähle ein Emoji"
@@ -518,7 +527,6 @@ enableInfiniteScroll: "Automatisch mehr Notizen laden"
visibility: "Sichtbarkeit"
poll: "Umfrage"
useCw: "Inhalt verstecken"
fixedWidgetsPosition: "Widgetposition fixieren"
enablePlayer: "Video-Player öffnen"
disablePlayer: "Video-Player schließen"
expandTweet: "Tweet ausklappen"
@@ -532,6 +540,7 @@ pluginInstallWarn: "Installiere nur vertrauenswürdige Plugins."
deck: "Deck"
undeck: "Deck verlassen"
useBlurEffectForModal: "Weichzeichnungseffekt für Modals verwenden"
useFullReactionPicker: "Vollständige Reaktionsauswahl nutzen"
generateAccessToken: "Zugriffstoken generieren"
permission: "Berechtigungen"
enableAll: "Alle aktivieren"
@@ -564,8 +573,121 @@ overview: "Übersicht"
logs: "Logs"
delayed: "Verzögert"
database: "Datenbank"
channel: "Kanal"
channel: "Kanäle"
create: "Erstellen"
notificationSetting: "Benachrichtigungseinstellungen"
notificationSettingDesc: "Wähle die Art der anzuzeigenden Benachrichtigung"
useGlobalSetting: "Globale Einstellung verwenden"
useGlobalSettingDesc: "Wenn dies eingeschaltet ist, werden die Benachrichtigungseinstellungen deines Benutzerkontos verwendet. Wenn dies ausgeschaltet ist, können individuelle Einstellungen vorgenommen werden."
other: "Andere"
regenerateLoginToken: "Login-Token regenerieren"
regenerateLoginTokenDescription: "Den bei Logins intern verwendeten Token regenerieren. Normalerweise wird dies nicht benötigt. Bei Regeneration werden alle Geräte ausgeloggt."
setMultipleBySeparatingWithSpace: "Trenne Elemente durch ein Leerzeichen um mehrere Einstellungen zu kofigurieren."
fileIdOrUrl: "Datei-ID oder URL"
chatOpenBehavior: "Verhalten des Chatfensters bei Öffnung"
sample: "Beispiel"
abuseReports: "Melden"
reportAbuse: "Melden"
reportAbuseOf: "{name} melden"
fillAbuseReportDescription: "Bitte gib Details für diese Meldung an. Falls es sich um eine spezielle Notiz handelt, bitte gib dessen URL an."
abuseReported: "Die Meldung wurde versendet. Vielen Dank."
send: "Senden"
abuseMarkAsResolved: "Meldung als gelöst markieren"
openInNewTab: "In neuem Tab öffnen"
openInSideView: "In Seitenansicht öffnen"
defaultNavigationBehaviour: "Standardnavigationsverhalten"
editTheseSettingsMayBreakAccount: "Bei Bearbeitung dieser Einstellungen besteht die Gefahr, dein Benutzerkonto zu beschädigen."
instanceTicker: "Instanz-Informationen von Notizen"
waitingFor: "Warte auf {x}"
random: "Zufällig"
system: "System"
switchUi: "UI wechseln"
desktop: "Desktop"
_mfm:
cheatSheet: "MFM Spickzettel"
intro: "MFM ist eine an vielen Stellen verwendbare und Misskey-exklusive Markup-Sprache. Hier kannst du eine Liste von verfügbarer MFM-Syntax anschauen."
dummy: "Misskey erweitert die Welt des Fediverse"
mention: "Erwähnung"
mentionDescription: "Mit At-Zeichen und Nutzername kann ein individueller Nutzer angegeben werden."
hashtag: "Hashtag"
hashtagDescription: "Mit einer Raute und Text kann ein Hashtag angegeben werden."
url: "URL"
urlDescription: "URLs können angezeigt werden."
link: "Link"
linkDescription: "Ein spezifizierter Textabschnitt kann als URL angezeigt werden."
bold: "Fett"
boldDescription: "Zeichen zur Betonung dicker erscheinen lassen."
small: "Klein"
smallDescription: "Inhalt klein und dünn erscheinen lassen."
center: "Zentrieren"
centerDescription: "Inhalt zentriert anzeigen lassen."
inlineCode: "Code (Eingebettet)"
inlineCodeDescription: "Syntax-Hervorhebung für (Programm-)Code eingebettet anzeigen lassen."
blockCode: "Code (Block)"
blockCodeDescription: "Syntax-Hervorhebung für mehrzeiligen (Programm-)Code als Block anzeigen lassen."
inlineMath: "Mathe (Eingebettet)"
inlineMathDescription: "Mathematische Formeln (KaTeX) eingebettet anzeigen."
blockMath: "Mathe (Block)"
blockMathDescription: "Mehrzeilige mathematische Formeln (KaTeX) als Block einbetten."
quote: "Zitationen"
quoteDescription: "Inhalt als Zitat anzeigen lassen."
emoji: "Benutzerdefinierte Emojis"
emojiDescription: "Emoji-Namen mit Doppelpunkten umschließen, um benutzerdefinierte Emojis anzeigen zu lassen."
search: "Suche"
searchDescription: "Eine vorgefertige Suchanfragebox anzeigen lassen."
flip: "Spiegelung"
flipDescription: "Inhalt horizontal oder vertikal gespiegelt anzeigen lassen."
jelly: "Animation (Dehnen)"
jellyDescription: "Verleiht eine sich dehnende Animation."
tada: "Animation (Tada)"
tadaDescription: "Verleiht eine Animation mit \"Tada!\"-Gefühl"
jump: "Animation (Sprung)"
jumpDescription: "Verleiht eine springende Animation."
bounce: "Animation (Federn)"
bounceDescription: "Erzeugt eine federnde Animation."
shake: "Animation (Zittern)"
shakeDescription: "Verleiht eine zitternde Animation."
twitch: "Animation (Zucken)"
twitchDescription: "Verleiht eine sehr stark zuckende Animation."
spin: "Animation (Rotieren)"
spinDescription: "Verleiht eine rotierende Animation."
_reversi:
reversi: "Reversi"
gameSettings: "Spieleinstellungen"
chooseBoard: "Spielbrett auswählen"
blackOrWhite: "Schwarz/Weiß"
blackIs: "{name} spielt Schwarz"
rules: "Regeln"
botSettings: "Optionen des Computergegners"
thisGameIsStartedSoon: "Dieses Spiel beginnt in wenigen Sekunden"
waitingForOther: "Warte auf den Zug des Gegenspielers"
waitingForMe: "Warte auf deinen Zug"
waitingBoth: "Mach dich bereit"
ready: "Bereit"
cancelReady: "Nicht bereit"
opponentTurn: "Zug deines Gegners"
myTurn: "Dein Zug"
turnOf: "Zug von {name}"
pastTurnOf: "Zug von {name}"
surrender: "Aufgeben"
surrendered: "durch Aufgabe"
drawn: "Unentschieden"
won: "{name} hat gesiegt"
black: "Schwarz"
white: "Weiß"
total: "Gesamt"
turnCount: " Zug {count}"
myGames: "Meine Runden"
allGames: "Alle Runden"
ended: "Beendet"
playing: "Laufend"
isLlotheo: "Der mit weniger Steinen gewinnt (Llotheo)"
loopedMap: "Wiederholendes Spielbrett"
canPutEverywhere: "Steine können überall platziert werden"
_instanceTicker:
none: "Nie anzeigen"
remote: "Für Benutzer fremder Instanzen anzeigen"
always: "Immer anzeigen"
_serverDisconnectedBehavior:
reload: "Automatisch aktualisieren"
dialog: "Warnungsfenster zeigen"
@@ -576,13 +698,13 @@ _channel:
setBanner: "Kanalbanner festlegen"
removeBanner: "Kanalbanner entfernen"
featured: "Trends"
owned: "Besitzer"
following: "Folgt"
owned: "Besitzt"
following: "Gefolgt"
usersCount: "{n} Teilnehmer"
notesCount: "{n} Notizen"
_sidebar:
full: "Voll"
icon: "Profilbild"
icon: "Symbol"
hide: "Ausblenden"
_wordMute:
muteWords: "Wort stummschalten"
@@ -782,6 +904,7 @@ _widgets:
photos: "Fotos"
digitalClock: "Digitaluhr"
federation: "Föderation"
postForm: "Neue Notiz anfertigen"
_cw:
hide: "Ausblenden"
show: "Mehr anzeigen"
@@ -1238,14 +1361,17 @@ _notification:
youWereInvitedToGroup: "Du wurdest in eine Gruppe eingeladen"
_types:
all: "Alle"
follow: "Folgt"
mention: "Erwähnung"
follow: "Neue Follower"
mention: "Erwähnungen"
reply: "Antworten"
renote: "Renote"
quote: "Zitieren"
renote: "Renotes"
quote: "Zitationen"
reaction: "Reaktionen"
pollVote: "Umfragen"
receiveFollowRequest: "Follow-Anfragen"
pollVote: "Antworten auf Umfragen"
receiveFollowRequest: "Follow-Anfrage erhalten"
followRequestAccepted: "Follow-Anfrage akzeptiert"
groupInvited: "Gruppeneinladung erhalten"
app: "Benachrichtigungen von Apps"
_deck:
alwaysShowMainColumn: "Hauptspalte immer zeigen"
columnAlign: "Spalten ausrichten"

View File

@@ -16,6 +16,9 @@ noNotes: "No notes"
noNotifications: "No notifications"
instance: "Instance"
settings: "Settings"
basicSettings: "Basic Settings"
otherSettings: "Other Settings"
openInWindow: "Open in window"
profile: "Profile"
timeline: "Timeline"
noAccountDescription: "This user has not written their bio yet."
@@ -29,7 +32,7 @@ users: "Users"
addUser: "Add a user"
favorite: "Favorite"
favorites: "Favorites"
unfavorite: "Undo favorite"
unfavorite: "Unfavorite"
pin: "Pin to profile"
unpin: "Unpin from profile"
copyContent: "Copy contents"
@@ -40,6 +43,7 @@ deleteAndEditConfirm: "Are you sure you want to delete this note and edit it? Yo
addToList: "Add to list"
sendMessage: "Send a message"
copyUsername: "Copy username"
searchUser: "User search"
reply: "Reply"
loadMore: "Load more"
youGotNewFollower: "Followed you"
@@ -66,8 +70,11 @@ followers: "Followers"
followsYou: "Follows you"
createList: "Create list"
manageLists: "Manage lists"
error: "Something happened :("
error: "Error"
somethingHappened: "An error occurred"
retry: "Retry"
pageLoadError: "Failed to load page"
pageLoadErrorDescription: "This is normally caused by network errors or the browser's cache. Try clearung the cache and then try again after waiting a little while."
enterListName: "List name"
privacy: "Privacy"
makeFollowManuallyApprove: "Follow requests require approval"
@@ -106,6 +113,8 @@ unsuspendConfirm: "Are you sure you that want to unsuspend this account?"
selectList: "Select a list"
selectAntenna: "Select an Antenna"
selectWidget: "Select a widget"
editWidgets: "Edit widgets"
editWidgetsExit: "Done"
customEmojis: "Custom Emoji"
emoji: "Emoji"
emojiName: "Emoji name"
@@ -177,7 +186,6 @@ processing: "Processing"
preview: "Preview"
default: "Default"
noCustomEmojis: "There are no emojis"
customEmojisOfRemote: "Emojis from other instances"
noJobs: "There are no jobs"
federating: "Federating"
blocked: "Blocked"
@@ -365,8 +373,6 @@ unregister: "Unregister"
passwordLessLogin: "Set up password-less login"
resetPassword: "Reset password"
newPasswordIs: "The new password is \"{password}\""
autoNoteWatch: "Watch note automatically"
autoNoteWatchDescription: "Get notified about the notes which you reactioned or replied."
reduceUiAnimation: "Reduce UI animation"
share: "Share"
notFound: "Not found"
@@ -397,13 +403,14 @@ next: "Next"
retype: "Enter again"
noteOf: "{user}'s notes"
inviteToGroup: "Invite to group"
maxNoteTextLength: "Character limit of the note"
maxNoteTextLength: "Character limit of notes"
quoteAttached: "Quoted"
quoteQuestion: "Do you want to append a quote?"
noMessagesYet: "No messages yet"
newMessageExists: "You've got a new message"
onlyOneFileCanBeAttached: "You can only attach one file to a message"
signinRequired: "Please sign in"
invitations: "Invitations"
invitationCode: "Invitation code"
checking: "Checking"
available: "Available"
@@ -445,7 +452,7 @@ total: "Total"
weekOverWeekChanges: "Weekly"
dayOverDayChanges: "Daily"
appearance: "Appearance"
clinetSettings: "Client Settings"
clientSettings: "Client settings"
accountSettings: "Account Settings"
promotion: "Promoted"
promote: "Promote"
@@ -476,6 +483,8 @@ newNoteRecived: "You've got a new note"
sounds: "Sounds"
listen: "Listen"
none: "None"
showInPage: "Show in page"
popout: "Pop-out"
volume: "Volume"
details: "Details"
chooseEmoji: "Choose an emoji"
@@ -518,7 +527,6 @@ enableInfiniteScroll: "Enable infinite scrolling"
visibility: "Visiblility"
poll: "Poll"
useCw: "Hide content"
fixedWidgetsPosition: "Make widget position fixed"
enablePlayer: "Open video player"
disablePlayer: "Close video player"
expandTweet: "Expand tweet"
@@ -532,6 +540,7 @@ pluginInstallWarn: "Please do not install untrustworthy plugins."
deck: "Deck"
undeck: "Leave Deck"
useBlurEffectForModal: "Use blur effect for modals"
useFullReactionPicker: "Use full-size reaction picker"
generateAccessToken: "Generate access token"
permission: "Permissions"
enableAll: "Enable all"
@@ -564,8 +573,121 @@ overview: "Overview"
logs: "Logs"
delayed: "Delayed"
database: "Database"
channel: "Channel"
channel: "Channels"
create: "Create"
notificationSetting: "Notification settings"
notificationSettingDesc: "Select the type of notification to display"
useGlobalSetting: "Use global setting"
useGlobalSettingDesc: "If turned on, your account's notification settings will be used. If turned off, individual configurations can be made."
other: "Other"
regenerateLoginToken: "Regenerate login token"
regenerateLoginTokenDescription: "Regenerate the token used internally during login. Normally this action is not necessary. If regenerated, all devices will be logged out."
setMultipleBySeparatingWithSpace: "You can set multiple by separating them with spaces."
fileIdOrUrl: "File-ID or URL"
chatOpenBehavior: "Behavior of the chat window when opened"
sample: "Sample"
abuseReports: "Reports"
reportAbuse: "Report"
reportAbuseOf: "Report {name}"
fillAbuseReportDescription: "Please fill in the report details. If it is about a specific note, please include its URL."
abuseReported: "Your report has been sent. Thank you very much."
send: "Send"
abuseMarkAsResolved: "Mark report as resolved"
openInNewTab: "Open in new tab"
openInSideView: "Open in side view"
defaultNavigationBehaviour: "Default navigation behavior"
editTheseSettingsMayBreakAccount: "Editing these settings may damage your account."
instanceTicker: "Instance information of notes"
waitingFor: "Waiting for {x}"
random: "Random"
system: "System"
switchUi: "Switch UI"
desktop: "Desktop"
_mfm:
cheatSheet: "MFM Cheatsheet"
intro: "MFM is a Misskey-exclusive markup language that can be used in many places. Here you can view a list of all available MFM syntax."
dummy: "Misskey expands the world of the Fediverse"
mention: "Mention"
mentionDescription: "Using an At-Symbol and a username, you can specify a specific user."
hashtag: "Hashtag"
hashtagDescription: "Using a number sign and text, you can specify a hashtag."
url: "URL"
urlDescription: "URLs can be displayed."
link: "Link"
linkDescription: "Specific parts of text can be displayed as URL."
bold: "Bold"
boldDescription: "Highlights letters by making them thicker."
small: "Small"
smallDescription: "Displays contents small and thinn."
center: "Center"
centerDescription: "Displays content centered."
inlineCode: "Code (Inline)"
inlineCodeDescription: "Displays inline syntax highlighting for (program-)code."
blockCode: "Code (Block)"
blockCodeDescription: "Displays syntax highlighting for multi-line (program-)code in a block."
inlineMath: "Math (In-line)"
inlineMathDescription: "Display math formulas (KaTeX) in-line"
blockMath: "Math (Block)"
blockMathDescription: "Display multi-line Math formulas (KaTeX) in a block"
quote: "Quote"
quoteDescription: "Displays content as quote."
emoji: "Custom Emoji"
emojiDescription: "By surrounding a custom emoji name with colons, custom emoji can be displayed."
search: "Search"
searchDescription: "Displays a search box with pre-entered text."
flip: "Flip"
flipDescription: "Flips content horizontally or vertically."
jelly: "Animation (Jelly)"
jellyDescription: "Infuses a jelly-like animation."
tada: "Animation (Tada)"
tadaDescription: "Infuses a \"Tada!\"-like animation."
jump: "Animation (Jump)"
jumpDescription: "Infuses a jumping animation."
bounce: "Animation (Bounce)"
bounceDescription: "Causes a bouncy animation."
shake: "Animation (Shake)"
shakeDescription: "Infuses a shaking animation."
twitch: "Animation (Twitch)"
twitchDescription: "Infuses a strongly twitching animation."
spin: "Animation (Spin)"
spinDescription: "Infuses a spinning animation."
_reversi:
reversi: "Reversi"
gameSettings: "Game settings"
chooseBoard: "Choose a board"
blackOrWhite: "Black/White"
blackIs: "{name} is playing Black"
rules: "Rules"
botSettings: "Bot options"
thisGameIsStartedSoon: "The game will start in a few seconds"
waitingForOther: "Waiting for the opponent's turn"
waitingForMe: "Waiting for your turn"
waitingBoth: "Get ready"
ready: "Ready"
cancelReady: "Cancel ready"
opponentTurn: "Opponent's turn"
myTurn: "Your turn"
turnOf: "{name}'s turn"
pastTurnOf: "{name}'s turn"
surrender: "Surrender"
surrendered: "By surrender"
drawn: "Draw"
won: "{name}'s win"
black: "Black"
white: "White"
total: "Total"
turnCount: "Turn {count}"
myGames: "My rounds"
allGames: "All rounds"
ended: "Ended"
playing: "Currently playing"
isLlotheo: "The one with fewer stones wins (Llotheo)"
loopedMap: "Looped map"
canPutEverywhere: "Tiles are placeable everywhere"
_instanceTicker:
none: "Never show"
remote: "Show for remote users"
always: "Always show"
_serverDisconnectedBehavior:
reload: "Automatically reload"
dialog: "Show warning dialog"
@@ -576,8 +698,8 @@ _channel:
setBanner: "Set banner"
removeBanner: "Remove banner"
featured: "Trending"
owned: "Owner"
following: "Following"
owned: "Owned"
following: "Followed"
usersCount: "{n} Participants"
notesCount: "{n} Notes"
_sidebar:
@@ -782,6 +904,7 @@ _widgets:
photos: "Photos"
digitalClock: "Digital clock"
federation: "Federation"
postForm: "Compose a note"
_cw:
hide: "Hide"
show: "Load more"
@@ -1238,14 +1361,17 @@ _notification:
youWereInvitedToGroup: "Invited to group"
_types:
all: "All"
follow: "Following"
mention: "Mention"
follow: "Follows"
mention: "Mentions"
reply: "Replies"
renote: "Renote"
quote: "Quote"
reaction: "Reaction"
pollVote: "Polls"
receiveFollowRequest: "Follow requests"
renote: "Renotes"
quote: "Quotes"
reaction: "Reactions"
pollVote: "Votes on polls"
receiveFollowRequest: "Follow request received"
followRequestAccepted: "Follow request accepted"
groupInvited: "Invited to groups"
app: "Notifications from apps"
_deck:
alwaysShowMainColumn: "Always show main column"
columnAlign: "Align columns"

View File

@@ -16,6 +16,9 @@ noNotes: "No hay notas"
noNotifications: "No hay notificaciones"
instance: "Instancia"
settings: "Configuración"
basicSettings: "Configuración Básica"
otherSettings: "Configuración avanzada"
openInWindow: "Abrir en una ventana"
profile: "Perfil"
timeline: "Linea de tiempo"
noAccountDescription: "Este usuario no tiene una descripción"
@@ -40,6 +43,7 @@ deleteAndEditConfirm: "¿Quieres borrar y editar este nota? Las reacciones, reno
addToList: "Agregar a lista"
sendMessage: "Énviar mensaje"
copyUsername: "Copiar nombre de usuario"
searchUser: "Búsqueda de usuarios"
reply: "Responder"
loadMore: "Ver más"
youGotNewFollower: "te ha seguido"
@@ -66,8 +70,11 @@ followers: "Seguidores"
followsYou: "Te sigue"
createList: "Crear lista"
manageLists: "Administrar listas"
error: "Ocurrió un problema"
error: "Error"
somethingHappened: "Ocurrió un error"
retry: "Reintentar"
pageLoadError: "Error al leer la página"
pageLoadErrorDescription: "Normalmente es debido a la red o al caché del navegador. Por favor limpie el caché o intente más tarde."
enterListName: "Ingrese nombre de lista"
privacy: "Privacidad"
makeFollowManuallyApprove: "Aprobar manualmente las solicitudes de seguimiento"
@@ -106,6 +113,8 @@ unsuspendConfirm: "¿Quiere dejar de suspender esta cuenta?"
selectList: "Seleccione una lista"
selectAntenna: "Seleccionar antena"
selectWidget: "Seleccionar widget"
editWidgets: "Editar widgets"
editWidgetsExit: "Terminar edición"
customEmojis: "Emojis personalizados"
emoji: "Emoji"
emojiName: "Nombre del emoji"
@@ -177,7 +186,6 @@ processing: "Procesando"
preview: "Vista previa"
default: "Predeterminado"
noCustomEmojis: "No hay emojis personalizados"
customEmojisOfRemote: "Emojis remotos"
noJobs: "No hay trabajos"
federating: "Federando"
blocked: "Bloqueando"
@@ -365,8 +373,6 @@ unregister: "Cancelar registro"
passwordLessLogin: "Iniciar sesión sin contraseña"
resetPassword: "Resetear contraseña"
newPasswordIs: "La nueva contraseña es \"{password}\""
autoNoteWatch: "Ver nota automáticamente"
autoNoteWatchDescription: "Recibe notificaciones sobre las notas de otros usuarios que a los que respondiste y reaccionaste"
reduceUiAnimation: "Reducir la animación de la UI"
share: "Compartir"
notFound: "No se encuentra"
@@ -404,6 +410,7 @@ noMessagesYet: "Aún no hay chat"
newMessageExists: "Tienes un mensaje nuevo"
onlyOneFileCanBeAttached: "Solo se puede añadir un archivo al mensaje"
signinRequired: "Iniciar sesión"
invitations: "Invitar"
invitationCode: "Código de invitación"
checking: "Comprobando"
available: "Disponible"
@@ -445,7 +452,7 @@ total: "Total"
weekOverWeekChanges: "Dif semanal"
dayOverDayChanges: "Dif diaria"
appearance: "Apariencia"
clinetSettings: "Ajustes del cliente"
clientSettings: "Configuración del cliente"
accountSettings: "Ajustes de cuenta"
promotion: "Promovido"
promote: "Promover"
@@ -476,6 +483,8 @@ newNoteRecived: "Tienes una nota nuevo"
sounds: "Sonidos"
listen: "Escuchar"
none: "Ninguna"
showInPage: "Mostrar en la página"
popout: "Popout"
volume: "Volumen"
details: "Detalles"
chooseEmoji: "Elije un emoji"
@@ -518,7 +527,6 @@ enableInfiniteScroll: "Activar scroll infinito"
visibility: "Visibilidad"
poll: "Encuesta"
useCw: "Esconder contenidos"
fixedWidgetsPosition: "Fijar la posición de los widgets"
enablePlayer: "Abrir reproductor"
disablePlayer: "Cerrar reproductor"
expandTweet: "Expandir tweet"
@@ -532,6 +540,7 @@ pluginInstallWarn: "Por favor no instale plugins que no son de confianza"
deck: "Deck"
undeck: "Quitar deck"
useBlurEffectForModal: "Usar efecto borroso en modales"
useFullReactionPicker: "Reacción"
generateAccessToken: "Generar token de acceso"
permission: "Permisos"
enableAll: "Activar todo"
@@ -566,6 +575,88 @@ delayed: "atrasado"
database: "Base de datos"
channel: "Canal"
create: "Crear"
notificationSetting: "Ajustes de Notificaciones"
notificationSettingDesc: "Por favor elija el tipo de notificación a mostrar"
useGlobalSetting: "Usar ajustes globales"
useGlobalSettingDesc: "Al activarse, se usará la configuración de notificaciones de la cuenta, al desactivarse se pueden hacer configuraciones particulares."
other: "Otro"
regenerateLoginToken: "Regenerar token de login"
regenerateLoginTokenDescription: "Regenerar el token usado internamente durante el login. No siempre es necesario hacerlo. Al hacerlo de nuevo, se deslogueará en todos los dispositivos."
setMultipleBySeparatingWithSpace: "Puedes añadir mas de uno, separado por espacios."
fileIdOrUrl: "Id del archivo o URL"
chatOpenBehavior: "Comportamiento al abrir el chat"
sample: "Muestra"
abuseReports: "Reportes"
reportAbuse: "Reportar"
reportAbuseOf: "Reportar a {name}"
fillAbuseReportDescription: "Ingrese los detalles del reporte. Si hay una nota en particular, ingrese la URL de esta."
abuseReported: "Se ha enviado el reporte. Muchas gracias."
send: "Enviar"
abuseMarkAsResolved: "Marcar reporte como resuelto"
openInNewTab: "Abrir en una Nueva Pestaña"
openInSideView: "Abrir en una vista al costado"
defaultNavigationBehaviour: "Navegación por defecto"
editTheseSettingsMayBreakAccount: "Editar estas configuraciones puede dañar su cuenta."
instanceTicker: "Información de notas de la instancia"
waitingFor: "Esperando a {x}"
random: "Aleatorio"
system: "Sistema"
switchUi: "Cambiar interfaz de usuario"
desktop: "Escritorio"
_mfm:
cheatSheet: "Hoja de referencia de MFM"
intro: "MFM es un lenguaje de marcado dedicado que se puede usar en varios lugares dentro de Misskey. Aquí puede ver una lista de sintaxis disponibles en MFM."
mention: "Menciones"
mentionDescription: "El signo @ seguido de un nombre de usuario se puede utilizar para notificar a un usuario en particular."
hashtag: "Hashtag"
url: "URL"
link: "Vínculo"
bold: "Negrita"
center: "Centrar"
blockCode: "Código (bloque)"
blockCodeDescription: "Código de resaltado de sintaxis, como programas de varias líneas con bloques."
quote: "Citar"
emoji: "Emojis personalizados"
search: "Buscar"
flip: "Echar de un capirotazo"
flipDescription: "Voltea el contenido hacia arriba / abajo o hacia la izquierda / derecha."
_reversi:
reversi: "Reversi"
gameSettings: "Configuración del juego"
chooseBoard: "Elegir tablero"
blackOrWhite: "Blancas/Negras"
blackIs: "{name} juega con fichas negras"
rules: "Reglas"
botSettings: "Opciones del bot"
thisGameIsStartedSoon: "El juego empezará en segundos"
waitingForOther: "Esperando el turno del adversario"
waitingForMe: "Esperando mi turno"
waitingBoth: "Prepárate"
ready: "Listo"
cancelReady: "No estoy listo"
opponentTurn: "Turno del adversario"
myTurn: "Mi turno"
turnOf: "Turno de {name}"
pastTurnOf: "Turno de {name}"
surrender: "Rendirse"
surrendered: "Por rendirse"
drawn: "Empate"
won: "{name} ha ganado"
black: "Negro"
white: "Blanco"
total: "Total"
turnCount: "Turno {count}"
myGames: "Mis juegos"
allGames: "Todos los juegos"
ended: "Finalizado"
playing: "Jugando"
isLlotheo: "El que tenga menos fichas gana (LLoTheO)"
loopedMap: "Mapa en bucle"
canPutEverywhere: "Puedes colocar donde quieras"
_instanceTicker:
none: "No mostrar"
remote: "Mostrar a usuarios remotos"
always: "Mostrar siempre"
_serverDisconnectedBehavior:
reload: "Recargar automáticamente"
dialog: "Mostrar diálogo de advertencia"
@@ -782,6 +873,7 @@ _widgets:
photos: "Fotos"
digitalClock: "Reloj digital"
federation: "Federación"
postForm: "Formulario"
_cw:
hide: "Ocultar"
show: "Ver más"
@@ -1244,8 +1336,11 @@ _notification:
renote: "Renotar"
quote: "Citar"
reaction: "Reacción"
pollVote: "Encuestas"
receiveFollowRequest: "Solicitudes de seguimiento"
pollVote: "Votado en la encuesta"
receiveFollowRequest: "Recibió una solicitud de seguimiento"
followRequestAccepted: "El seguimiento fue aceptado"
groupInvited: "Invitado al grupo"
app: "Notificaciones desde aplicaciones"
_deck:
alwaysShowMainColumn: "Siempre mostrar la columna principal"
columnAlign: "Alinear columnas"

View File

@@ -16,6 +16,9 @@ noNotes: "Aucune note"
noNotifications: "Aucune notification"
instance: "Instance"
settings: "Paramètres"
basicSettings: "Paramètres basiques"
otherSettings: "Autres paramètres"
openInWindow: "Ouvrir dans une nouvelle fenêtre"
profile: "Profil"
timeline: "Fil"
noAccountDescription: "Lutilisateur·rice na pas encore renseigné de biographie de présentation sur son profil."
@@ -40,6 +43,7 @@ deleteAndEditConfirm: "Êtes-vous sûr·e de vouloir supprimer cette note et la
addToList: "Ajouter à une liste"
sendMessage: "Envoyer un message"
copyUsername: "Copier le nom dutilisateur·rice"
searchUser: "Chercher un·e utilisateur·rice"
reply: "Répondre"
loadMore: "Afficher plus …"
youGotNewFollower: "Vous suit"
@@ -66,8 +70,10 @@ followers: "Abonné·e·s"
followsYou: "Vous suit"
createList: "Créer une liste"
manageLists: "Gérer les listes"
error: "Une erreur est survenue"
error: "Erreur"
somethingHappened: "Une erreur est survenue"
retry: "Réessayer"
pageLoadError: "Le chargement de la page a échoué"
enterListName: "Nom de la liste"
privacy: "Confidentialité"
makeFollowManuallyApprove: "Accepter manuellement les demandes dabonnement"
@@ -106,6 +112,8 @@ unsuspendConfirm: "Êtes-vous sûr·e de vouloir annuler la suspension de ce com
selectList: "Sélectionner une liste"
selectAntenna: "Sélectionner une antenne"
selectWidget: "Sélectionner un widget"
editWidgets: "Modifier les widgets"
editWidgetsExit: "Fait"
customEmojis: "Émojis personnalisés"
emoji: "Émoji"
emojiName: "Nom de lémoji"
@@ -177,7 +185,6 @@ processing: "Traitement en cours"
preview: "Prévisualisation"
default: "Par défaut"
noCustomEmojis: "Il n'y a pas démoji"
customEmojisOfRemote: "Émojis provenant des autres instances"
noJobs: "Il ny a aucune tâche planifiée"
federating: "En cours de fédération"
blocked: "Bloqué·e"
@@ -264,6 +271,7 @@ rename: "Renommer"
avatar: "Avatar"
banner: "Bannière"
nsfw: "Contenu sensible"
whenServerDisconnected: "Lorsque la connexion au serveur est perdue"
disconnectedFromServer: "Déconnecté·e du serveur"
reload: "Rafraîchir"
doNothing: "Ignorer"
@@ -364,8 +372,6 @@ unregister: "Se désinscrire"
passwordLessLogin: "Connectez-vous sans mot de passe"
resetPassword: "Réinitialiser mot de passe"
newPasswordIs: "Votre nouveau mot de passe est \"{password}\""
autoNoteWatch: "Surveiller les notes automatiquement"
autoNoteWatchDescription: "Soyez informé des notes auxquelles vous avez réagi ou répondu."
reduceUiAnimation: "Réduire les animations dans linterface"
share: "Partager"
notFound: "Non trouvé"
@@ -403,6 +409,7 @@ noMessagesYet: "Pas encore discuté"
newMessageExists: "Vous avez un nouveau message"
onlyOneFileCanBeAttached: "Vous ne pouvez joindre quun seul fichier au message"
signinRequired: "Veuillez vous connecter"
invitations: "Inviter"
invitationCode: "Code dinvitation"
checking: "Vérification"
available: "Disponible"
@@ -444,7 +451,7 @@ total: "Total"
weekOverWeekChanges: "Diff hebdo"
dayOverDayChanges: "Diff quotidien"
appearance: "Aspect"
clinetSettings: "Paramètres du client"
clientSettings: "Paramètres du client"
accountSettings: "Paramètres du compte"
promotion: "Promu"
promote: "Promouvoir"
@@ -474,6 +481,7 @@ newNoteRecived: "Vous avez une nouvelle note"
sounds: "Sons"
listen: "Écouter"
none: "Rien"
popout: "Fenêtre contextuelle"
volume: "Volume"
details: "Détails"
chooseEmoji: "Choisissez un émoji"
@@ -516,7 +524,6 @@ enableInfiniteScroll: "Activer le défilement infini"
visibility: "Visibilité"
poll: "Sondage"
useCw: "Masquer le contenu"
fixedWidgetsPosition: "Rendre la position du widget fixe"
enablePlayer: "Activer le lecteur vidéo"
disablePlayer: "Désactiver le lecteur vidéo"
expandTweet: "Étendre le tweet"
@@ -538,6 +545,7 @@ tokenRequested: "Autoriser l'accès au compte"
pluginTokenRequestedDescription: "Ce plugin pourra utiliser les autorisations définies ici."
notificationType: "Type de notifications"
edit: "Editer"
useStarForReactionFallback: "Utiliser ★ comme alternative si lémoji de réaction est inconnu"
emailConfig: "Configuration du serveur email"
enableEmail: "Activer la distribution de courriel"
emailConfigInfo: "Utilisé pour confirmer votre adresse de courriel et la réinitialisation de votre mot de passe en cas doubli."
@@ -549,16 +557,56 @@ smtpUser: "Nom dutilisateur·rice"
smtpPass: "Mot de passe"
emptyToDisableSmtpAuth: "Laisser le nom dutilisateur et le mot de passe vides pour désactiver la vérification SMTP"
smtpSecure: "Utiliser SSL/TLS implicitement dans les connexions SMTP"
smtpSecureInfo: "Désactiver cette option lorsque STARTTLS est utilisé"
testEmail: "Tester la distribution de courriel"
wordMute: "Filtre de mots"
userSaysSomething: "{name} a dit quelque chose"
makeActive: "Activer"
display: "Affichage"
copy: "Copier"
metrics: "Métriques"
overview: "Aperçu"
logs: "Journaux"
delayed: "en retard"
database: "Base de données"
channel: "Canaux"
create: "Créer"
notificationSetting: "Paramètres des notifications "
notificationSettingDesc: "Sélectionnez le type de notification à afficher"
useGlobalSetting: "Utiliser paramètre général"
other: "Autre"
regenerateLoginToken: "Régénérer le jeton de connexion"
setMultipleBySeparatingWithSpace: "Vous pouvez définir plus dun, séparés par des espaces."
fileIdOrUrl: "ID du fichier ou URL"
chatOpenBehavior: "Comportement de la fenêtre de discussion lors de son ouverture"
random: "Aléatoire"
_mfm:
mention: "Mentionner"
hashtag: "Hashtags"
link: "Lien"
center: "Centrée"
quote: "Citer"
emoji: "Émojis personnalisés"
search: "Rechercher"
_reversi:
total: "Total"
_serverDisconnectedBehavior:
reload: "Rechargement automatique"
_channel:
create: "Créer un canal"
edit: "Éditer le canal"
removeBanner: "Supprimer la bannière"
featured: "Tendances"
usersCount: "{n} Participants"
notesCount: "{n} Notes"
_sidebar:
full: "Complet"
icon: "Avatar"
hide: "Masquer"
_wordMute:
muteWords: "Mot à mettre en sourdine"
muteWordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR."
mutedNotes: "Notes mises en sourdine"
_theme:
explore: "Explorer les thèmes"
install: "Installer un thème"
@@ -569,6 +617,8 @@ _theme:
invalid: "Le format du thème n'est pas valide"
make: "Créer un thème"
base: "Base"
addConstant: "Ajouter une constante"
constant: "Constante"
defaultValue: "Valeur par défaut"
color: "Couleur"
key: "Clé "
@@ -594,6 +644,7 @@ _theme:
renote: "Renote"
divider: "Séparateur"
infoWarnFg: "Texte davertissement"
cwBg: "Arrière-plan du CW"
badge: "Badge"
messageBg: "Arrière plan de la discussion"
_sfx:
@@ -678,6 +729,8 @@ _permissions:
"write:page-likes": "Mettre à jour les favoris sur les Pages"
"read:user-groups": "Voir les groupes d'utilisateur·rice·s"
"write:user-groups": "Éditer les groupes des utilisateur·rice·s"
"read:channels": "Lire les canaux"
"write:channels": "Modifier les canaux"
_auth:
shareAccess: "Autoriser \"{name}\" à accéder à votre compte ?"
shareAccessAsk: "Voulez-vous vraiment autoriser cette application à accéder à votre compte?"
@@ -711,6 +764,7 @@ _widgets:
photos: "Photos"
digitalClock: "Horloge numérique"
federation: "Fédération"
postForm: "Formulaire à publier"
_cw:
hide: "Masquer"
show: "Afficher plus …"
@@ -752,6 +806,7 @@ _visibility:
_postForm:
replyPlaceholder: "Répondre à cette note ..."
quotePlaceholder: "Citez cette note ..."
channelPlaceholder: "Publier vers le canal"
_placeholders:
a: "Quoi de neuf ?"
b: "Quoi de neuf ?"
@@ -1165,11 +1220,15 @@ _notification:
yourFollowRequestAccepted: "Votre demande dabonnement a été accepté"
youWereInvitedToGroup: "Invité au groupe"
_types:
all: "Toutes"
follow: "Abonnements"
mention: "Mentionner"
reply: "Réponses"
renote: "Renote"
quote: "Citer"
reaction: "Réactions"
groupInvited: "Invité aux groupes"
app: "Notifications provenant des apps"
_deck:
alwaysShowMainColumn: "Toujours afficher la colonne principale"
columnAlign: "Aligner les colonnes"

View File

@@ -15,17 +15,24 @@ const merge = (...args) => args.reduce((a, c) => ({
const languages = [
'ar-SA',
//'cs-CZ',
//'da-DK',
'cs-CZ',
'da-DK',
'de-DE',
'en-US',
'es-ES',
'fr-FR',
'ja-JP',
'ja-KS',
'kab-KAB',
'kn-IN',
'ko-KR',
//'nl-NL',
//'pl-PL',
'nl-NL',
'no-NO',
'pl-PL',
'pt-PT',
'ru-RU',
'ug-CN',
'uk-UA',
'zh-CN',
'zh-TW',
];

View File

@@ -214,6 +214,7 @@ imageUrl: "画像URL"
remove: "削除"
removed: "削除しました"
removeAreYouSure: "「{x}」を削除しますか?"
resetAreYouSure: "リセットしますか?"
saved: "保存しました"
messaging: "チャット"
upload: "アップロード"
@@ -373,8 +374,6 @@ unregister: "登録を解除"
passwordLessLogin: "パスワード無しログイン"
resetPassword: "パスワードをリセット"
newPasswordIs: "新しいパスワードは「{password}」です"
autoNoteWatch: "ノートの自動ウォッチ"
autoNoteWatchDescription: "あなたがリアクションしたり返信したりした他のユーザーのノートに関する通知を受け取るようにします。"
reduceUiAnimation: "UIのアニメーションを減らす"
share: "共有"
notFound: "見つかりません"
@@ -412,6 +411,7 @@ noMessagesYet: "まだチャットはありません"
newMessageExists: "新しいメッセージがあります"
onlyOneFileCanBeAttached: "メッセージに添付できるファイルはひとつです"
signinRequired: "ログインしてください"
invitations: "招待"
invitationCode: "招待コード"
checking: "確認しています"
available: "利用できます"
@@ -541,6 +541,7 @@ pluginInstallWarn: "信頼できないプラグインはインストールしな
deck: "デッキ"
undeck: "デッキ解除"
useBlurEffectForModal: "モーダルにぼかし効果を使用"
useFullReactionPicker: "フル機能リアクションピッカーを使用"
generateAccessToken: "アクセストークンの発行"
permission: "権限"
enableAll: "全て有効にする"
@@ -586,6 +587,111 @@ setMultipleBySeparatingWithSpace: "スペースで区切って複数設定でき
fileIdOrUrl: "ファイルIDまたはURL"
chatOpenBehavior: "チャットを開くときの動作"
sample: "サンプル"
abuseReports: "通報"
reportAbuse: "通報"
reportAbuseOf: "{name}を通報する"
fillAbuseReportDescription: "通報理由の詳細を記入してください。対象のートがある場合はそのURLも記入してください。"
abuseReported: "内容が送信されました。ご報告ありがとうございました。"
send: "送信"
abuseMarkAsResolved: "対応済みにする"
openInNewTab: "新しいタブで開く"
openInSideView: "サイドビューで開く"
defaultNavigationBehaviour: "デフォルトのナビゲーション"
editTheseSettingsMayBreakAccount: "これらの設定を編集するとアカウントが破損する可能性があります。"
instanceTicker: "ノートのインスタンス情報"
waitingFor: "{x}を待っています"
random: "ランダム"
system: "システム"
switchUi: "UI切り替え"
desktop: "デスクトップ"
_mfm:
cheatSheet: "MFMチートシート"
intro: "MFMは、Misskey内の様々な場所で使用できる専用のマークアップ言語です。ここでは、MFMで使用可能な構文一覧が確認できます。"
dummy: "MisskeyでFediverseの世界が広がります"
mention: "メンション"
mentionDescription: "アットマーク + ユーザー名で、特定のユーザーを示すことができます。"
hashtag: "ハッシュタグ"
hashtagDescription: "ナンバーサイン + タグで、ハッシュタグを示すことができます。"
url: "URL"
urlDescription: "URLを示すことができます。"
link: "リンク"
linkDescription: "文章の特定の範囲を、URLに紐づけることができます。"
bold: "太字"
boldDescription: "文字を太く表示して強調することができます。"
small: "目立たなく"
smallDescription: "内容を小さく・薄く表示させることができます。"
center: "中央寄せ"
centerDescription: "内容を中央寄せで表示させることができます。"
inlineCode: "コード(インライン)"
inlineCodeDescription: "プログラムなどのコードをインラインでシンタックスハイライトします。"
blockCode: "コード(ブロック)"
blockCodeDescription: "複数行のプログラムなどのコードをブロックでシンタックスハイライトします。"
inlineMath: "数式(インライン)"
inlineMathDescription: "数式(KaTeX)をインラインで表示します。"
blockMath: "数式(ブロック)"
blockMathDescription: "複数行の数式(KaTeX)をブロックで表示します。"
quote: "引用"
quoteDescription: "内容が引用であることを示すことができます。"
emoji: "カスタム絵文字"
emojiDescription: "コロンでカスタム絵文字名を囲むと、カスタム絵文字を表示させることができます。"
search: "検索"
searchDescription: "入力済み検索ボックスを表示させることができます。"
flip: "反転"
flipDescription: "内容を上下または左右に反転させます。"
jelly: "アニメーション(びよんびよん)"
jellyDescription: "びよんびよんするアニメーションを与えます。"
tada: "アニメーション(じゃーん)"
tadaDescription: "ジャーン!という感じのアニメーションを与えます。"
jump: "アニメーション(ジャンプ)"
jumpDescription: "飛び跳ねるようなアニメーションを与えます。"
bounce: "アニメーション(バウンド)"
bounceDescription: "ぽよんぽよん弾むようなアニメーションを与えます。"
shake: "アニメーション(ぶるぶる)"
shakeDescription: "ぶるぶるするアニメーションを与えます。"
twitch: "アニメーション(ブレ)"
twitchDescription: "激しくブレるアニメーションを与えます。"
spin: "アニメーション(回転)"
spinDescription: "回転するアニメーションを与えます。"
_reversi:
reversi: "リバーシ"
gameSettings: "対局の設定"
chooseBoard: "ボードを選択"
blackOrWhite: "先行/後攻"
blackIs: "{name}が黒(先行)"
rules: "ルール"
botSettings: "Botのオプション"
thisGameIsStartedSoon: "対局は数秒後に開始されます"
waitingForOther: "相手の準備が完了するのを待っています"
waitingForMe: "あなたの準備が完了するのを待っています"
waitingBoth: "準備してください"
ready: "準備完了"
cancelReady: "準備を再開"
opponentTurn: "相手のターンです"
myTurn: "あなたのターンです"
turnOf: "{name}のターンです"
pastTurnOf: "{name}のターン"
surrender: "投了"
surrendered: "投了により"
drawn: "引き分け"
won: "{name}の勝ち"
black: "黒"
white: "白"
total: "合計"
turnCount: "{count}ターン目"
myGames: "自分の対局"
allGames: "みんなの対局"
ended: "終了"
playing: "対局中"
isLlotheo: "石の少ない方が勝ち(ロセオ)"
loopedMap: "ループマップ"
canPutEverywhere: "どこでも置けるモード"
_instanceTicker:
none: "表示しない"
remote: "リモートユーザーに表示"
always: "常に表示"
_serverDisconnectedBehavior:
reload: "自動でリロード"

View File

@@ -1,12 +1,12 @@
---
_lang_: "日本語 (関西弁)"
introMisskey: "ようこそMisskeyは、オープンソースの分散型マイクロブログサービスやねん。\n「ート」を作成し、いま起こっとることを共有したり、あんたについて皆に発信しよう📡\n「リアクション」機能で、皆のートに素はよ反応を追加することもできます✌\n新しい世界を探検しよう🚀"
introMisskey: "ようこそMisskeyってのは、オープンソースの分散型マイクロブログサービスやねん。\n「ート」を作成し、いま起こっとることを共有したり、あんたんこととか皆に伝えていこう📡\n「リアクション」機能で、皆のートに素はよ反応を追加することもできるんやで✌\n新しい世界を探検してみらん?🚀"
monthAndDay: "{month}月 {day}日"
search: "探す"
notifications: "通知"
username: "ユーザー名"
password: "パスワード"
fetchingAsApObject: "連合に照会"
fetchingAsApObject: "今ちと連合に照会しとるで"
ok: "おっけー"
gotIt: "ほい"
cancel: "やめとくわ"
@@ -16,35 +16,40 @@ noNotes: "ノートはあらへん"
noNotifications: "通知はあらへん"
instance: "インスタンス"
settings: "設定"
basicSettings: "基本設定"
otherSettings: "その他の設定"
openInWindow: "ウィンドウで開いてや"
profile: "プロフィール"
timeline: "タイムライン"
noAccountDescription: "自己紹介はあらへん"
login: "ログイン"
loggingIn: "ログインしとります"
loggingIn: "ログインしよるで"
logout: "ログアウト"
signup: "新規登録"
uploading: "アップロードしとります"
save: "保存"
uploading: "アップロードしよるで"
save: "とっとく"
users: "ユーザー"
addUser: "ユーザー増やす"
addUser: "ユーザーを追加や"
favorite: "お気に入り"
favorites: "お気に入り"
unfavorite: "お気に入りやめる"
pin: "ピン留め"
unpin: "ピン留めやめる"
unfavorite: "やっぱ気に入らん"
pin: "ピン留めしとく"
unpin: "やっぱピン留めせん"
copyContent: "内容をコピー"
copyLink: "リンクをコピー"
delete: "ほかす"
deleteAndEdit: "ほかして直す"
deleteAndEditConfirm: "このートをほかしてもっかい直すこのートへのリアクション、Remote、返信も全部消えんで"
deleteAndEditConfirm: "このートをほかしてもっかい直すこのートへのリアクション、Renote、返信も全部消えるんやけどそれでもええん?"
addToList: "リストに入れたる"
sendMessage: "メッセージを送る"
copyUsername: "ユーザー名をコピー"
searchUser: "ユーザーを検索"
reply: "返す"
loadMore: "もっとあるやろ!"
youGotNewFollower: "フォローされたで"
receiveFollowRequest: "フォローリクエストされたで"
followRequestAccepted: "フォローが承認されたで"
mention: "メンション"
mentions: "あんた宛て"
directNotes: "ダイレクト投稿"
importAndExport: "インポートとエクスポート"
@@ -57,7 +62,7 @@ unfollowConfirm: "{name}のフォローを解除してもええんか?"
exportRequested: "エクスポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。エクスポート終わったら「ドライブ」に突っ込んどくで。"
importRequested: "インポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。"
lists: "リスト"
noLists: "リストあらへん"
noLists: "リストなんてあらへん"
note: "ノート"
notes: "ノート"
following: "フォロー"
@@ -65,32 +70,35 @@ followers: "フォロワー"
followsYou: "フォローされとるで"
createList: "リスト作る"
manageLists: "リストの管理"
error: "問題が発生してん"
retry: "もっぺんやってみる"
error: "エラー"
somethingHappened: "なんかアカンことが起こったで"
retry: "もっぺんやる?"
pageLoadError: "ページの読み込みに失敗してしもうたで…"
pageLoadErrorDescription: "これは普通、ネットワークかブラウザキャッシュが原因やからね。キャッシュをクリアするか、もうちっとだけ待ってくれへんか?"
enterListName: "リスト名を入れてや"
privacy: "プライバシーってなんや?オカンの年齢か"
makeFollowManuallyApprove: "他人のフォローは許可してからや!"
privacy: "プライバシーってなんや?"
makeFollowManuallyApprove: "他人からのフォローは自分が決める"
defaultNoteVisibility: "もとからの公開範囲"
follow: "フォロー"
followRequest: "フォロー許してくれや!言うてみる"
followRequests: "フォロー許してくれや!"
followRequest: "フォローを頼む"
followRequests: "フォローを頼む"
unfollow: "フォローやめる"
followRequestPending: "フォロー許してくれるん待っとる"
enterEmoji: "絵文字を入れてや"
renote: "Renote"
unrenote: "Renoteやめる"
quote: "引用"
pinnedNote: "ピン留めされノート"
pinnedNote: "ピン留めされとるノート"
you: "あんた"
clickToShow: "押してみ、見せたるわ"
sensitive: "見たらあかんで"
clickToShow: "押したら見えるようになるで"
sensitive: "ちょっとアカンやつやで"
add: "増やす"
reaction: "リアクション"
reactionSettingDescription: "リアクションピッカーに出しとくリアクションを選んでや。"
rememberNoteVisibility: "公開範囲覚えといて"
attachCancel: "くっつけるのやめよか"
markAsSensitive: "ちょっと見せられへんわ"
unmarkAsSensitive: "別にええんじゃね?"
attachCancel: "やっぱ添付やめてくれん?"
markAsSensitive: "ちょっとこれはアカン"
unmarkAsSensitive: "そこまでアカンことないやろ"
enterFileName: "ファイル名を入れてや"
mute: "ミュート"
unmute: "ミュートやめたる"
@@ -98,30 +106,35 @@ block: "ブロック"
unblock: "ブロックやめたる"
suspend: "凍結"
unsuspend: "溶かす"
blockConfirm: "ブロックしてしもうてええか?"
unblockConfirm: "ブロックすんのやめるけどええか?"
blockConfirm: "ブロックしてええか?"
unblockConfirm: "ブロックやめたるってほんまか?"
suspendConfirm: "凍結してしもうてええか?"
unsuspendConfirm: "解凍するけどええか?"
selectList: "リストを選ぶ"
selectAntenna: "アンテナを選ぶ"
selectWidget: "ウィジェットを選ぶ"
editWidgets: "ウィジェットをいじる"
editWidgetsExit: "編集終ったで"
customEmojis: "カスタム絵文字"
emoji: "絵文字"
emojiName: "絵文字名"
emojiUrl: "絵文字画像URL"
addEmoji: "絵文字を追加"
settingGuide: "ええ感じの設定"
cacheRemoteFiles: "リモートのファイルをキャッシュする"
cacheRemoteFilesDescription: "この設定をチャラにすると、リモートファイルをキャッシュせず直リンクするようになります。サーバーのストレージ節約できますが、サムネイルが生成されへんので通信量が増加します。"
flagAsBot: "Botやでと言っとく"
flagAsCat: "Catやでと言っとく"
autoAcceptFollowed: "フォローしとるユーザーからのフォロリクは全部勝手にええでって言うで"
cacheRemoteFilesDescription: "この設定を切っとくと、リモートファイルをキャッシュせず直リンクするようになってしまうんやで? サーバーのストレージ節約できるんやけど、かわりにサムネイルが作られんくなるから通信量が増えるで?"
flagAsBot: "Botやで"
flagAsCat: "Catやで"
autoAcceptFollowed: "フォローしとるユーザーからのフォロリクエストには勝手に許可しとくで。"
addAcount: "アカウント追加"
loginFailed: "ログインに失敗して"
loginFailed: "ログインに失敗してしもうた…"
showOnRemote: "リモートで見る"
general: "全般"
wallpaper: "壁紙"
setWallpaper: "壁紙を設定"
removeWallpaper: "壁紙ほかす"
removeWallpaper: "壁紙を削除"
searchWith: "検索: {q}"
youHaveNoLists: "リストあらへん"
youHaveNoLists: "リストあらへんで?"
followConfirm: "{name}をフォローしてええか?"
proxyAccount: "プロキシアカウント"
proxyAccountDescription: "プロキシアカウントは、代わりにフォローしてくれるアカウントや。例えば、551に豚まんが無いときやったり、ユーザーがリモートユーザーをアカウントに入れたとき、リストに入れられたユーザーが誰からもフォローされてないと寂しいやん。寂しいし、アクティビティも配達されへんから、プロキシアカウントがフォローしてくれるで。ええやつやん…"
@@ -131,7 +144,7 @@ recipient: "宛先"
annotation: "注釈"
federation: "連合"
instances: "インスタンス"
registeredAt: "一見さんになった日"
registeredAt: "初観測"
latestRequestSentAt: "ちょっと前のリクエスト送信"
latestRequestReceivedAt: "ちょっと前のリクエスト受信"
latestStatus: "ちょっと前のステータス"
@@ -173,7 +186,6 @@ processing: "処理しとる"
preview: "プレビュー"
default: "デフォルト"
noCustomEmojis: "絵文字はあらへん"
customEmojisOfRemote: "リモートの絵文字"
noJobs: "ジョブはあらへん"
federating: "連合しとる"
blocked: "ブロックしとる"
@@ -259,7 +271,8 @@ copyUrl: "URLをコピー"
rename: "名前を変えるで"
avatar: "アイコン"
banner: "バナー"
nsfw: "見たらあかんで"
nsfw: "ちょっとアカンやつやで"
whenServerDisconnected: "サーバーとの接続が失くなってしもうたとき"
disconnectedFromServer: "サーバーが機嫌悪いねん"
reload: "リロード"
doNothing: "何もせんとく"
@@ -295,7 +308,19 @@ proxyRemoteFilesDescription: "この設定を入れると、保存しとらん
driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量"
driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量"
inMb: "メガバイト単位"
iconUrl: "アイコン画像のURL"
bannerUrl: "バナー画像のURL"
basicInfo: "基本情報"
pinnedUsers: "ピン留めしたユーザー"
pinnedUsersDescription: "「みつける」ページとかにピン留めしたいユーザーをここに書けばええんやで。他ん人との名前は改行で区切ればええんやで。"
hcaptcha: "hCaptchaキャプチャ"
enableHcaptcha: "hCaptchaキャプチャをつけとく"
hcaptchaSiteKey: "サイトキー"
hcaptchaSecretKey: "シークレットキー"
recaptcha: "reCAPTCHA"
enableRecaptcha: "reCAPTCHAリキャプチャを有効にする"
recaptchaSiteKey: "サイトキー"
recaptchaSecretKey: "シークレットキー"
avoidMultiCaptchaConfirm: "ぎょうさんのCaptchaをつこてしまうと、仲良うせんことがあるんや。他のCaptchaをなおしとこか別にキャンセルしてもろうたらCaptchaは消されへんで済むけど知らんで。"
antennas: "アンテナ"
manageAntennas: "アンテナいじる"
@@ -348,17 +373,58 @@ unregister: "登録やめる"
passwordLessLogin: "パスワード無くてもログインできるようにする"
resetPassword: "パスワードをリセット"
newPasswordIs: "今度のパスワードは「{password}」や"
reduceUiAnimation: "UIの動きやアニメーションを減らしてくれや。"
share: "わけわけ"
notFound: "見つからへんね"
notFoundDescription: "指定されたURLに該当するページはあらへんやった。"
uploadFolder: "とりあえずここへアップロード"
cacheClear: "キャッシュをほかす"
markAsReadAllNotifications: "通知はもう全て読んだわっ"
markAsReadAllUnreadNotes: "投稿は全て読んだわっ"
markAsReadAllTalkMessages: "チャットはもうぜんぶ読んだわっ"
help: "ヘルプ"
inputMessageHere: "ここにメッセージ書いてや"
close: "さいなら"
group: "グループ"
groups: "グループ"
createGroup: "グループを作るで"
ownedGroups: "所有しとるグループ"
joinedGroups: "参加しとるグループ"
invites: "来てや"
groupName: "グループ名"
members: "メンバー"
transfer: "譲渡"
messagingWithUser: "ユーザーとチャット"
messagingWithGroup: "グループでチャット"
title: "タイトル"
text: "テキスト"
enable: "有効にするで"
next: "次"
retype: "もっかい入力"
noteOf: "{user}のノート"
inviteToGroup: "グループに招く"
maxNoteTextLength: "ノートの文字数制限"
quoteAttached: "引用付いとるで"
quoteQuestion: "引用として添付してもええか?"
noMessagesYet: "まだチャットはあらへんで"
newMessageExists: "新しいメッセージがきたで"
onlyOneFileCanBeAttached: "すまん、メッセージに添付できるファイルはひとつだけなんや。"
invitations: "来てや"
invitationCode: "招待コード"
checking: "確認しとるで"
smtpHost: "ホスト"
smtpUser: "ユーザー名"
smtpPass: "パスワード"
_mfm:
mention: "メンション"
quote: "引用"
emoji: "カスタム絵文字"
search: "探す"
_sidebar:
icon: "アイコン"
_theme:
keys:
mention: "メンション"
renote: "Renote"
_sfx:
note: "ノート"
@@ -442,6 +508,7 @@ _notification:
youWereFollowed: "フォローされたで"
_types:
follow: "フォロー"
mention: "メンション"
renote: "Renote"
quote: "引用"
reaction: "リアクション"

View File

@@ -35,6 +35,9 @@ userList: "Tibdarin"
uiLanguage: "Tutlayt n wegrudem"
smtpUser: "Isem n umseqdac"
smtpPass: "Awal uffir"
_mfm:
mention: "Bder"
search: "Nadi"
_theme:
keys:
mention: "Bder"

View File

@@ -56,6 +56,8 @@ instances: "ನಿದರ್ಶನ"
remove: "ಅಳಿಸು"
smtpUser: "ಬಳಕೆಹೆಸರು"
smtpPass: "ಗುಪ್ತಪದ"
_mfm:
search: "ಹುಡುಕು"
_sfx:
notification: "ಅಧಿಸೂಚನೆಗಳು"
_widgets:

View File

@@ -66,7 +66,6 @@ followers: "팔로워"
followsYou: "당신을 팔로우합니다"
createList: "리스트 만들기"
manageLists: "리스트 관리"
error: "오류가 발생했습니다"
retry: "다시 시도"
enterListName: "리스트 이름을 입력"
privacy: "프라이버시"
@@ -177,7 +176,6 @@ processing: "처리중"
preview: "미리보기"
default: "기본값"
noCustomEmojis: "이모지가 없습니다"
customEmojisOfRemote: "다른 인스턴스들의 이모지"
noJobs: "작업이 없습니다"
federating: "연합 중"
blocked: "차단됨"
@@ -364,8 +362,6 @@ unregister: "등록 해제"
passwordLessLogin: "비밀번호 없이 로그인"
resetPassword: "비밀번호 재설정"
newPasswordIs: "새로운 비밀번호는 \"{password}\" 입니다"
autoNoteWatch: "노트를 자동으로 지켜보기"
autoNoteWatchDescription: "리액션하거나 답글을 남긴 다른 유저의 노트에 대한 알림을 받습니다."
reduceUiAnimation: "UI의 애니메이션을 줄이기"
share: "공유"
notFound: "찾을 수 없습니다"
@@ -403,6 +399,7 @@ noMessagesYet: "아직 대화가 없습니다"
newMessageExists: "새 메시지가 있습니다"
onlyOneFileCanBeAttached: "메시지에 첨부할 수 있는 파일은 하나까지입니다"
signinRequired: "로그인 해주세요"
invitations: "초대"
invitationCode: "초대 코드"
checking: "확인하는 중입니다"
available: "사용 가능합니다"
@@ -443,7 +440,6 @@ remote: "리모트"
total: "합계"
weekOverWeekChanges: "지난주보다"
dayOverDayChanges: "어제보다"
clinetSettings: "클라이언트 설정"
accountSettings: "계정 설정"
promotion: "프로모션"
promote: "프로모션하기"
@@ -515,7 +511,6 @@ enableInfiniteScroll: "자동으로 좀 더 보기"
visibility: "공개 범위"
poll: "투표"
useCw: "내용 숨기기"
fixedWidgetsPosition: "위젯의 위치 고정"
enablePlayer: "플레이어 열기"
disablePlayer: "플레이어 닫기"
expandTweet: "트윗 확장하기"
@@ -552,6 +547,17 @@ copy: "복사"
logs: "로그"
database: "데이터베이스"
channel: "채널"
random: "랜덤"
_mfm:
mention: "멘션"
hashtag: "해시태그"
link: "링크"
center: "가운데 정렬"
quote: "인용"
emoji: "커스텀 이모지"
search: "검색"
_reversi:
total: "합계"
_channel:
create: "채널 생성"
setBanner: "배너 설정"
@@ -708,6 +714,7 @@ _widgets:
photos: "사진"
digitalClock: "디지털 시계"
federation: "연합"
postForm: "글 입력란"
_cw:
hide: "숨기기"
show: "더 보기"
@@ -1165,7 +1172,6 @@ _notification:
renote: "Renote"
quote: "인용"
reaction: "리액션"
receiveFollowRequest: "팔로우 요청"
_deck:
alwaysShowMainColumn: "메인 칼럼 항상 표시"
columnAlign: "칼럼 정렬"

View File

@@ -1,2 +1,23 @@
---
_lang_: "język polski"
search: "Szukaj"
notifications: "Powiadomienia"
username: "Nazwa użytkownika"
password: "Hasło"
ok: "OK"
gotIt: "Rozumiem!"
cancel: "Anuluj"
enterUsername: "Wprowadź nazwę użytkownika"
smtpUser: "Nazwa użytkownika"
smtpPass: "Hasło"
_mfm:
search: "Szukaj"
_sfx:
notification: "Powiadomienia"
_widgets:
notifications: "Powiadomienia"
_profile:
username: "Nazwa użytkownika"
_deck:
_columns:
notifications: "Powiadomienia"

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,5 @@
---
_lang_: "ياپونچە"
search: "ئىزدەش"
_mfm:
search: "ئىزدەش"

482
locales/uk-UA.yml Normal file
View File

@@ -0,0 +1,482 @@
---
_lang_: "Українська"
monthAndDay: "{month}/{day}"
search: "Пошук"
notifications: "Сповіщення"
username: "Ім'я користувача"
password: "Пароль"
fetchingAsApObject: "Отримуємо з федіверсу..."
ok: "OK"
gotIt: "Зрозуміло!"
cancel: "Скасувати"
enterUsername: "Введіть ім'я користувача"
renotedBy: "Поширено {user}"
noNotes: "Немає дописів"
noNotifications: "Немає сповіщень"
instance: "Інстанс"
settings: "Налаштування"
basicSettings: "Основні налаштування"
otherSettings: "Інші налаштування"
openInWindow: "Відкрити у вікні"
profile: "Профіль"
timeline: "Стрічка"
noAccountDescription: "Цей користувач ще нічого не написав про себе"
login: "Увійти"
loggingIn: "Здійснюємо вхід..."
logout: "Вийти"
signup: "Реєстрація"
uploading: "Завантаження..."
save: "Зберегти"
users: "Користувачі"
addUser: "Додати користувача"
favorite: "Обране"
favorites: "Обране"
unfavorite: "Видалити з обраного"
pin: "Закріпити"
unpin: "Відкріпити"
copyContent: "Скопіювати контент"
copyLink: "Скопіювати посилання"
delete: "Видалити"
deleteAndEdit: "Видалити й редагувати"
addToList: "Додати до списку"
sendMessage: "Надіслати повідомлення"
copyUsername: "Скопіювати ім’я користувача"
searchUser: "Пошук користувачів"
reply: "Відповісти"
loadMore: "Показати більше"
youGotNewFollower: "У вас новий підписник"
receiveFollowRequest: "Отримано запит на підписку"
followRequestAccepted: "Запит на підписку прийнято"
mention: "Згадка"
mentions: "Згадки"
directNotes: "Прямі повідомлення"
importAndExport: "Імпорт та експорт"
import: "Імпорт"
export: "Експорт"
files: "Файли"
download: "Завантажити"
unfollowConfirm: "Ви впевнені, що хочете відписатися від {name}?"
lists: "Списки"
noLists: "Немає списків"
note: "Дописи"
notes: "Дописи"
following: "Підписки"
followers: "Підписники"
followsYou: "Підписаний(-а) на вас"
createList: "Створити список"
manageLists: "Управління списками"
error: "Помилка"
somethingHappened: "Щось пішло не так"
retry: "Спробувати знову"
pageLoadError: "Помилка при завантаженні сторінки"
enterListName: "Введіть назву списку"
privacy: "Приватність"
makeFollowManuallyApprove: "Підтверджувати підписників уручну"
defaultNoteVisibility: "Видимість допису за замовчуванням"
follow: "Підписка"
followRequest: "Запит на підписку"
followRequests: "Запити на підписку"
unfollow: "Відписатися"
followRequestPending: "Очікуючі запити на підписку"
enterEmoji: "Введіть емодзі"
renote: "Поширити"
unrenote: "Відміна поширення"
quote: "Цитата"
pinnedNote: "Закріплений допис"
you: "Ви"
clickToShow: "Натисніть для перегляду"
sensitive: "NSFW"
add: "Додати"
reaction: "Реакції"
rememberNoteVisibility: "Пам’ятати видимість дописів"
attachCancel: "Видалити вкладення"
markAsSensitive: "Позначити як NSFW"
unmarkAsSensitive: "Зняти позначку NSFW"
enterFileName: "Введіть ім'я файлу"
mute: "Ігнорувати"
unmute: "Показувати"
block: "Заблокувати"
unblock: "Розблокувати"
suspend: "Призупинити"
unsuspend: "Відновити"
blockConfirm: "Ви впевнені, що хочете заблокувати цей акаунт?"
unblockConfirm: "Ви впевнені, що хочете розблокувати цей акаунт?"
suspendConfirm: "Ви впевнені, що хочете призупинити цей акаунт?"
unsuspendConfirm: "Ви впевнені, що хочете відновити цей акаунт?"
selectList: "Виберіть список"
selectAntenna: "Виберіть антену"
selectWidget: "Виберіть віджет"
editWidgets: "Редагувати віджети"
editWidgetsExit: "Готово"
customEmojis: "Кастомні емоджі"
emoji: "Емоджі"
emojiName: "Назва емоджі"
emojiUrl: "URL емодзі"
addEmoji: "Додати емодзі"
settingGuide: "Рекомендована конфігурація"
cacheRemoteFiles: "Кешувати дані з інших інстансів"
flagAsBot: "Акаунт бота"
flagAsCat: "Акаунт кота"
autoAcceptFollowed: "Автоматично приймати запити на підписку від користувачів, на яких ви підписані"
addAcount: "Додати акаунт"
loginFailed: "Не вдалося увійти"
showOnRemote: "Переглянути в оригіналі"
general: "Загальне"
wallpaper: "Шпалери"
setWallpaper: "Встановити шпалери"
removeWallpaper: "Прибрати шпалери"
searchWith: "Шукати з {q}"
youHaveNoLists: "У вас немає списків"
followConfirm: "Підписатися на {name}?"
proxyAccount: "Проксі-акаунт"
host: "Хост"
selectUser: "Виберіть користувача"
recipient: "Кому"
annotation: "Коментар"
federation: "Федіверс"
instances: "Інстанс"
registeredAt: "Приєднався(-лась)"
latestRequestSentAt: "Останній запит надіслано"
latestRequestReceivedAt: "Останній запит прийнято"
latestStatus: "Останній статус"
storageUsage: "Використання простіру"
charts: "Графіки"
perHour: "Щогодини"
perDay: "Щоденно"
stopActivityDelivery: "Припинити розсилання активності"
blockThisInstance: "Заблокувати цей інстанс"
operations: "Операції"
software: "Програмне забезпечення"
version: "Версія"
metadata: "Метадані"
withNFiles: "файли: {n}"
monitor: "Монітор"
jobQueue: "Черга завдань"
cpuAndMemory: "ЦП та пам'ять"
network: "Мережа"
disk: "Диск"
instanceInfo: "Про цей інстанс"
statistics: "Статистика"
clearQueue: "Очистити чергу"
clearQueueConfirmTitle: "Ви впевнені, що хочете очистити чергу?"
clearCachedFiles: "Очистити кеш"
clearCachedFilesConfirm: "Ви впевнені, що хочете видалити всі кешовані файли?"
blockedInstances: "Заблоковані інстанси"
muteAndBlock: "Ігнор і блокування"
mutedUsers: "Ігноровані користувачі"
blockedUsers: "Заблоковані користувачі"
noUsers: "Немає користувачів"
editProfile: "Редагувати профіль"
noteDeleteConfirm: "Ви дійсно хочете видалити цей допис?"
pinLimitExceeded: "Більше дописів не можна закріпити"
done: "Готово"
processing: "Обробка"
preview: "Передогляд"
default: "За умовчанням"
noCustomEmojis: "Немає кастомних емоджі"
noJobs: "Немає завдань"
federating: "Федерується"
blocked: "Заблоковано"
suspended: "Призупинено"
notResponding: "Не відповідає"
changePassword: "Змінити пароль"
security: "Безпека"
currentPassword: "Поточний пароль"
newPassword: "Новий пароль"
newPasswordRetype: "Новий пароль (повторно)"
attachFile: "Вкласти файл"
more: "Бiльше!"
featured: "Виділено"
noSuchUser: "Користувача не знайдено"
lookup: "Пошук"
announcements: "Оголошення"
imageUrl: "URL зображення"
remove: "Видалити"
removed: "Видалено"
saved: "Збережено"
messaging: "Чати"
upload: "Завантажити"
fromDrive: "З диска"
fromUrl: "З URL"
uploadFromUrl: "Завантажити з URL"
explore: "Огляд"
games: "Ігри Misskey"
noMoreHistory: "Подальшої історії немає"
start: "Розпочати"
home: "Домівка"
activity: "Активність"
images: "Зображення"
birthday: "День народження"
yearsOld: "{age} років"
registeredDate: "Приєднався(-лась)"
location: "Локація"
theme: "Тема"
themeForLightMode: "Світла тема"
themeForDarkMode: "Темна тема"
light: "Світла"
dark: "Темна"
lightThemes: "Світлі теми"
darkThemes: "Темні теми"
drive: "Диск"
fileName: "Ім'я файлу"
selectFile: "Вибрати файл"
selectFiles: "Вибрати файли"
selectFolder: "Вибрати теку"
selectFolders: "Вибрати теки"
renameFile: "Перейменувати файл"
folderName: "Ім'я теки"
createFolder: "Створити теку"
renameFolder: "Перейменувати теку"
deleteFolder: "Видалити теку"
addFile: "Додати файл"
emptyDrive: "Диск порожній"
emptyFolder: "Тека порожня"
unableToDelete: "Видалення неможливе"
inputNewFileName: "Введіть ім'я нового файлу"
inputNewFolderName: "Введіть ім'я нової теки"
hasChildFilesOrFolders: "Ця тека не порожня і не може бути видалена"
copyUrl: "Копіювати URL"
rename: "Перейменувати"
avatar: "Аватар"
banner: "Банер"
nsfw: "NSFW"
disconnectedFromServer: "Підключення до сервера було перервано"
reload: "Оновити"
doNothing: "Нічого не робити"
reloadConfirm: "Перезавантажити стрічку?"
watch: "Стежити"
unwatch: "Не стежити"
accept: "Прийняти"
reject: "Відхилити"
instanceName: "Назва інстансу"
instanceDescription: "Описання інстансу"
maintainerName: "Ім'я адміністратора"
maintainerEmail: "Email адміністратора"
tosUrl: "URL умов використання"
thisYear: "Рік"
thisMonth: "Місяць"
today: "День"
dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Сторінки"
integration: "Інтеграція"
connectSerice: "Під’єднатися"
disconnectSerice: "Відключитися"
enableLocalTimeline: "Увімкнути локальну стрічку"
enableGlobalTimeline: "Увімкнути глобальну стрічку"
registration: "Реєстрація"
enableRegistration: "Дозволити реєстрацію"
invite: "Запрошення"
proxyRemoteFiles: "Проксувати файли з інших інстансів"
iconUrl: "URL аватара"
bannerUrl: "URL банера"
basicInfo: "Основна інформація"
pinnedUsers: "Закріплені користувачі"
hcaptcha: "hCaptcha"
enableHcaptcha: "Увімкнути hCaptcha"
hcaptchaSiteKey: "Ключ сайту"
hcaptchaSecretKey: "Секретний ключ"
recaptcha: "reCAPTCHA"
enableRecaptcha: "Увімкнути reCAPTCHA"
recaptchaSiteKey: "Ключ сайту"
recaptchaSecretKey: "Секретний ключ"
antennas: "Антени"
manageAntennas: "Налаштування антен"
name: "Ім'я"
antennaSource: "Джерело антени"
antennaKeywords: "Ключові слова антени"
antennaExcludeKeywords: "Винятки"
serviceworker: "ServiceWorker"
enableServiceworker: "Ввімкнути ServiceWorker"
caseSensitive: "З урахуванням регістру"
notesAndReplies: "Дописи та відповіді"
popularUsers: "Популярні користувачі"
recentlyUpdatedUsers: "Нещодавно активні користувачі"
recentlyRegisteredUsers: "Нещодавно зареєстровані користувачі"
recentlyDiscoveredUsers: "Нещодавно знайдені користувачі"
exploreUsersCount: "{count} користувачів"
exploreFediverse: "Огляд федіверсу"
popularTags: "Популярні теги"
userList: "Списки"
about: "Інформація"
aboutMisskey: "Про Misskey"
administrator: "Адмін"
token: "Токен"
twoStepAuthentication: "Двохфакторна аутентифікація"
moderator: "Модератор"
securityKey: "Ключ захисту"
securityKeyName: "Назва ключа"
registerSecurityKey: "Зареєструвати ключ захисту"
lastUsed: "Востаннє використано"
unregister: "Скасувати реєстрацію"
passwordLessLogin: "Налаштувати вхід без пароля"
resetPassword: "Скинути пароль"
newPasswordIs: "Новий пароль: {password}"
share: "Поділитись"
notFound: "Не знайдено"
cacheClear: "Очистити кеш"
help: "Допомога"
inputMessageHere: "Введіть повідомлення тут"
close: "Закрити"
group: "Група"
groups: "Групи"
createGroup: "Створити групу"
ownedGroups: "Власні групи"
invites: "Запрошення"
groupName: "Назва групи"
transfer: "Передача"
messagingWithUser: "Чат з користувачами"
messagingWithGroup: "Чат з групою"
title: "Тема"
text: "Текст"
next: "Далі"
retype: "Введіть ще раз"
noteOf: "Допис {user}"
inviteToGroup: "Запрошення до групи"
maxNoteTextLength: "Максимальна довжина допису"
quoteAttached: "Цитата"
quoteQuestion: "Ви хочете додати цитату?"
noMessagesYet: "Ще немає повідомлень"
newMessageExists: "Є нові повідомлення"
onlyOneFileCanBeAttached: "До повідомлення можна вкласти лише один файл"
signinRequired: "Будь ласка, авторизуйтесь"
invitations: "Запрошення"
invitationCode: "Код запрошення"
checking: "Перевірка…"
available: "Доступно"
unavailable: "Недоступно"
usernameInvalidFormat: "літери, цифри та _ є прийнятними"
tooShort: "Занадто короткий"
tooLong: "Занадто довгий"
weakPassword: "Слабкий пароль"
strongPassword: "Міцний пароль"
passwordMatched: "Все вірно"
passwordNotMatched: "Паролі не співпадають"
signinWith: "Увійти за допомогою {x}"
uiLanguage: "Мова інтерфейсу"
aboutX: "Про {x}"
useOsNativeEmojis: "Використовувати емодзі ОС"
youHaveNoGroups: "Немає груп"
noHistory: "Історія порожня"
disableAnimatedMfm: "Відключити анімації MFM"
doing: "Виконується"
category: "Категорія"
tags: "Теги"
docSource: "Джерело цього документа"
createAccount: "Створити акаунт"
existingAcount: "Існуючий акаунт"
regenerate: "Оновити"
fontSize: "Розмір шрифту"
noFollowRequests: "Немає запитів на підписку"
dashboard: "Панель приладів"
local: "Локальні"
remote: "Віддалений"
total: "Всього"
appearance: "Вигляд"
clientSettings: "Налаштування клієнта"
accountSettings: "Налаштування акаунта"
promotion: "Просування"
promote: "Просунути"
numberOfDays: "Кількість днів"
hideThisNote: "Сховати цей допис"
showInPage: "Показати на сторінці"
sort: "Сортування"
ascendingOrder: "За зростанням"
descendingOrder: "За спаданням"
script: "Скрипт"
deleteAllFiles: "Видалити всі файли"
deleteAllFilesConfirm: "Ви дійсно хочете видалити всі файли?"
sidebar: "Бокова панель"
rooms: "Кімнати"
relays: "Ретранслятори"
addRelay: "Додати ретранслятор"
smtpHost: "Хост"
smtpUser: "Ім'я користувача"
smtpPass: "Пароль"
regenerateLoginToken: "Оновити Login Token"
_mfm:
cheatSheet: " Довідка MFM"
mention: "Згадка"
quote: "Цитата"
emoji: "Кастомні емоджі"
search: "Пошук"
_reversi:
total: "Всього"
_sidebar:
icon: "Аватар"
_theme:
keys:
mention: "Згадка"
renote: "Поширити"
_sfx:
note: "Дописи"
notification: "Сповіщення"
chat: "Чати"
_antennaSources:
homeTimeline: "Дописи тих, на кого ви підписані"
_widgets:
notifications: "Сповіщення"
timeline: "Стрічка"
activity: "Активність"
federation: "Федіверс"
_cw:
show: "Показати більше"
_visibility:
home: "Домівка"
followers: "Підписники"
localOnly: "Лише локально"
_postForm:
replyPlaceholder: "Відповідь на допис..."
_profile:
name: "Ім'я"
username: "Ім'я користувача"
_exportOrImport:
followingList: "Підписки"
muteList: "Ігнорувати"
blockingList: "Заблокувати"
userLists: "Списки"
_timelines:
home: "Домівка"
_rooms:
_roomType:
default: "За умовчанням"
_furnitures:
monitor: "Монітор"
_pages:
blocks:
image: "Зображення"
script:
categories:
list: "Списки"
blocks:
_join:
arg1: "Списки"
_randomPick:
arg1: "Списки"
_dailyRandomPick:
arg1: "Списки"
_seedRandomPick:
arg2: "Списки"
_pick:
arg1: "Списки"
_listLen:
arg1: "Списки"
types:
array: "Списки"
_notification:
youRenoted: "{name} поширив(ла) ваш допис"
youWereFollowed: "У вас новий підписник"
_types:
follow: "Підписки"
mention: "Згадка"
renote: "Поширити"
quote: "Цитата"
reaction: "Реакції"
_deck:
_columns:
notifications: "Сповіщення"
tl: "Стрічка"
antenna: "Антени"
list: "Списки"
mentions: "Згадки"

View File

@@ -16,6 +16,9 @@ noNotes: "没有帖文"
noNotifications: "无通知"
instance: "实例"
settings: "设置"
basicSettings: "基本设置"
otherSettings: "其他设置"
openInWindow: "在新窗口中打开"
profile: "个人资料"
timeline: "时间线"
noAccountDescription: "这个人很懒,没有写自我介绍"
@@ -40,6 +43,7 @@ deleteAndEditConfirm: "要删除此帖并再次编辑吗?对此帖的所有回
addToList: "添加至列表"
sendMessage: "发送"
copyUsername: "复制用户名"
searchUser: "搜索用户"
reply: "回复"
loadMore: "查看更多"
youGotNewFollower: "你有新的关注者"
@@ -66,8 +70,11 @@ followers: "关注者"
followsYou: "关注了你"
createList: "创建列表"
manageLists: "管理列表"
error: "有点小问题"
error: "错误"
somethingHappened: "出现了问题"
retry: "重试"
pageLoadError: "页面加载失败。"
pageLoadErrorDescription: "这通常是由于网络或浏览器缓存的原因。请清除缓存或等待片刻后重试。"
enterListName: "输入列表名称"
privacy: "隐私"
makeFollowManuallyApprove: "关注者请求需要批准"
@@ -77,7 +84,7 @@ followRequest: "关注申请"
followRequests: "关注申请"
unfollow: "取消关注"
followRequestPending: "发送关注申请"
enterEmoji: "输入Emoji"
enterEmoji: "输入表情符号"
renote: "转发"
unrenote: "取消转发"
quote: "引用"
@@ -106,11 +113,13 @@ unsuspendConfirm: "要解除冻结吗?"
selectList: "选择列表"
selectAntenna: "天线选择"
selectWidget: "选择小工具"
editWidgets: "编辑小工具"
editWidgetsExit: "完成编辑"
customEmojis: "自定义Emoji"
emoji: "表情符号"
emojiName: "Emoji 名称"
emojiUrl: "emoji 地址"
addEmoji: "添加Emoji"
emojiName: "表情符号名称"
emojiUrl: "表情符号地址"
addEmoji: "添加表情符号"
settingGuide: "推荐配置"
cacheRemoteFiles: "远程文件缓存"
cacheRemoteFilesDescription: "当禁用此设定时远程文件将直接从远程实例载入。禁用后会减小储存空间需求,但是会增加流量,因为缩略图不会被生成。"
@@ -155,7 +164,7 @@ jobQueue: "作业队列"
cpuAndMemory: "CPU使用量"
network: "网络"
disk: "存储"
instanceInfo: "实例情报"
instanceInfo: "实例信息"
statistics: "统计"
clearQueue: "清除队列"
clearQueueConfirmTitle: "确定清除队列?"
@@ -176,8 +185,7 @@ done: "完成"
processing: "处理中"
preview: "预览"
default: "默认"
noCustomEmojis: "自定义Emoji"
customEmojisOfRemote: "远程Emoji"
noCustomEmojis: "没有自定义表情符号"
noJobs: "没有任务"
federating: "联合中"
blocked: "已拦截"
@@ -286,7 +294,7 @@ dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "页面"
integration: "连携"
integration: "关联"
connectSerice: "已连接"
disconnectSerice: "断开连接"
enableLocalTimeline: "启用本地时间线功能"
@@ -345,7 +353,7 @@ popularTags: "热门标签"
userList: "列表"
about: "关于"
aboutMisskey: "关于 Misskey"
aboutMisskeyText: "Misskey是由syuilo于2014年开发的开放源代码软件。"
aboutMisskeyText: "Misskey是由syuilo于2014年开发的开软件。"
misskeyMembers: "现在由以下成员进行开发和维护:"
misskeySource: "源代码在这里公开:"
misskeyTranslation: "与我们一同进行Misskey的翻译工作"
@@ -365,8 +373,6 @@ unregister: "删除账户"
passwordLessLogin: "无密码登录"
resetPassword: "重置密码"
newPasswordIs: "新的密码是「{password}」"
autoNoteWatch: "自动关注帖子"
autoNoteWatchDescription: "让您能够收到关于「回应」和回复其他用户的帖子的通知。"
reduceUiAnimation: "减少UI动画"
share: "分享"
notFound: "未找到"
@@ -404,6 +410,7 @@ noMessagesYet: "现在没有新的聊天"
newMessageExists: "新信息"
onlyOneFileCanBeAttached: "只能添加一个附件"
signinRequired: "请先登录"
invitations: "邀请"
invitationCode: "邀请码"
checking: "正在确认"
available: "可用"
@@ -423,7 +430,7 @@ or: "或者"
uiLanguage: "显示语言"
groupInvited: "群组招待"
aboutX: "关于 {x}"
useOsNativeEmojis: "使用OS原生Emoji"
useOsNativeEmojis: "使用OS原生表情符号"
youHaveNoGroups: "没有群组"
joinOrCreateGroup: "请加入一个现有的群组,或者创建新群组。"
noHistory: "没有历史记录"
@@ -445,7 +452,7 @@ total: "总计"
weekOverWeekChanges: "与前一周相比"
dayOverDayChanges: "与前一日相比"
appearance: "外观"
clinetSettings: "客户端设置"
clientSettings: "客户端设置"
accountSettings: "账户设置"
promotion: "推广"
promote: "推广"
@@ -476,6 +483,8 @@ newNoteRecived: "有新的帖子"
sounds: "声音"
listen: "听"
none: "空"
showInPage: "在页面中显示"
popout: "弹窗"
volume: "音量"
details: "详情"
chooseEmoji: "选择表情符号"
@@ -518,7 +527,6 @@ enableInfiniteScroll: "启用自动滚动页面模式"
visibility: "可见性"
poll: "调查问卷"
useCw: "隐藏内容"
fixedWidgetsPosition: "固定小工具的位置"
enablePlayer: "打开播放器"
disablePlayer: "关闭播放器"
expandTweet: "展开推文"
@@ -532,6 +540,7 @@ pluginInstallWarn: "请不要安装不明来源的插件"
deck: "Deck"
undeck: "取消Deck"
useBlurEffectForModal: "模态框使用模糊效果"
useFullReactionPicker: "使用全功能的回应工具栏"
generateAccessToken: "生成访问令牌"
permission: "权限"
enableAll: "启用全部"
@@ -540,7 +549,7 @@ tokenRequested: "允许访问账户"
pluginTokenRequestedDescription: "此插件将能够拥有此处设置的权限"
notificationType: "通知类型"
edit: "编辑"
useStarForReactionFallback: "如果回应的颜文字未知,则使用★作为代替"
useStarForReactionFallback: "如果回应的是未知表情符号,则使用★作为代替"
emailConfig: "邮件服务器设置"
enableEmail: "启用发送邮件功能"
emailConfigInfo: "用于确认电子邮件和密码重置"
@@ -566,6 +575,119 @@ delayed: "延迟"
database: "数据库"
channel: "频道"
create: "创建"
notificationSetting: "通知设置"
notificationSettingDesc: "选择要显示的通知类型。"
useGlobalSetting: "使用全局设置"
useGlobalSettingDesc: "启用时,将使用帐户通知设置。关闭时,则可以单独设置。"
other: "其他"
regenerateLoginToken: "重新生成登录令牌"
regenerateLoginTokenDescription: "重新生成用于登录的内部令牌。通常您不需要这样做。重新生成后,您将在所有设备上登出。"
setMultipleBySeparatingWithSpace: "您可以使用空格分隔多个项目。"
fileIdOrUrl: "文件ID或者URL"
chatOpenBehavior: "聊天窗口打开时的行为"
sample: "示例"
abuseReports: "举报"
reportAbuse: "举报"
reportAbuseOf: "举报{name}"
fillAbuseReportDescription: "请填写举报的详细原因。如果有对方发的帖子请同时填写URL地址。"
abuseReported: "内容已发送。感谢您的报告。"
send: "发送"
abuseMarkAsResolved: "处理完毕"
openInNewTab: "在新标签页中打开"
openInSideView: "在侧边栏中打开"
defaultNavigationBehaviour: "默认导航"
editTheseSettingsMayBreakAccount: "编辑这些设置可以会损坏您的账号"
instanceTicker: "帖子的实例信息"
waitingFor: "等待{x}"
random: "随机"
system: "系统"
switchUi: "切换界面"
desktop: "桌面"
_mfm:
cheatSheet: "MFM代码速查表"
intro: "MFM是一种在Misskey中的各个位置使用的专用标记语言。在这里您可以看到MFM中可用的语法列表。"
dummy: "通过Misskey扩展Fediverse的世界"
mention: "提及"
mentionDescription: "可以使用 @+用户名 来指示特定用户"
hashtag: "话题标签"
hashtagDescription: "可以使用井号+文字来表示话题标签。"
url: "URL"
urlDescription: "可以表示URL地址。"
link: "链接"
linkDescription: "可以将部分文字和URL关联起来。"
bold: "粗体"
boldDescription: "可以将文字显示为粗体来表示强调。"
small: "缩小"
smallDescription: "可以使内容文字变小、变淡。"
center: "居中"
centerDescription: "可以将内容居中显示。"
inlineCode: "代码(内嵌)"
inlineCodeDescription: "将文字中的程序代码语法高亮显示。"
blockCode: "代码(块)"
blockCodeDescription: "语法高亮显示整块程序代码。"
inlineMath: "数学公式(内嵌)"
inlineMathDescription: "显示内嵌的KaTex公式。"
blockMath: "数学公式(块)"
blockMathDescription: "显示整块的多行KaTex数学公式。"
quote: "引用"
quoteDescription: "可以用来表示引用的内容。"
emoji: "自定义表情符号"
emojiDescription: "可以将自定义表情符号使用冒号括起来,就可以显示自定义表情符号了。"
search: "搜索"
searchDescription: "显示含有搜索内容示例的搜索框。"
flip: "翻转"
flipDescription: "将内容上下或左右翻转。"
jelly: "动画(果冻)"
jellyDescription: "显示果冻一样的动画效果。"
tada: "动画(锵锵)"
tadaDescription: "显示\"锵锵!\"的动画效果。"
jump: "动画(跳动)"
jumpDescription: "显示跳动的动画效果。"
bounce: "动画(弹性)"
bounceDescription: "显示弹性一样的动画效果。"
shake: "动画(摇晃)"
shakeDescription: "显示摇晃的动画效果。"
twitch: "动画(颤抖)"
twitchDescription: "显示强烈颤抖的动画效果。"
spin: "动画(回转)"
spinDescription: "显示回转的动画效果。"
_reversi:
reversi: "黑白棋"
gameSettings: "对局设置"
chooseBoard: "棋盘选择"
blackOrWhite: "先手/后手"
blackIs: "{name}执黑(先走)"
rules: "规则"
botSettings: "机器人设置"
thisGameIsStartedSoon: "对局在几秒后开始"
waitingForOther: "等待对手准备"
waitingForMe: "等待您的准备"
waitingBoth: "请准备"
ready: "准备就绪"
cancelReady: "重新准备"
opponentTurn: "对手的会合"
myTurn: "您的回合"
turnOf: "{name}的回合"
pastTurnOf: "{name}的回合"
surrender: "认输 "
surrendered: "对手认输"
drawn: "平局"
won: "{name}获胜"
black: "黑"
white: "白"
total: "总计"
turnCount: "{count}回合"
myGames: "我的对局"
allGames: "所有对局"
ended: "结束"
playing: "对局中"
isLlotheo: "棋子较少一方获胜(LLoTheO规则)"
loopedMap: "循环棋盘"
canPutEverywhere: "可以下在任意位置"
_instanceTicker:
none: "不显示"
remote: "显示给远程用户"
always: "始终显示"
_serverDisconnectedBehavior:
reload: "自动重载"
dialog: "对话框警告"
@@ -782,6 +904,7 @@ _widgets:
photos: "照片"
digitalClock: "数字时钟"
federation: "联邦宇宙"
postForm: "投稿窗口"
_cw:
hide: "隐藏"
show: "查看更多"
@@ -1244,8 +1367,11 @@ _notification:
renote: "转发"
quote: "引用"
reaction: "回应"
pollVote: "投票"
receiveFollowRequest: "关注请求"
pollVote: "问卷调查已投票"
receiveFollowRequest: "收到关注请求"
followRequestAccepted: "关注请求已接受"
groupInvited: "加入群组邀请"
app: "关联应用的通知"
_deck:
alwaysShowMainColumn: "总是显示主列"
columnAlign: "列对齐"

View File

@@ -1,21 +1,24 @@
---
_lang_: "中文(繁體)"
_lang_: "繁體中文"
introMisskey: "歡迎! Misskey是一個開源的去中心化的社群網站。\n通過「貼文」來分享現在發生的事情吧 📡\n「反應」功能可以讓你快速的對大家的「帖子」來表達感情👍\n一起來探索新的世界吧 🚀"
monthAndDay: "{month}月 {day}日"
search: "搜尋"
notifications: "通知"
username: "使用名稱"
username: "使用名稱"
password: "密碼"
fetchingAsApObject: "從 Fediverse 查詢中..."
ok: "確定"
ok: "OK"
gotIt: "知道了"
cancel: "取消"
enterUsername: "輸入使用者名稱"
renotedBy: "{user}轉發"
renotedBy: "{user} 轉發"
noNotes: "貼文不可用。"
noNotifications: "沒有通知"
instance: "實例"
settings: "設定"
basicSettings: "基本設定"
otherSettings: "其他設定"
openInWindow: "在新視窗開啟"
profile: "個人檔案"
timeline: "時間軸"
noAccountDescription: "此用戶還沒有自我介紹"
@@ -40,6 +43,7 @@ deleteAndEditConfirm: "要刪除並再次編輯嗎?此貼文的所有反應,
addToList: "添加至清單"
sendMessage: "發送訊息"
copyUsername: "複製用戶名"
searchUser: "搜尋用戶"
reply: "回覆"
loadMore: "瀏覽更多"
youGotNewFollower: "您有新的追隨者"
@@ -66,8 +70,10 @@ followers: "追隨者"
followsYou: "追隨你的人"
createList: "建立清單"
manageLists: "管理清單"
error: "發生錯誤"
error: "錯誤"
somethingHappened: "發生錯誤"
retry: "重試"
pageLoadError: "載入頁面失敗"
enterListName: "輸入清單名稱"
privacy: "隱私"
makeFollowManuallyApprove: "手動審核追隨請求"
@@ -93,8 +99,8 @@ attachCancel: "移除附件"
markAsSensitive: "標記為敏感內容"
unmarkAsSensitive: "取消標記為敏感內容"
enterFileName: "請輸入檔案名稱"
mute: "音"
unmute: "解除音"
mute: "音"
unmute: "解除音"
block: "封鎖"
unblock: "解除封鎖"
suspend: "凍結"
@@ -106,16 +112,18 @@ unsuspendConfirm: "確定解凍此帳號?"
selectList: "選擇清單"
selectAntenna: "選擇天線"
selectWidget: "選擇小工具"
editWidgets: "編輯小工具"
editWidgetsExit: "停止編輯"
customEmojis: "自訂表情符號"
emoji: "表情符號"
emojiName: "表情符號名稱"
emojiUrl: "表情符號URL"
addEmoji: "新增表情符號"
settingGuide: "推薦設定"
cacheRemoteFiles: "遠程文件緩存"
cacheRemoteFilesDescription: "如果禁用此設定,遠程文件將會被直接連結而非緩存。禁用將節省服務器上的存儲空間,但會因為沒有生成預覽圖而增加流量。"
flagAsBot: "此帳戶是Bot"
flagAsCat: "此帳戶是Cat"
cacheRemoteFiles: "緩存非遠程檔案"
cacheRemoteFilesDescription: "禁用此設定會停止遠端檔案的緩存,從而節省儲存空間。但資料會因直接連線從而產生額外連接數據。"
flagAsBot: "此使用者是機器人"
flagAsCat: "此使用者是貓"
autoAcceptFollowed: "自動許可追隨"
addAcount: "新增帳號"
loginFailed: "登入失敗"
@@ -163,8 +171,8 @@ clearCachedFiles: "清除快取資料"
clearCachedFilesConfirm: "確定要清除緩存資料嗎?"
blockedInstances: "已封鎖的實例"
blockedInstancesDescription: "請逐行輸入需要封鎖的實例。已封鎖的實例將無法與本實例進行通訊。"
muteAndBlock: "禁言 / 封鎖"
mutedUsers: "已禁言用戶"
muteAndBlock: "靜音/封鎖"
mutedUsers: "已靜音用戶"
blockedUsers: "已封鎖用戶"
noUsers: "無用戶"
editProfile: "編輯個人檔案"
@@ -176,7 +184,6 @@ processing: "處理中"
preview: "預覽"
default: "預設"
noCustomEmojis: "沒有表情符號"
customEmojisOfRemote: "來自其他實例的表情符號"
noJobs: "沒有任務"
federating: "整合搜索中"
blocked: "已封鎖"
@@ -220,10 +227,11 @@ messageRead: "已讀"
noMoreHistory: "沒有更多歷史紀錄"
startMessaging: "開始傳送訊息"
nUsersRead: "{n}人已讀"
agreeTo: "我同意{0}"
tos: "使用條款"
start: "開始"
home: "首頁"
remoteUserCaution: "由於是遠程用戶,信息不完整。"
remoteUserCaution: "由於該用戶來自遠端實例,因此資料用戶並未即時更新。"
activity: "動態"
images: "圖片"
birthday: "生日"
@@ -293,7 +301,7 @@ disablingTimelinesInfo: "即使您禁用了時間線功能,管理員和協調
registration: "註冊"
enableRegistration: "開啟新用戶註冊"
invite: "邀請"
proxyRemoteFiles: "代理遠程檔案"
proxyRemoteFiles: "遠端代理檔案"
proxyRemoteFilesDescription: "啟用此設置後,由於超出存儲容量而未保存或刪除的遠程文件將被本地代理,並且將生成預覽圖。這不影響服務器的存儲。"
driveCapacityPerLocalAccount: "每個本地用戶的雲端容量"
driveCapacityPerRemoteAccount: "每個非本地用戶的雲端容量"
@@ -316,7 +324,7 @@ antennas: "天線"
manageAntennas: "管理天線"
name: "名稱"
antennaSource: "接收來源"
antennaKeywords: "包含關鍵字"
antennaKeywords: "包含關鍵字"
antennaExcludeKeywords: "排除關鍵字"
antennaKeywordsDescription: "用空格分隔指定AND、用換行符分隔指定OR"
notifyAntenna: "通知我有新的貼文"
@@ -362,8 +370,6 @@ unregister: "刪除賬戶"
passwordLessLogin: "設置無密碼登入"
resetPassword: "重置密碼"
newPasswordIs: "新密碼為「{password}」"
autoNoteWatch: "自動追隨貼文"
autoNoteWatchDescription: "收到反應或回覆過的貼文的通知"
reduceUiAnimation: "減少介面的動態視覺"
share: "分享"
notFound: "找不到"
@@ -401,6 +407,7 @@ noMessagesYet: "沒有訊息"
newMessageExists: "有新的訊息"
onlyOneFileCanBeAttached: "只能添加一個附件"
signinRequired: "請先登入"
invitations: "邀請"
invitationCode: "邀請碼"
checking: "確認中"
available: "可用的"
@@ -430,12 +437,36 @@ category: "類別"
tags: "標籤"
docSource: "文件來源"
createAccount: "建立帳戶"
existingAcount: "現有帳戶"
regenerate: "再生"
fontSize: "字體大小"
openImageInNewTab: "於新分頁中開啟圖片"
local: "本地"
remote: "遠端"
total: "合計"
clinetSettings: "用戶端設定"
weekOverWeekChanges: "與上週相比"
dayOverDayChanges: "與前一日相比"
appearance: "外觀"
clientSettings: "用戶端設定"
accountSettings: "帳號設定"
promotion: "推廣貼文"
promote: "推廣"
numberOfDays: "有效天數"
hideThisNote: "隱藏此貼文"
showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦"
objectStorageBaseUrl: "Base URL"
objectStorageBucket: "儲存空間Bucket"
objectStoragePrefix: "前綴"
objectStorageEndpoint: "訪問網域名稱Endpoint"
objectStorageEndpointDesc: "如要使用AWS S3請留空。否則請根據伺服器要求以'<host>'或 '<host>:<port>'的形式設定訪問網域名稱Endpoint。"
objectStorageRegion: "地域Region"
objectStorageUseSSL: "使用SSL"
objectStorageUseProxy: "使用網路代理"
serverLogs: "伺服器日誌"
deleteAll: "刪除所有記錄"
sounds: "音效"
none: "無"
showInPage: "在頁面中顯示"
volume: "音量"
details: "詳細資訊"
chooseEmoji: "選擇您的表情符號\n"
@@ -443,14 +474,21 @@ unableToProcess: "操作無法完成"
recentUsed: "最近使用"
install: "安裝"
uninstall: "解除安裝"
installedApps: "已授權的應用程式"
nothing: "未發現"
installedDate: "安裝時間"
lastUsedDate: "最後上線日期"
state: "狀態"
sort: "排序"
ascendingOrder: "昇冪"
descendingOrder: "降冪"
scratchpad: "暫存記憶體"
output: "輸出"
script: "腳本"
updateRemoteUser: "更新非本地用戶資料"
deleteAllFiles: "刪除所有檔案"
deleteAllFilesConfirm: "要删除所有檔案吗?"
removeAllFollowing: "解除所有追隨"
userSuspended: "該用戶已被凍結"
userSilenced: "該用戶已被禁言。"
sidebar: "側邊列"
@@ -468,7 +506,6 @@ enableInfiniteScroll: "啟用自動滾動頁面模式"
visibility: "公開範圍"
poll: "投票"
useCw: "隱藏內容"
fixedWidgetsPosition: "固定小工具的位置"
enablePlayer: "打開播放器"
disablePlayer: "關閉播放器"
expandTweet: "展開推文"
@@ -488,17 +525,63 @@ tokenRequested: "允許訪問帳號"
notificationType: "通知形式"
edit: "編輯"
useStarForReactionFallback: "以★代替未知的表情符號"
emailConfig: "電郵服務器設定"
emailConfig: "電子郵件伺服器設定"
enableEmail: "啟用發送電郵功能"
emailConfigInfo: "用於確認電郵地址及密碼重置"
email: "電郵地址"
smtpConfig: "SMTP服器設定"
smtpConfig: "SMTP服器設定"
smtpHost: "主機"
smtpPort: "端口"
smtpUser: "使用名稱"
smtpUser: "使用名稱"
smtpPass: "密碼"
emptyToDisableSmtpAuth: "留空使用者名稱和密碼以禁用SMTP驗證。"
testEmail: "郵件測試發送"
wordMute: "靜音文字"
display: "檢視"
copy: "複製"
metrics: "指標"
overview: "概覽"
logs: "日誌"
delayed: "延遲"
database: "資料庫"
channel: "頻道"
create: "新增"
notificationSetting: "通知設定"
other: "其他"
regenerateLoginTokenDescription: "再生用於登入的內部權杖。一般情況下是不需要這樣做的。一旦再生,所有裝置將會被登出。"
sample: "範例 "
abuseReports: "檢舉"
reportAbuse: "檢舉"
reportAbuseOf: "檢舉{name}"
send: "發送"
openInNewTab: "在新分頁中開啟"
random: "隨機"
system: "系統"
_mfm:
mention: "提及"
hashtag: "#tag"
link: "鏈接"
quote: "引用"
emoji: "自訂表情符號"
search: "搜尋"
_reversi:
reversi: "黑白棋"
gameSettings: "對弈設定"
chooseBoard: "選擇棋盤"
rules: "規則"
botSettings: "機器人設定"
opponentTurn: "對手回合"
myTurn: "你的回合"
turnOf: "{name}的回合"
pastTurnOf: "{name}的回合"
surrender: "認輸"
black: "黑"
white: "白"
total: "合計"
ended: "已結束"
playing: "正在對弈"
_instanceTicker:
always: "總是顯示"
_serverDisconnectedBehavior:
reload: "自動重載"
dialog: "以對話框警告"
@@ -506,7 +589,7 @@ _serverDisconnectedBehavior:
_channel:
create: "建立頻道"
edit: "編輯頻道"
setBanner: "設置封面圖"
setBanner: "設定橫幅"
removeBanner: "移除封面圖"
featured: "流行"
owned: "管理中"
@@ -515,12 +598,33 @@ _channel:
notesCount: "有{n}個帖子"
_sidebar:
icon: "頭像"
hide: "隱藏"
_wordMute:
muteWords: "加入靜音文字"
mutedNotes: "已靜音的貼文"
_theme:
constant: "常數"
defaultValue: "預設值"
color: "顏色"
func: "函数"
argument: "引數"
alpha: "透明度"
darken: "暗度"
lighten: "亮度"
keys:
bg: "背景"
fg: "文本"
shadow: "陰影"
link: "鏈接"
hashtag: "#tag"
mention: "提及"
mentionMe: "提及我"
renote: "轉發貼文"
divider: "分割線"
infoBg: "資訊背景"
infoFg: "資訊內容"
infoWarnBg: "警告背景"
infoWarnFg: "警告字元"
_sfx:
note: "貼文"
noteMy: "我的貼文"
@@ -557,6 +661,7 @@ _tutorial:
step4_1: "筆記發出去了嗎?"
step4_2: "如果你的貼文有顯示在時間軸上,就代表已經發文成功。"
step5_1: "現在試試看追隨其他人來讓你的時間軸變得更生動吧。"
step5_2: "你可以在{featured}上看到受歡迎的貼文,你也可以選擇從列表中追隨你喜歡的人,或者在{explore}上找到熱門使用者。"
step5_3: "想要追隨其他人,只要點擊他們的頭像並按「追隨」即可。"
step5_4: "如果使用者的名字旁有鎖頭的圖示,代表他們需要手動核准你的追隨請求。"
step6_1: "現在你可以在時間軸上看到其他用戶的貼文"
@@ -564,6 +669,8 @@ _tutorial:
step6_3: "在他人的貼文按下「+」的圖示即可選擇想要的表情符號來進行「反應」。"
step7_1: "以上為Misskey的基本操作說明教學在此告一段落。辛苦了。"
step7_2: "歡迎到{help}來瞭解更多Misskey相關介紹。"
_2fa:
registerDevice: "註冊裝置"
_permissions:
"read:blocks": "已封鎖用戶名單"
"write:blocks": "編輯已封鎖用戶名單"
@@ -572,13 +679,32 @@ _permissions:
"read:favorites": "瀏覽已收藏"
"write:favorites": "編輯收藏清單"
"write:following": "追隨/解除追隨"
"read:messaging": "顯示訊息"
"write:messaging": "撰寫或刪除私人訊息"
"read:mutes": "顯示已靜音列表"
"write:mutes": "編輯已靜音列表"
"write:notes": "撰寫或刪除貼文"
"read:notifications": "查看通知"
"write:notifications": "編輯通知"
"read:reactions": "查看反應"
"write:reactions": "編輯反應"
"write:votes": "投票"
"read:pages": "顯示頁面"
"write:pages": "編輯頁面"
"read:page-likes": "顯示頁面的已喜歡"
"write:page-likes": "編輯頁面上喜歡"
"read:user-groups": "顯示使用者群組"
"write:user-groups": "編輯使用者群組"
"read:channels": "已查看的頻道"
"write:channels": "操作頻道"
"write:channels": "編輯頻道"
_auth:
shareAccess: "要授權「“{name}”」存取您的帳戶嗎?"
_antennaSources:
all: "全部貼文"
homeTimeline: "來自已追隨使用者的貼文"
users: "來自特定使用者的貼文"
userList: "來自特定清單中的貼文"
userGroup: "來自特定群組的貼文"
_weekday:
sunday: "週日"
monday: "週一"
@@ -588,63 +714,236 @@ _weekday:
friday: "週五"
saturday: "週六"
_widgets:
memo: "備忘錄"
notifications: "通知"
timeline: "時間軸"
calendar: "行事曆"
trends: "發燒貼文"
clock: "時鐘"
rss: "RSS閱讀器"
activity: "動態"
photos: "照片"
digitalClock: "電子時鐘"
federation: "聯邦宇宙"
_cw:
hide: "隱藏"
show: "瀏覽更多"
chars: "{count}字元"
files: "{count} 個檔案"
_poll:
noOnlyOneChoice: "至少需要兩個選項。"
expiration: "期限"
infinite: "無期限"
at: "結束時間"
deadlineDate: "截止日期"
deadlineTime: "小時"
duration: "時長"
votesCount: "{n}票"
totalVotes: "一共{n}票"
vote: "投票"
showResult: "顯示結果"
voted: "已投票"
closed: "已結束"
remainingDays: "{d}天{h}小時後結束"
_visibility:
public: "公開"
home: "首頁"
followers: "追隨者"
specified: "指定使用者"
specifiedDescription: "僅發送至指定使用者"
localOnly: "僅限本地"
localOnlyDescription: "對遠端使用者隱藏"
_postForm:
replyPlaceholder: "回覆此貼文..."
quotePlaceholder: "引用此貼文..."
channelPlaceholder: "發佈到頻道"
_placeholders:
a: "今天過得如何?"
b: "有什麼新鮮事嗎?"
c: "有什麼新鮮想法嗎?"
d: "想要發布些什麼嗎?"
e: "寫些什麼吧..."
f: "期待你發佈的內容..."
_profile:
name: "名稱"
username: "使用名稱"
username: "使用名稱"
description: "關於我"
youCanIncludeHashtags: "你也可以在「關於我」中加上 #tag"
metadata: "更多資訊"
metadataLabel: "標籤"
metadataContent: "内容"
_exportOrImport:
allNotes: "全部貼文"
followingList: "追隨中"
muteList: "音"
muteList: "音"
blockingList: "封鎖"
userLists: "清單"
_charts:
usersIncDec: "使用者増減"
usersTotal: "使用者合共"
activeUsers: "活躍使用者"
notesIncDec: "貼文増減"
localNotesIncDec: "本地貼文増減"
remoteNotesIncDec: "非本地貼文的數目增减"
notesTotal: "貼文合共"
filesIncDec: "檔案増減"
filesTotal: "累計檔案"
storageUsageIncDec: "儲存空間的増減"
storageUsageTotal: "已使用的儲存空間合共"
_instanceCharts:
requests: "請求"
users: "使用者増減"
usersTotal: "總計使用者"
notes: "貼文増減"
notesTotal: "累計貼文"
ff: "追隨/追隨者的増減"
ffTotal: "追隨/追隨者累計"
cacheSize: "增加或減少快取用量"
cacheSizeTotal: "快取大小總計"
files: "檔案數量的増減"
filesTotal: "檔案數量總計"
_timelines:
home: "首頁"
local: "本地"
social: "社群"
global: "全域"
_rooms:
roomOf: "{user}的房間"
addFurniture: "擺放家具"
translate: "移動 "
rotate: "旋轉"
exit: "返回"
remove: "移除"
clear: "全部移除"
clearConfirm: "確定要移除全部家具嗎?"
leaveConfirm: "修改未儲存,是否要離開?"
chooseImage: "選擇圖像"
roomType: "房間種類"
carpetColor: "地板顏色"
_roomType:
default: "預設"
washitsu: "和室"
_furnitures:
milk: "牛奶盒"
bed: "床"
low-table: "咖啡桌"
desk: "書桌"
chair: "椅子"
chair2: "椅子2"
fan: "通風機"
pc: "電腦"
plant: "觀葉植物"
plant2: "觀葉植物2"
eraser: "橡皮擦"
pencil: "鉛筆"
pudding: "布丁"
cardboard-box: "紙板箱"
cardboard-box2: "紙板箱2"
cardboard-box3: "紙板箱3"
book: "讀物"
book2: "讀物2"
piano: "鋼琴"
moon: "月亮"
corkboard: "木栓板"
mousepad: "滑鼠墊"
monitor: "監視器"
keyboard: "鍵盤"
carpet-stripe: "條紋地毯"
bin: "垃圾箱"
cup-noodle: "杯面"
holo-display: "投影機"
energy-drink: "能量飲料"
doll-ai: "小藍的人偶公仔"
banknote: "大疊鈔票"
_pages:
newPage: "建立頁面"
editPage: "編輯頁面"
created: "頁面已建立"
updated: "頁面已更新"
deleted: "頁面已被刪除"
editThisPage: "編輯此頁面"
viewSource: "檢視原始碼"
viewPage: "顯示頁面"
like: "喜歡"
unlike: "收回喜歡"
my: "我的頁面"
liked: "已喜歡的頁面"
inspector: "面板檢查"
variables: "變數"
title: "標題"
url: "頁面網址"
font: "字型"
fontSerif: "襯線體"
fontSansSerif: "無襯線體"
inputBlocks: "輸入"
blocks:
text: "文本"
textarea: "文字區域"
section: "區段"
image: "圖片"
button: "按鈕"
if: "如果"
_if:
variable: "變數"
_post:
text: "内容"
canvasId: "畫布ID"
textInput: "插入文字"
_textInput:
name: "變數名稱"
text: "標題"
default: "預設值"
textareaInput: "多行文字输入"
_textareaInput:
name: "變數名稱"
text: "標題"
default: "預設值"
numberInput: "輸入數值"
_numberInput:
name: "變數名稱"
_canvas:
width: "寬度"
_counter:
text: "標題"
default: "預設值"
canvas: "畫布"
_canvas:
id: "畫布ID"
width: "寬度"
height: "高度"
switch: "開關"
_switch:
name: "變數名稱"
text: "標題"
default: "預設值"
counter: "計數器"
_counter:
name: "變數名稱"
text: "標題"
inc: "増加値"
_button:
text: "標題"
colored: "彩色"
action: "按下按鈕後發生的行為"
_action:
_dialog:
content: "内容"
resetRandom: "重設亂數"
pushEvent: "發送事件"
_pushEvent:
event: "事件名稱"
no-variable: "沒有"
callAiScript: "調用AiScript"
_callAiScript:
functionName: "函數名稱"
radioButton: "選項"
_radioButton:
name: "變數名稱"
title: "標題"
default: "預設值"
script:
categories:
logical: "邏輯運算"
operation: "計算"
comparison: "對比"
random: "隨機"
value: "數值 "
fn: "函数"
text: "文本操作"
@@ -654,6 +953,8 @@ _pages:
text: "文本"
multiLineText: "文本 (多行)"
textList: "文本列表"
_strLen:
arg1: "文本"
_strPick:
arg1: "文本"
arg2: "字元位置"
@@ -667,18 +968,22 @@ _pages:
_add:
arg1: "A"
arg2: "B"
subtract: "减去"
_subtract:
arg1: "A"
arg2: "B"
multiply: "乘"
_multiply:
arg1: "A"
arg2: "B"
divide: "除"
_divide:
arg1: "A"
arg2: "B"
_mod:
arg1: "A"
arg2: "B"
round: "四舍五入"
_round:
arg1: "數值"
eq: "A和B相等"
@@ -720,6 +1025,7 @@ _pages:
not: "否"
_not:
arg1: "否"
random: "隨機"
_random:
arg1: "機率"
rannum: "亂數"
@@ -762,6 +1068,8 @@ _pages:
arg1: "文字"
_numberToString:
arg1: "數值"
_splitStrByLine:
arg1: "文本"
ref: "變數"
aiScriptVar: "AiScript的變數"
fn: "函数"
@@ -777,25 +1085,43 @@ _pages:
array: "清單"
stringArray: "文本列表"
enviromentVariables: "環境變數"
pageVariables: "頁面元素"
_relayStatus:
requesting: "等待核准"
accepted: "已通過核准"
rejected: "已拒絕"
_notification:
youRenoted: "{name} 轉發了你的貼文"
youGotPoll: "{name}已投票"
youWereFollowed: "您有新的追隨者"
yourFollowRequestAccepted: "您的追隨請求已通過"
youWereInvitedToGroup: "您有新的群組邀請"
_types:
all: "全部 "
follow: "追隨中"
mention: "提及"
reply: "回覆"
renote: "轉發貼文"
quote: "引用"
reaction: "反應"
receiveFollowRequest: "已收到追隨請求"
followRequestAccepted: "追隨請求已接受"
app: "應用程式通知"
_deck:
alwaysShowMainColumn: "總是顯示主欄"
columnAlign: "對齊欄位"
addColumn: "新增欄位"
swapLeft: "向左移動"
swapRight: "向右移動"
swapUp: "往上移動"
swapDown: "往下移動"
stackLeft: "向左折疊"
popRight: "向右彈出"
_columns:
widgets: "小工具"
notifications: "通知"
tl: "時間軸"
antenna: "天線"
list: "清單"
mentions: "提及"
direct: "指定使用者"

View File

@@ -0,0 +1,32 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class refineAbuseUserReport1603094348345 implements MigrationInterface {
name = 'refineAbuseUserReport1603094348345'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_d049123c413e68ca52abe734203"`);
await queryRunner.query(`DROP INDEX "IDX_d049123c413e68ca52abe73420"`);
await queryRunner.query(`DROP INDEX "IDX_5cd442c3b2e74fdd99dae20243"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" RENAME COLUMN "userId" TO "targetUserId"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "assigneeId" character varying(32)`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "resolved" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "comment"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "comment" character varying(2048) NOT NULL DEFAULT '{}'::varchar[]`);
await queryRunner.query(`CREATE INDEX "IDX_2b15aaf4a0dc5be3499af7ab6a" ON "abuse_user_report" ("resolved") `);
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_08b883dd5fdd6f9c4c1572b36de" FOREIGN KEY ("assigneeId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_08b883dd5fdd6f9c4c1572b36de"`);
await queryRunner.query(`DROP INDEX "IDX_2b15aaf4a0dc5be3499af7ab6a"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "comment"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "comment" character varying(512) NOT NULL DEFAULT '{}'::varchar[]`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "resolved"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "assigneeId"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" RENAME COLUMN "targetUserId" TO "userId"`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5cd442c3b2e74fdd99dae20243" ON "abuse_user_report" ("userId", "reporterId") `);
await queryRunner.query(`CREATE INDEX "IDX_d049123c413e68ca52abe73420" ON "abuse_user_report" ("userId") `);
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_d049123c413e68ca52abe734203" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
}

View File

@@ -0,0 +1,20 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class refineAbuseUserReport21603095701770 implements MigrationInterface {
name = 'refineAbuseUserReport21603095701770'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "targetUserHost" character varying(128)`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "reporterHost" character varying(128)`);
await queryRunner.query(`CREATE INDEX "IDX_4ebbf7f93cdc10e8d1ef2fc6cd" ON "abuse_user_report" ("targetUserHost") `);
await queryRunner.query(`CREATE INDEX "IDX_f8d8b93740ad12c4ce8213a199" ON "abuse_user_report" ("reporterHost") `);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX "IDX_f8d8b93740ad12c4ce8213a199"`);
await queryRunner.query(`DROP INDEX "IDX_4ebbf7f93cdc10e8d1ef2fc6cd"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "reporterHost"`);
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "targetUserHost"`);
}
}

View File

@@ -0,0 +1,14 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class instanceThemeColor1603776877564 implements MigrationInterface {
name = 'instanceThemeColor1603776877564'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "instance" ADD "themeColor" character varying(64) DEFAULT null`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "themeColor"`);
}
}

View File

@@ -0,0 +1,14 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class instanceFavicon1603781553011 implements MigrationInterface {
name = 'instanceFavicon1603781553011'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "instance" ADD "faviconUrl" character varying(256) DEFAULT null`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "faviconUrl"`);
}
}

View File

@@ -0,0 +1,14 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class deleteAutoWatch1604821689616 implements MigrationInterface {
name = 'deleteAutoWatch1604821689616'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "autoWatch"`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "user_profile" ADD "autoWatch" boolean NOT NULL DEFAULT false`);
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.48.1",
"version": "12.56.0",
"codename": "indigo",
"repository": {
"type": "git",
@@ -46,7 +46,7 @@
"@koa/multer": "3.0.0",
"@koa/router": "9.0.1",
"@sinonjs/fake-timers": "6.0.1",
"@syuilo/aiscript": "0.11.0",
"@syuilo/aiscript": "0.11.1",
"@types/bcryptjs": "2.4.2",
"@types/bull": "3.14.0",
"@types/cbor": "5.0.1",
@@ -92,57 +92,57 @@
"@types/request-stats": "3.0.0",
"@types/rimraf": "3.0.0",
"@types/seedrandom": "2.4.28",
"@types/sharp": "0.25.0",
"@types/sharp": "0.26.0",
"@types/sinonjs__fake-timers": "6.0.1",
"@types/speakeasy": "2.0.5",
"@types/tinycolor2": "1.4.2",
"@types/tmp": "0.2.0",
"@types/uuid": "8.3.0",
"@types/web-push": "3.3.0",
"@types/webpack": "4.41.22",
"@types/webpack": "4.41.24",
"@types/webpack-stream": "3.2.11",
"@types/websocket": "1.0.1",
"@types/ws": "7.2.7",
"@typescript-eslint/parser": "4.4.0",
"@vue/compiler-sfc": "3.0.0",
"@typescript-eslint/parser": "4.6.1",
"@vue/compiler-sfc": "3.0.2",
"abort-controller": "3.0.0",
"apexcharts": "3.22.0",
"apexcharts": "3.22.1",
"autobind-decorator": "2.4.0",
"autosize": "4.0.2",
"autwh": "0.1.0",
"aws-sdk": "2.770.0",
"aws-sdk": "2.787.0",
"bcryptjs": "2.4.3",
"blurhash": "1.1.3",
"bull": "3.18.0",
"bull": "3.18.1",
"cafy": "15.2.1",
"cbor": "5.1.0",
"chalk": "4.1.0",
"chart.js": "2.9.3",
"chart.js": "2.9.4",
"cli-highlight": "2.1.4",
"commander": "4.1.1",
"content-disposition": "0.5.3",
"core-js": "3.6.5",
"core-js": "3.7.0",
"crc-32": "1.2.0",
"css-loader": "4.3.0",
"css-loader": "5.0.1",
"cssnano": "4.1.10",
"dateformat": "3.0.3",
"deep-entries": "3.1.0",
"diskusage": "1.1.3",
"double-ended-queue": "2.1.0-0",
"escape-regexp": "0.0.1",
"eslint": "7.10.0",
"eslint-plugin-vue": "7.0.1",
"eslint": "7.12.1",
"eslint-plugin-vue": "7.1.0",
"eventemitter3": "4.0.7",
"feed": "4.2.1",
"fibers": "5.0.0",
"file-type": "15.0.1",
"file-type": "16.0.1",
"fluent-ffmpeg": "2.1.2",
"glob": "7.1.6",
"got": "11.8.0",
"gulp": "4.0.2",
"gulp-rename": "2.0.0",
"gulp-replace": "1.0.0",
"gulp-sourcemaps": "2.6.5",
"gulp-terser": "1.4.0",
"gulp-tslint": "8.1.4",
"gulp-typescript": "6.0.0-alpha.1",
"hard-source-webpack-plugin": "0.13.1",
@@ -158,8 +158,8 @@
"js-yaml": "3.14.0",
"jsdom": "16.4.0",
"json5": "2.1.3",
"json5-loader": "4.0.0",
"jsonld": "3.1.1",
"json5-loader": "4.0.1",
"jsonld": "3.2.0",
"jsrsasign": "8.0.20",
"katex": "0.12.0",
"koa": "2.13.0",
@@ -175,24 +175,24 @@
"lookup-dns-cache": "2.1.0",
"markdown-it": "11.0.1",
"markdown-it-anchor": "6.0.0",
"mocha": "8.1.3",
"mocha": "8.2.1",
"moji": "0.5.1",
"ms": "2.1.2",
"multer": "1.4.2",
"nested-property": "4.0.0",
"node-fetch": "2.6.1",
"nodemailer": "6.4.13",
"nodemailer": "6.4.15",
"object-assign-deep": "0.4.0",
"os-utils": "0.0.14",
"p-cancelable": "2.0.0",
"parse5": "6.0.1",
"parsimmon": "1.16.0",
"pg": "8.4.1",
"pg": "8.4.2",
"portscanner": "2.2.0",
"postcss": "8.1.1",
"postcss-loader": "4.0.3",
"prismjs": "1.21.0",
"probe-image-size": "5.0.0",
"postcss": "8.1.6",
"postcss-loader": "4.0.4",
"prismjs": "1.22.0",
"probe-image-size": "6.0.0",
"promise-limit": "2.7.0",
"promise-sequential": "1.1.1",
"pug": "2.0.4",
@@ -201,8 +201,8 @@
"qrcode": "1.4.4",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.15.5",
"recaptcha-promise": "0.1.3",
"re2": "1.15.8",
"recaptcha-promise": "1.0.0",
"reconnecting-websocket": "4.4.0",
"redis": "3.0.2",
"redis-lock": "0.1.4",
@@ -214,48 +214,46 @@
"rimraf": "3.0.2",
"rndstr": "1.0.0",
"s-age": "1.1.2",
"sass": "1.27.0",
"sass-loader": "10.0.2",
"sass": "1.29.0",
"sass-loader": "10.0.5",
"seedrandom": "3.0.5",
"sharp": "0.26.1",
"sharp": "0.26.2",
"speakeasy": "2.0.0",
"stringz": "2.1.0",
"style-loader": "1.3.0",
"style-loader": "2.0.0",
"summaly": "2.4.0",
"syslog-pro": "1.0.0",
"systeminformation": "4.27.8",
"systeminformation": "4.28.1",
"syuilo-password-strength": "0.0.1",
"textarea-caret": "3.1.0",
"three": "0.117.1",
"tinycolor2": "1.4.2",
"tmp": "0.2.1",
"ts-loader": "8.0.4",
"ts-loader": "8.0.9",
"ts-node": "9.0.0",
"tslint": "6.1.3",
"tslint-sonarts": "1.9.0",
"typeorm": "0.2.28",
"typescript": "4.0.3",
"typeorm": "0.2.29",
"typescript": "4.0.5",
"ulid": "2.3.0",
"url-loader": "4.1.0",
"url-loader": "4.1.1",
"uuid": "8.3.1",
"v-debounce": "0.1.2",
"vue": "3.0.1",
"vue": "3.0.2",
"vue-color": "2.7.1",
"vue-draggable-next": "1.0.8",
"vue-i18n": "9.0.0-beta.4",
"vue-json-pretty": "1.7.0",
"vue-loader": "16.0.0-beta.7",
"vue-prism-component": "1.2.0",
"vue-i18n": "9.0.0-beta.6",
"vue-json-pretty": "1.7.1",
"vue-loader": "16.0.0-beta.8",
"vue-prism-editor": "1.2.2",
"vue-router": "4.0.0-beta.13",
"vue-router": "4.0.0-rc.2",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader-corejs3": "1.5.0",
"vue-template-compiler": "2.6.12",
"vuex": "4.0.0-beta.4",
"vuex": "4.0.0-rc.1",
"vuex-persistedstate": "3.1.0",
"web-push": "3.4.4",
"webpack": "5.1.3",
"webpack-cli": "3.3.12",
"webpack": "5.4.0",
"webpack-cli": "4.2.0",
"websocket": "1.0.32",
"ws": "7.3.1",
"xev": "2.0.1"

View File

@@ -2,10 +2,11 @@ import { ComponentCustomProperties } from 'vue';
import { Store } from 'vuex';
declare module '@vue/runtime-core' {
// tslint:disable-next-line:no-empty-interface
interface State {
}
interface ComponentCustomProperties {
$store: Store<State>
$store: Store<State>;
}
}

View File

@@ -0,0 +1,85 @@
<template>
<XWindow ref="window" :initial-width="400" :initial-height="500" :can-resize="true" @closed="$emit('closed')">
<template #header>
<Fa :icon="faExclamationCircle" style="margin-right: 0.5em;"/>
<i18n-t keypath="reportAbuseOf" tag="span">
<template #name>
<b><MkAcct :user="user"/></b>
</template>
</i18n-t>
</template>
<div class="dpvffvvy">
<div class="_section">
<div class="_content">
<MkTextarea v-model:value="comment">
<span>{{ $t('details') }}</span>
<template #desc>{{ $t('fillAbuseReportDescription') }}</template>
</MkTextarea>
</div>
</div>
<div class="_section">
<div class="_content">
<MkButton @click="send" primary full :disabled="comment.length === 0">{{ $t('send') }}</MkButton>
</div>
</div>
</div>
</XWindow>
</template>
<script lang="ts">
import { defineComponent, markRaw } from 'vue';
import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
import XWindow from '@/components/ui/window.vue';
import MkTextarea from '@/components/ui/textarea.vue';
import MkButton from '@/components/ui/button.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XWindow,
MkTextarea,
MkButton,
},
props: {
user: {
type: Object,
required: true,
},
initialComment: {
type: String,
required: false,
},
},
emits: ['closed'],
data() {
return {
comment: this.initialComment || '',
faExclamationCircle,
};
},
methods: {
send() {
os.apiWithDialog('users/report-abuse', {
userId: this.user.id,
comment: this.comment,
}, undefined, res => {
os.dialog({
type: 'success',
text: this.$t('abuseReported')
});
this.$refs.window.close();
});
}
},
});
</script>
<style lang="scss" scoped>
.dpvffvvy {
--section-padding: 16px;
}
</style>

View File

@@ -28,7 +28,7 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, markRaw } from 'vue';
import { emojilist } from '../../misc/emojilist';
import contains from '@/scripts/contains';
import { twemojiSvgBase } from '../../misc/twemoji-base';
@@ -122,17 +122,13 @@ export default defineComponent({
users: [],
hashtags: [],
emojis: [],
items: [],
select: -1,
emojilist,
emojiDb: [] as EmojiDef[]
}
},
computed: {
items(): HTMLCollection {
return (this.$refs.suggests as Element).children;
},
useOsNativeEmojis(): boolean {
return this.$store.state.device.useOsNativeEmojis;
}
@@ -148,6 +144,7 @@ export default defineComponent({
updated() {
this.setPosition();
this.items = (this.$refs.suggests as Element | undefined)?.children || [];
},
mounted() {
@@ -180,7 +177,7 @@ export default defineComponent({
emojiDefinitions.sort((a, b) => a.name.length - b.name.length);
this.emojiDb = emojiDefinitions.concat(emjdb);
this.emojiDb = markRaw(emojiDefinitions.concat(emjdb));
//#endregion
this.textarea.addEventListener('keydown', this.onKeydown);
@@ -371,6 +368,7 @@ export default defineComponent({
selectNext() {
if (++this.select >= this.items.length) this.select = 0;
if (this.items.length === 0) this.select = -1;
this.applySelect();
},
@@ -384,8 +382,10 @@ export default defineComponent({
el.removeAttribute('data-selected');
}
this.items[this.select].setAttribute('data-selected', 'true');
(this.items[this.select] as any).focus();
if (this.select !== -1) {
this.items[this.select].setAttribute('data-selected', 'true');
(this.items[this.select] as any).focus();
}
},
chooseUser() {

View File

@@ -2,9 +2,9 @@
<span class="eiwwqkts" :class="{ cat }" :title="acct(user)" v-if="disableLink" v-user-preview="disablePreview ? undefined : user.id" @click="onClick">
<img class="inner" :src="url"/>
</span>
<router-link class="eiwwqkts" :class="{ cat }" :to="userPage(user)" :title="acct(user)" :target="target" v-else v-user-preview="disablePreview ? undefined : user.id">
<MkA class="eiwwqkts" :class="{ cat }" :to="userPage(user)" :title="acct(user)" :target="target" v-else v-user-preview="disablePreview ? undefined : user.id">
<img class="inner" :src="url"/>
</router-link>
</MkA>
</template>
<script lang="ts">

View File

@@ -1,6 +1,6 @@
<template>
<router-link :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1">
<div class="banner" v-if="channel.bannerUrl" :style="`background-image: url('${channel.bannerUrl}')`">
<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1">
<div class="banner" :style="bannerStyle">
<div class="fade"></div>
<div class="name"><Fa :icon="faSatelliteDish"/> {{ channel.name }}</div>
<div class="status">
@@ -30,7 +30,7 @@
{{ $t('updatedAt') }}: <MkTime :time="channel.lastNotedAt"/>
</span>
</footer>
</router-link>
</MkA>
</template>
<script lang="ts">
@@ -45,6 +45,16 @@ export default defineComponent({
},
},
computed: {
bannerStyle() {
if (this.channel.bannerUrl) {
return { backgroundImage: `url(${this.channel.bannerUrl})` };
} else {
return { backgroundColor: '#4c5e6d' };
}
}
},
data() {
return {
faSatelliteDish, faUsers, faPencilAlt,

View File

@@ -1,17 +1,14 @@
<template>
<XPrism :inline="inline" :language="prismLang">{{ code }}</XPrism>
<code v-if="inline" v-html="html" :class="`language-${prismLang}`"></code>
<pre v-else :class="`language-${prismLang}`"><code v-html="html" :class="`language-${prismLang}`"></code></pre>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import 'prismjs';
import 'prismjs/themes/prism-okaidia.css';
import XPrism from 'vue-prism-component';import * as os from '@/os';
export default defineComponent({
components: {
XPrism
},
props: {
code: {
type: String,
@@ -29,6 +26,9 @@ export default defineComponent({
computed: {
prismLang() {
return Prism.languages[this.lang] ? this.lang : 'js';
},
html() {
return Prism.highlight(this.code, Prism.languages[this.prismLang], this.prismLang);
}
}
});

View File

@@ -4,7 +4,7 @@
<div class="icon" v-if="icon">
<Fa :icon="icon"/>
</div>
<div class="icon" v-else-if="!input && !select && !user" :class="type">
<div class="icon" v-else-if="!input && !select" :class="type">
<Fa :icon="faCheck" v-if="type === 'success'"/>
<Fa :icon="faTimesCircle" v-if="type === 'error'"/>
<Fa :icon="faExclamationTriangle" v-if="type === 'warning'"/>
@@ -12,11 +12,9 @@
<Fa :icon="faQuestionCircle" v-if="type === 'question'"/>
<Fa :icon="faSpinner" pulse v-if="type === 'waiting'"/>
</div>
<header v-if="title" v-html="title"></header>
<header v-if="title == null && user">{{ $t('enterUsername') }}</header>
<div class="body" v-if="text" v-html="text"></div>
<header v-if="title"><Mfm :text="title"/></header>
<div class="body" v-if="text"><Mfm :text="text"/></div>
<MkInput v-if="input" v-model:value="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder" @keydown="onInputKeydown"></MkInput>
<MkInput v-if="user" v-model:value="userInputValue" autofocus @keydown="onInputKeydown"><template #prefix>@</template></MkInput>
<MkSelect v-if="select" v-model:value="selectedValue" autofocus>
<template v-if="select.items">
<option v-for="item in select.items" :value="item.value">{{ item.text }}</option>
@@ -28,8 +26,8 @@
</template>
</MkSelect>
<div class="buttons" v-if="(showOkButton || showCancelButton) && !actions">
<MkButton inline @click="ok" v-if="showOkButton" primary :autofocus="!input && !select && !user" :disabled="!canOk">{{ (showCancelButton || input || select || user) ? $t('ok') : $t('gotIt') }}</MkButton>
<MkButton inline @click="cancel" v-if="showCancelButton || input || select || user">{{ $t('cancel') }}</MkButton>
<MkButton inline @click="ok" v-if="showOkButton" primary :autofocus="!input && !select">{{ (showCancelButton || input || select) ? $t('ok') : $t('gotIt') }}</MkButton>
<MkButton inline @click="cancel" v-if="showCancelButton || input || select">{{ $t('cancel') }}</MkButton>
</div>
<div class="buttons" v-if="actions">
<MkButton v-for="action in actions" inline @click="() => { action.callback(); close(); }" :primary="action.primary" :key="action.text">{{ action.text }}</MkButton>
@@ -46,8 +44,6 @@ import MkModal from '@/components/ui/modal.vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/ui/input.vue';
import MkSelect from '@/components/ui/select.vue';
import parseAcct from '../../misc/acct/parse';
import * as os from '@/os';
export default defineComponent({
components: {
@@ -77,9 +73,6 @@ export default defineComponent({
select: {
required: false
},
user: {
required: false
},
icon: {
required: false
},
@@ -105,28 +98,12 @@ export default defineComponent({
data() {
return {
inputValue: this.input && this.input.default ? this.input.default : null,
userInputValue: null,
selectedValue: this.select ? this.select.default ? this.select.default : this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null,
canOk: true,
faTimesCircle, faQuestionCircle, faSpinner, faInfoCircle, faExclamationTriangle, faCheck
};
},
watch: {
userInputValue() {
if (this.user) {
os.api('users/show', parseAcct(this.userInputValue)).then(u => {
this.canOk = u != null;
}).catch(() => {
this.canOk = false;
});
}
},
},
mounted() {
if (this.user) this.canOk = false;
document.addEventListener('keydown', this.onKeydown);
},
@@ -141,21 +118,13 @@ export default defineComponent({
},
async ok() {
if (!this.canOk) return;
if (!this.showOkButton) return;
if (this.user) {
const user = await os.api('users/show', parseAcct(this.userInputValue));
if (user) {
this.done(false, user);
}
} else {
const result =
this.input ? this.inputValue :
this.select ? this.selectedValue :
true;
this.done(false, result);
}
const result =
this.input ? this.inputValue :
this.select ? this.selectedValue :
true;
this.done(false, result);
},
cancel() {
@@ -200,15 +169,15 @@ export default defineComponent({
font-size: 32px;
&.success {
color: var(--accent);
color: var(--success);
}
&.error {
color: #ec4137;
color: var(--error);
}
&.warning {
color: #ecb637;
color: var(--warn);
}
> * {

View File

@@ -0,0 +1,70 @@
<template>
<XModalWindow ref="dialog"
:width="800"
:height="500"
:with-ok-button="true"
:ok-button-disabled="(type === 'file') && (selected.length === 0)"
@click="cancel()"
@close="cancel()"
@ok="ok()"
@closed="$emit('closed')"
>
<template #header>
{{ multiple ? ((type === 'file') ? $t('selectFiles') : $t('selectFolders')) : ((type === 'file') ? $t('selectFile') : $t('selectFolder')) }}
<span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ number(selected.length) }})</span>
</template>
<XDrive :multiple="multiple" @changeSelection="onChangeSelection" @selected="ok()" :select="type"/>
</XModalWindow>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XDrive from './drive.vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import number from '@/filters/number';
export default defineComponent({
components: {
XDrive,
XModalWindow,
},
props: {
type: {
type: String,
required: false,
default: 'file'
},
multiple: {
type: Boolean,
default: false
}
},
emits: ['done', 'closed'],
data() {
return {
selected: []
};
},
methods: {
ok() {
this.$emit('done', this.selected);
this.$refs.dialog.close();
},
cancel() {
this.$emit('done');
this.$refs.dialog.close();
},
onChangeSelection(xs) {
this.selected = xs;
},
number
}
});
</script>

View File

@@ -1,72 +1,44 @@
<template>
<XModalWindow ref="dialog"
:width="800"
:height="500"
:with-ok-button="true"
:ok-button-disabled="(type === 'file') && (selected.length === 0)"
@click="cancel()"
@close="cancel()"
@ok="ok()"
<XWindow ref="window"
:initial-width="800"
:initial-height="500"
:can-resize="true"
@closed="$emit('closed')"
>
<template #header>
{{ multiple ? ((type === 'file') ? $t('selectFiles') : $t('selectFolders')) : ((type === 'file') ? $t('selectFile') : $t('selectFolder')) }}
<span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ number(selected.length) }})</span>
{{ $t('drive') }}
</template>
<div>
<XDrive :multiple="multiple" @changeSelection="onChangeSelection" @selected="ok()" :select="type"/>
</div>
</XModalWindow>
<XDrive :initial-folder="initialFolder"/>
</XWindow>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XDrive from './drive.vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import number from '@/filters/number';
import XWindow from '@/components/ui/window.vue';
export default defineComponent({
components: {
XDrive,
XModalWindow,
XWindow,
},
props: {
type: {
type: String,
required: false,
default: 'file'
initialFolder: {
type: Object,
required: false
},
multiple: {
type: Boolean,
default: false
}
},
emits: ['done', 'closed'],
emits: ['closed'],
data() {
return {
selected: []
};
},
methods: {
ok() {
this.$emit('done', this.selected);
this.$refs.dialog.close();
},
cancel() {
this.$emit('done');
this.$refs.dialog.close();
},
onChangeSelection(xs) {
this.selected = xs;
},
number
}
});
</script>

View File

@@ -2,6 +2,7 @@
<div class="rghtznwe"
:class="{ draghover }"
@click="onClick"
@contextmenu.stop="onContextmenu"
@mouseover="onMouseover"
@mouseout="onMouseout"
@dragover.prevent.stop="onDragover"
@@ -27,8 +28,9 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { faFolder, faFolderOpen } from '@fortawesome/free-regular-svg-icons';
import { faFolder, faFolderOpen, faTrashAlt, faWindowRestore } from '@fortawesome/free-regular-svg-icons';
import * as os from '@/os';
import { faICursor } from '@fortawesome/free-solid-svg-icons';
export default defineComponent({
props: {
@@ -241,6 +243,28 @@ export default defineComponent({
value: this.folder.id
});
},
onContextmenu(e) {
os.contextMenu([{
text: this.$t('openInWindow'),
icon: faWindowRestore,
action: () => {
os.popup(import('./drive-window.vue'), {
initialFolder: this.folder
}, {
}, 'closed');
}
}, null, {
text: this.$t('rename'),
icon: faICursor,
action: this.rename
}, null, {
text: this.$t('delete'),
icon: faTrashAlt,
danger: true,
action: this.deleteFolder
}], e);
},
}
});
</script>

View File

@@ -64,7 +64,7 @@ export default defineComponent({
},
props: {
initFolder: {
initialFolder: {
type: Object,
required: false
},
@@ -151,8 +151,8 @@ export default defineComponent({
this.connection.on('folderUpdated', this.onStreamDriveFolderUpdated);
this.connection.on('folderDeleted', this.onStreamDriveFolderDeleted);
if (this.initFolder) {
this.move(this.initFolder);
if (this.initialFolder) {
this.move(this.initialFolder);
} else {
this.fetch();
}
@@ -639,6 +639,10 @@ export default defineComponent({
<style lang="scss" scoped>
.yfudmmck {
display: flex;
flex-direction: column;
height: 100%;
> nav {
display: block;
z-index: 2;
@@ -698,6 +702,7 @@ export default defineComponent({
}
> .main {
flex: 1;
overflow: auto;
&, * {

View File

@@ -1,75 +1,104 @@
<template>
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
<div class="omfetrab _popup">
<header>
<button v-for="(category, i) in categories"
class="_button"
@click="go(category)"
:class="{ active: category.isActive }"
:key="i"
>
<Fa :icon="category.icon" fixed-width/>
</button>
</header>
<div class="omfetrab _popup" :class="{ compact }">
<input ref="search" class="search" :class="{ filled: q != null && q != '' }" v-model.trim="q" :placeholder="$t('search')" @paste.stop="paste" @keyup.enter="done()">
<div class="emojis">
<template v-if="categories[0].isActive">
<header class="category"><Fa :icon="faHistory" fixed-width/> {{ $t('recentUsed') }}</header>
<div class="list">
<button v-for="emoji in ($store.state.device.recentEmojis || [])"
<section class="result">
<div v-if="searchResultCustom.length > 0">
<button v-for="emoji in searchResultCustom"
class="_button"
:title="emoji.name"
@click="chosen(emoji)"
@click="chosen(emoji, $event)"
:key="emoji"
tabindex="0"
>
<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>
<img v-else :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
</button>
</div>
<header class="category"><Fa :icon="faAsterisk" fixed-width/> {{ $t('customEmojis') }}</header>
</template>
<template v-if="categories.find(x => x.isActive).name">
<div class="list">
<button v-for="emoji in emojilist.filter(e => e.category === categories.find(x => x.isActive).name)"
<div v-if="searchResultUnicode.length > 0">
<button v-for="emoji in searchResultUnicode"
class="_button"
:title="emoji.name"
@click="chosen(emoji)"
@click="chosen(emoji, $event)"
:key="emoji.name"
tabindex="0"
>
<MkEmoji :emoji="emoji.char"/>
</button>
</div>
</section>
<div class="index">
<section v-if="showPinned">
<div>
<button v-for="emoji in pinned"
class="_button"
@click="chosen(emoji, $event)"
tabindex="0"
>
<MkEmoji :emoji="emoji" :normal="true"/>
</button>
</div>
</section>
<section>
<header class="_acrylic"><Fa :icon="faClock" fixed-width/> {{ $t('recentUsed') }}</header>
<div>
<button v-for="emoji in $store.state.device.recentlyUsedEmojis"
class="_button"
@click="chosen(emoji, $event)"
:key="emoji"
>
<MkEmoji :emoji="emoji" :normal="true"/>
</button>
</div>
</section>
<div class="arrow"><Fa :icon="faChevronDown"/></div>
</div>
<section v-for="category in customEmojiCategories" :key="'custom:' + category" class="custom">
<header class="_acrylic" v-appear="() => visibleCategories[category] = true">{{ category || $t('other') }}</header>
<div v-if="visibleCategories[category]">
<button v-for="emoji in customEmojis.filter(e => e.category === category)"
class="_button"
:title="emoji.name"
@click="chosen(emoji, $event)"
:key="emoji.name"
>
<img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
</button>
</div>
</section>
<section v-for="category in categories" :key="category.name" class="unicode">
<header class="_acrylic" v-appear="() => category.isActive = true"><Fa :icon="category.icon" fixed-width/> {{ category.name }}</header>
<div v-if="category.isActive">
<button v-for="emoji in emojilist.filter(e => e.category === category.name)"
class="_button"
:title="emoji.name"
@click="chosen(emoji, $event)"
:key="emoji.name"
>
<MkEmoji :emoji="emoji.char"/>
</button>
</div>
</template>
<template v-else>
<div v-for="(key, i) in Object.keys(customEmojis)" :key="i">
<header class="sub" v-if="key">{{ key }}</header>
<div class="list">
<button v-for="emoji in customEmojis[key]"
class="_button"
:title="emoji.name"
@click="chosen(emoji)"
:key="emoji.name"
>
<img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
</button>
</div>
</div>
</template>
</section>
</div>
</div>
</MkModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, markRaw } from 'vue';
import { emojilist } from '../../misc/emojilist';
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faHistory, faUser } from '@fortawesome/free-solid-svg-icons';
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faClock, faUser, faChevronDown } from '@fortawesome/free-solid-svg-icons';
import { faHeart, faFlag, faLaugh } from '@fortawesome/free-regular-svg-icons';
import { groupByX } from '../../prelude/array';
import MkModal from '@/components/ui/modal.vue';
import Particle from '@/components/particle.vue';
import * as os from '@/os';
export default defineComponent({
components: {
@@ -80,20 +109,30 @@ export default defineComponent({
src: {
required: false
},
showPinned: {
required: false,
default: true
},
compact: {
required: false
},
},
emits: ['done', 'closed'],
data() {
return {
emojilist,
emojilist: markRaw(emojilist),
getStaticImageUrl,
customEmojis: {},
faGlobe, faHistory,
pinned: this.$store.state.settings.reactions,
customEmojiCategories: this.$store.getters['instance/emojiCategories'],
customEmojis: this.$store.state.instance.meta.emojis,
visibleCategories: {},
q: null,
searchResultCustom: [],
searchResultUnicode: [],
faGlobe, faClock, faChevronDown,
categories: [{
icon: faAsterisk,
isActive: true
}, {
name: 'face',
icon: faLaugh,
isActive: false
@@ -134,38 +173,212 @@ export default defineComponent({
};
},
created() {
let local = this.$store.state.instance.meta.emojis;
local = groupByX(local, (x: any) => x.category || '');
this.customEmojis = local;
watch: {
q() {
if (this.q == null || this.q === '') {
this.searchResultCustom = [];
this.searchResultUnicode = [];
return;
}
const q = this.q.replace(/:/g, '');
const searchCustom = () => {
const max = 8;
const emojis = this.customEmojis;
const matches = new Set();
const exactMatch = emojis.find(e => e.name === q);
if (exactMatch) matches.add(exactMatch);
if (q.includes(' ')) { // AND検索
const keywords = q.split(' ');
// 名前にキーワードが含まれている
for (const emoji of emojis) {
if (keywords.every(keyword => emoji.name.includes(keyword))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
// 名前またはエイリアスにキーワードが含まれている
for (const emoji of emojis) {
if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.aliases.some(alias => alias.includes(keyword)))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
} else {
for (const emoji of emojis) {
if (emoji.name.startsWith(q)) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.aliases.some(alias => alias.startsWith(q))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.name.includes(q)) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.aliases.some(alias => alias.includes(q))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
}
return matches;
};
const searchUnicode = () => {
const max = 8;
const emojis = this.emojilist;
const matches = new Set();
const exactMatch = emojis.find(e => e.name === q);
if (exactMatch) matches.add(exactMatch);
if (q.includes(' ')) { // AND検索
const keywords = q.split(' ');
// 名前にキーワードが含まれている
for (const emoji of emojis) {
if (keywords.every(keyword => emoji.name.includes(keyword))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
// 名前またはエイリアスにキーワードが含まれている
for (const emoji of emojis) {
if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.keywords.some(alias => alias.includes(keyword)))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
} else {
for (const emoji of emojis) {
if (emoji.name.startsWith(q)) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.keywords.some(keyword => keyword.startsWith(q))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.name.includes(q)) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.keywords.some(keyword => keyword.includes(q))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
}
return matches;
};
this.searchResultCustom = Array.from(searchCustom());
this.searchResultUnicode = Array.from(searchUnicode());
}
},
mounted() {
const isIos = navigator.userAgent.includes('WebKit') && !navigator.userAgent.includes('Chrome');
if (!isIos) {
this.$refs.search.focus({
preventScroll: true
});
}
},
methods: {
go(category: any) {
this.goCategory(category.name);
getKey(emoji: any) {
return typeof emoji === 'string' ? emoji : (emoji.char || `:${emoji.name}:`);
},
goCategory(name: string) {
let matched = false;
for (const c of this.categories) {
c.isActive = c.name === name;
if (c.isActive) {
matched = true;
}
chosen(emoji: any, ev) {
if (ev) {
const el = ev.currentTarget || ev.target;
const rect = el.getBoundingClientRect();
const x = rect.left + (el.clientWidth / 2);
const y = rect.top + (el.clientHeight / 2);
os.popup(Particle, { x, y }, {}, 'end');
}
if (!matched) {
this.categories[0].isActive = true;
}
},
chosen(emoji: any) {
const getKey = (emoji: any) => emoji.char || `:${emoji.name}:`;
let recents = this.$store.state.device.recentEmojis || [];
recents = recents.filter((e: any) => getKey(e) !== getKey(emoji));
recents.unshift(emoji)
this.$store.commit('device/set', { key: 'recentEmojis', value: recents.splice(0, 16) });
this.$emit('done', getKey(emoji));
const key = this.getKey(emoji);
this.$emit('done', key);
this.$refs.modal.close();
// 最近使った絵文字更新
if (!this.pinned.includes(key)) {
let recents = this.$store.state.device.recentlyUsedEmojis;
recents = recents.filter((e: any) => e !== key);
recents.unshift(key);
this.$store.commit('device/set', { key: 'recentlyUsedEmojis', value: recents.splice(0, 16) });
}
},
paste(event) {
const paste = (event.clipboardData || window.clipboardData).getData('text');
if (this.done(paste)) {
event.preventDefault();
}
},
done(query) {
if (query == null) query = this.q;
if (query == null) return;
const q = query.replace(/:/g, '');
const exactMatchCustom = this.customEmojis.find(e => e.name === q);
if (exactMatchCustom) {
this.chosen(exactMatchCustom);
return true;
}
const exactMatchUnicode = this.emojilist.find(e => e.char === q || e.name === q);
if (exactMatchUnicode) {
this.chosen(exactMatchUnicode);
return true;
}
if (this.searchResultCustom.length > 0) {
this.chosen(this.searchResultCustom[0]);
return true;
}
if (this.searchResultUnicode.length > 0) {
this.chosen(this.searchResultUnicode[0]);
return true;
}
},
}
});
@@ -173,87 +386,123 @@ export default defineComponent({
<style lang="scss" scoped>
.omfetrab {
width: 350px;
$eachSize: 40px;
$pad: 8px;
> header {
display: flex;
display: flex;
flex-direction: column;
width: ($eachSize * 7) + ($pad * 2);
contain: content;
--height: 300px;
> button {
flex: 1;
padding: 10px 0;
font-size: 16px;
transition: color 0.2s ease;
&.compact {
width: ($eachSize * 5) + ($pad * 2);
--height: 210px;
}
&:hover {
color: var(--textHighlighted);
transition: color 0s;
}
> .search {
width: 100%;
padding: 12px;
box-sizing: border-box;
font-size: 1em;
outline: none;
border: none;
background: transparent;
color: var(--fg);
&.active {
color: var(--accent);
transition: color 0s;
}
&:not(.filled) {
order: 1;
z-index: 2;
box-shadow: 0px -1px 0 0px var(--divider);
}
}
> .emojis {
height: 300px;
height: var(--height);
overflow-y: auto;
overflow-x: hidden;
> header.category {
position: sticky;
top: 0;
left: 0;
z-index: 1;
padding: 8px;
background: var(--panel);
font-size: 12px;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
header.sub {
padding: 4px 8px;
font-size: 12px;
}
div.list {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
gap: 4px;
padding: 8px;
> button {
position: relative;
padding: 0;
> .index {
min-height: var(--height);
position: relative;
border-bottom: solid 1px var(--divider);
> .arrow {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 16px 0;
text-align: center;
opacity: 0.5;
pointer-events: none;
}
}
&:before {
content: '';
display: block;
width: 1px;
height: 0;
padding-bottom: 100%;
}
section {
> header {
position: sticky;
top: 0;
left: 0;
z-index: 1;
padding: 8px;
font-size: 12px;
}
> div {
padding: $pad;
> button {
position: relative;
padding: 0;
width: $eachSize;
height: $eachSize;
border-radius: 4px;
&:focus {
outline: solid 2px var(--focus);
z-index: 1;
}
&:hover {
background: rgba(0, 0, 0, 0.05);
}
&:active {
background: var(--accent);
box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
}
&:hover {
> * {
transform: scale(1.2);
transition: transform 0s;
font-size: 24px;
height: 1.25em;
vertical-align: -.25em;
pointer-events: none;
}
}
}
> * {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: contain;
font-size: 28px;
transition: transform 0.2s ease;
pointer-events: none;
&.result {
border-bottom: solid 1px var(--divider);
&:empty {
display: none;
}
}
&.unicode {
min-height: 384px;
}
&.custom {
min-height: 64px;
}
}
}
}

View File

@@ -2,24 +2,19 @@
<img v-if="customEmoji" class="mk-emoji custom" :class="{ normal, noStyle }" :src="url" :alt="alt" :title="alt"/>
<img v-else-if="char && !useOsNativeEmojis" class="mk-emoji" :src="url" :alt="alt" :title="alt"/>
<span v-else-if="char && useOsNativeEmojis">{{ char }}</span>
<span v-else>:{{ name }}:</span>
<span v-else>{{ emoji }}</span>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
import { twemojiSvgBase } from '../../misc/twemoji-base';
import * as os from '@/os';
export default defineComponent({
props: {
name: {
type: String,
required: false
},
emoji: {
type: String,
required: false
required: true
},
normal: {
type: Boolean,
@@ -50,6 +45,10 @@ export default defineComponent({
},
computed: {
isCustom(): boolean {
return this.emoji.startsWith(':');
},
alt(): string {
return this.customEmoji ? `:${this.customEmoji.name}:` : this.char;
},
@@ -69,8 +68,8 @@ export default defineComponent({
watch: {
ce: {
handler() {
if (this.name) {
const customEmoji = this.ce.find(x => x.name == this.name);
if (this.isCustom) {
const customEmoji = this.ce.find(x => x.name === this.emoji.substr(1, this.emoji.length - 2));
if (customEmoji) {
this.customEmoji = customEmoji;
this.url = this.$store.state.device.disableShowingAnimatedImages
@@ -84,7 +83,7 @@ export default defineComponent({
},
created() {
if (!this.name) {
if (!this.isCustom) {
this.char = this.emoji;
}

View File

@@ -1,102 +0,0 @@
<template>
<div class="eqryymyo">
<div class="header">
<time ref="time" class="_ghost">
<span class="yyyymmdd">{{ yyyy }}/{{ mm }}/{{ dd }}</span>
<br>
<span class="hhnn">{{ hh }}<span :style="{ visibility: now.getSeconds() % 2 == 0 ? 'visible' : 'hidden' }">:</span>{{ nn }}</span>
</time>
</div>
<div class="content _panel _ghost">
<MkClock/>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import MkClock from './analog-clock.vue';
import * as os from '@/os';
export default defineComponent({
components: {
MkClock
},
data() {
return {
now: new Date(),
clock: null
};
},
computed: {
yyyy(): number {
return this.now.getFullYear();
},
mm(): string {
return ('0' + (this.now.getMonth() + 1)).slice(-2);
},
dd(): string {
return ('0' + this.now.getDate()).slice(-2);
},
hh(): string {
return ('0' + this.now.getHours()).slice(-2);
},
nn(): string {
return ('0' + this.now.getMinutes()).slice(-2);
}
},
mounted() {
this.tick();
this.clock = setInterval(this.tick, 1000);
},
beforeUnmount() {
clearInterval(this.clock);
},
methods: {
tick() {
this.now = new Date();
}
}
});
</script>
<style lang="scss" scoped>
.eqryymyo {
display: inline-block;
overflow: visible;
> .header {
padding: 0 12px;
padding-top: 4px;
text-align: center;
font-size: 12px;
font-family: Lucida Console, Courier, monospace;
&:hover + .content {
opacity: 1;
}
> time {
display: table-cell;
vertical-align: middle;
height: 48px;
> .yyyymmdd {
opacity: 0.7;
}
}
}
> .content {
opacity: 0;
display: block;
position: absolute;
top: auto;
right: 0;
margin: 16px 0 0 0;
padding: 16px;
width: 230px;
transition: opacity 0.2s ease;
}
}
</style>

View File

@@ -6,7 +6,7 @@
<footer>
<span>{{ image.type }}</span>
<span>{{ bytes(image.size) }}</span>
<span v-if="image.properties?.width">{{ number(image.properties.width) }}px × {{ number(image.properties.height) }}px</span>
<span v-if="image.properties && image.properties.width">{{ number(image.properties.width) }}px × {{ number(image.properties.height) }}px</span>
</footer>
</div>
</MkModal>
@@ -41,10 +41,13 @@ export default defineComponent({
<style lang="scss" scoped>
.xubzgfga {
max-width: 1024px;
display: flex;
flex-direction: column;
height: 100%;
> header,
> footer {
align-self: center;
display: inline-block;
padding: 6px 9px;
font-size: 90%;
@@ -60,7 +63,10 @@ export default defineComponent({
> img {
display: block;
max-width: 100%;
flex: 1;
min-height: 0;
object-fit: contain;
width: 100%;
cursor: zoom-out;
image-orientation: from-image;
}

View File

@@ -1,6 +1,7 @@
import { App } from 'vue';
import mfm from './misskey-flavored-markdown.vue';
import a from './ui/a.vue';
import acct from './acct.vue';
import avatar from './avatar.vue';
import emoji from './emoji.vue';
@@ -10,10 +11,10 @@ import time from './time.vue';
import url from './url.vue';
import loading from './loading.vue';
import error from './error.vue';
import streamIndicator from './stream-indicator.vue';
export default function(app: App) {
app.component('Mfm', mfm);
app.component('MkA', a);
app.component('MkAcct', acct);
app.component('MkAvatar', avatar);
app.component('MkEmoji', emoji);
@@ -23,5 +24,4 @@ export default function(app: App) {
app.component('MkUrl', url);
app.component('MkLoading', loading);
app.component('MkError', error);
app.component('StreamIndicator', streamIndicator);
}

View File

@@ -0,0 +1,62 @@
<template>
<div class="hpaizdrt" :style="bg">
<img v-if="info.faviconUrl" class="icon" :src="info.faviconUrl"/>
<span class="name">{{ info.name }}</span>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { instanceName } from '@/config';
export default defineComponent({
props: {
instance: {
type: Object,
required: false
},
},
data() {
return {
info: this.instance || {
faviconUrl: '/favicon.ico',
name: instanceName,
themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement)?.content
}
}
},
computed: {
bg(): any {
const themeColor = this.info.themeColor || '#777777';
return {
background: `linear-gradient(90deg, ${themeColor}, ${themeColor + '00'})`
};
}
}
});
</script>
<style lang="scss" scoped>
.hpaizdrt {
$height: 1.1rem;
height: $height;
border-radius: 4px 0 0 4px;
overflow: hidden;
color: #fff;
> .icon {
height: 100%;
}
> .name {
margin-left: 4px;
line-height: $height;
font-size: 0.9em;
vertical-align: top;
font-weight: bold;
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<component :is="self ? 'router-link' : 'a'" class="xlcxczvw _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
<component :is="self ? 'MkA' : 'a'" class="xlcxczvw _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
:title="url"
@@ -46,7 +46,7 @@ export default defineComponent({
if (!document.body.contains(this.$el)) return;
if (this.close) return;
const { dispose } = os.popup(await import('@/components/url-preview-popup.vue'), {
const { dispose } = await os.popup(import('@/components/url-preview-popup.vue'), {
url: this.url,
source: this.$el
});

View File

@@ -32,8 +32,6 @@ export default defineComponent({
raw: {
default: false
},
// specify the parent element
parentElement: {}
},
data() {
return {
@@ -66,7 +64,7 @@ export default defineComponent({
if (this.$refs.gridOuter) {
let height = 287;
const parent = this.parentElement || this.$parent.$el;
const parent = this.$parent.$el;
if (this.$refs.gridOuter.clientHeight) {
height = this.$refs.gridOuter.clientHeight;
@@ -81,11 +79,6 @@ export default defineComponent({
});
}
},
watch: {
parentElement() {
this.size();
}
}
});
</script>

View File

@@ -1,11 +1,11 @@
<template>
<router-link class="ldlomzub" :class="{ isMe }" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')">
<MkA class="ldlomzub" :class="{ isMe }" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')">
<span class="me" v-if="isMe">{{ $t('you') }}</span>
<span class="main">
<span class="username">@{{ username }}</span>
<span class="host" v-if="(host != localHost) || $store.state.settings.showFullAcct">@{{ toUnicode(host) }}</span>
</span>
</router-link>
</MkA>
<a class="ldlomzub" :href="url" target="_blank" rel="noopener" v-else>
<span class="main">
<span class="username">@{{ username }}</span>

View File

@@ -9,8 +9,8 @@ import { concat } from '../../prelude/array';
import MkFormula from './formula.vue';
import MkCode from './code.vue';
import MkGoogle from './google.vue';
import MkA from './ui/a.vue';
import { host } from '@/config';
import { RouterLink } from 'vue-router';
export default defineComponent({
props: {
@@ -77,10 +77,66 @@ export default defineComponent({
}, genEl(token.children));
}
case 'big': {
return h('strong', {
style: `display: inline-block; font-size: 150%;` + (this.$store.state.device.animatedMfm ? 'animation: anime-tada 1s linear infinite both;' : ''),
}, genEl(token.children));
case 'fn': {
// TODO: CSSを文字列で組み立てていくと token.node.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
let style;
switch (token.node.props.name) {
case 'tada': {
style = `font-size: 150%;` + (this.$store.state.device.animatedMfm ? 'animation: tada 1s linear infinite both;' : '');
break;
}
case 'jelly': {
const speed = token.node.props.args.speed || '1s';
style = (this.$store.state.device.animatedMfm ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
break;
}
case 'twitch': {
const speed = token.node.props.args.speed || '0.5s';
style = this.$store.state.device.animatedMfm ? `animation: mfm-twitch ${speed} ease infinite;` : '';
break;
}
case 'shake': {
const speed = token.node.props.args.speed || '0.5s';
style = this.$store.state.device.animatedMfm ? `animation: mfm-shake ${speed} ease infinite;` : '';
break;
}
case 'spin': {
const direction =
token.node.props.args.left ? 'reverse' :
token.node.props.args.alternate ? 'alternate' :
'normal';
const anime =
token.node.props.args.x ? 'mfm-spinX' :
token.node.props.args.y ? 'mfm-spinY' :
'mfm-spin';
const speed = token.node.props.args.speed || '1.5s';
style = this.$store.state.device.animatedMfm ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : '';
break;
}
case 'jump': {
style = this.$store.state.device.animatedMfm ? 'animation: mfm-jump 0.75s linear infinite;' : '';
break;
}
case 'bounce': {
style = this.$store.state.device.animatedMfm ? 'animation: mfm-bounce 0.75s linear infinite; transform-origin: center bottom;' : '';
break;
}
case 'flip': {
const transform =
(token.node.props.args.h && token.node.props.args.v) ? 'scale(-1, -1)' :
token.node.props.args.v ? 'scaleY(-1)' :
'scaleX(-1)';
style = `transform: ${transform};`;
break;
}
}
if (style == null) {
return h('span', {}, ['[', token.node.props.name, ...genEl(token.children), ']']);
} else {
return h('span', {
style: 'display: inline-block;' + style,
}, genEl(token.children));
}
}
case 'small': {
@@ -95,36 +151,6 @@ export default defineComponent({
}, genEl(token.children))];
}
case 'motion': {
return h('span', {
style: 'display: inline-block;' + (this.$store.state.device.animatedMfm ? 'animation: anime-rubberBand 1s linear infinite both;' : ''),
}, genEl(token.children));
}
case 'spin': {
const direction =
token.node.props.attr == 'left' ? 'reverse' :
token.node.props.attr == 'alternate' ? 'alternate' :
'normal';
const style = this.$store.state.device.animatedMfm
? `animation: anime-spin 1.5s linear infinite; animation-direction: ${direction};` : '';
return h('span', {
style: 'display: inline-block;' + style
}, genEl(token.children));
}
case 'jump': {
return h('span', {
style: this.$store.state.device.animatedMfm ? 'display: inline-block; animation: anime-jump 0.75s linear infinite;' : 'display: inline-block;'
}, genEl(token.children));
}
case 'flip': {
return h('span', {
style: 'display: inline-block; transform: scaleX(-1);'
}, genEl(token.children));
}
case 'url': {
return [h(MkUrl, {
key: Math.random(),
@@ -150,7 +176,7 @@ export default defineComponent({
}
case 'hashtag': {
return [h(RouterLink, {
return [h(MkA, {
key: Math.random(),
to: this.isNote ? `/tags/${encodeURIComponent(token.node.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.node.props.hashtag)}`,
style: 'color:var(--hashtag);'
@@ -186,17 +212,10 @@ export default defineComponent({
}
}
case 'title': {
return [h('div', {
class: 'title'
}, genEl(token.children))];
}
case 'emoji': {
return [h(MkEmoji, {
key: Math.random(),
emoji: token.node.props.emoji,
name: token.node.props.name,
emoji: token.node.props.name ? `:${token.node.props.name}:` : token.node.props.emoji,
customEmojis: this.customEmojis,
normal: this.plain
})];

View File

@@ -13,6 +13,103 @@ export default defineComponent({
});
</script>
<style lang="scss">
@keyframes mfm-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes mfm-spinX {
0% { transform: perspective(128px) rotateX(0deg); }
100% { transform: perspective(128px) rotateX(360deg); }
}
@keyframes mfm-spinY {
0% { transform: perspective(128px) rotateY(0deg); }
100% { transform: perspective(128px) rotateY(360deg); }
}
@keyframes mfm-jump {
0% { transform: translateY(0); }
25% { transform: translateY(-16px); }
50% { transform: translateY(0); }
75% { transform: translateY(-8px); }
100% { transform: translateY(0); }
}
@keyframes mfm-bounce {
0% { transform: translateY(0) scale(1, 1); }
25% { transform: translateY(-16px) scale(1, 1); }
50% { transform: translateY(0) scale(1, 1); }
75% { transform: translateY(0) scale(1.5, 0.75); }
100% { transform: translateY(0) scale(1, 1); }
}
// const val = () => `translate(${Math.floor(Math.random() * 20) - 10}px, ${Math.floor(Math.random() * 20) - 10}px)`;
// let css = '';
// for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
@keyframes mfm-twitch {
0% { transform: translate(7px, -2px) }
5% { transform: translate(-3px, 1px) }
10% { transform: translate(-7px, -1px) }
15% { transform: translate(0px, -1px) }
20% { transform: translate(-8px, 6px) }
25% { transform: translate(-4px, -3px) }
30% { transform: translate(-4px, -6px) }
35% { transform: translate(-8px, -8px) }
40% { transform: translate(4px, 6px) }
45% { transform: translate(-3px, 1px) }
50% { transform: translate(2px, -10px) }
55% { transform: translate(-7px, 0px) }
60% { transform: translate(-2px, 4px) }
65% { transform: translate(3px, -8px) }
70% { transform: translate(6px, 7px) }
75% { transform: translate(-7px, -2px) }
80% { transform: translate(-7px, -8px) }
85% { transform: translate(9px, 3px) }
90% { transform: translate(-3px, -2px) }
95% { transform: translate(-10px, 2px) }
100% { transform: translate(-2px, -6px) }
}
// const val = () => `translate(${Math.floor(Math.random() * 6) - 3}px, ${Math.floor(Math.random() * 6) - 3}px) rotate(${Math.floor(Math.random() * 24) - 12}deg)`;
// let css = '';
// for (let i = 0; i <= 100; i += 5) { css += `${i}% { transform: ${val()} }\n`; }
@keyframes mfm-shake {
0% { transform: translate(-3px, -1px) rotate(-8deg) }
5% { transform: translate(0px, -1px) rotate(-10deg) }
10% { transform: translate(1px, -3px) rotate(0deg) }
15% { transform: translate(1px, 1px) rotate(11deg) }
20% { transform: translate(-2px, 1px) rotate(1deg) }
25% { transform: translate(-1px, -2px) rotate(-2deg) }
30% { transform: translate(-1px, 2px) rotate(-3deg) }
35% { transform: translate(2px, 1px) rotate(6deg) }
40% { transform: translate(-2px, -3px) rotate(-9deg) }
45% { transform: translate(0px, -1px) rotate(-12deg) }
50% { transform: translate(1px, 2px) rotate(10deg) }
55% { transform: translate(0px, -3px) rotate(8deg) }
60% { transform: translate(1px, -1px) rotate(8deg) }
65% { transform: translate(0px, -1px) rotate(-7deg) }
70% { transform: translate(-1px, -3px) rotate(6deg) }
75% { transform: translate(0px, -2px) rotate(4deg) }
80% { transform: translate(-2px, -1px) rotate(3deg) }
85% { transform: translate(1px, -3px) rotate(-10deg) }
90% { transform: translate(1px, 0px) rotate(3deg) }
95% { transform: translate(-2px, 0px) rotate(-3deg) }
100% { transform: translate(2px, 1px) rotate(2deg) }
}
@keyframes mfm-rubberBand {
from { transform: scale3d(1, 1, 1); }
30% { transform: scale3d(1.25, 0.75, 1); }
40% { transform: scale3d(0.75, 1.25, 1); }
50% { transform: scale3d(1.15, 0.85, 1); }
65% { transform: scale3d(0.95, 1.05, 1); }
75% { transform: scale3d(1.05, 0.95, 1); }
to { transform: scale3d(1, 1, 1); }
}
</style>
<style lang="scss" scoped>
.havbbuyv {
white-space: pre-wrap;
@@ -38,12 +135,9 @@ export default defineComponent({
}
> ::v-deep(code) {
font-size: 0.8em;
word-break: break-all;
}
::v-deep(.title) {
text-align: center;
border-bottom: solid 1px var(--divider);
padding: 4px 6px;
}
}
</style>

View File

@@ -1,17 +1,17 @@
<template>
<header class="kkwtjztg">
<router-link class="name" :to="userPage(note.user)" v-user-preview="note.user.id">
<MkA class="name" :to="userPage(note.user)" v-user-preview="note.user.id">
<MkUserName :user="note.user"/>
</router-link>
</MkA>
<span class="is-bot" v-if="note.user.isBot">bot</span>
<span class="username"><MkAcct :user="note.user"/></span>
<span class="admin" v-if="note.user.isAdmin"><Fa :icon="faBookmark"/></span>
<span class="moderator" v-if="!note.user.isAdmin && note.user.isModerator"><Fa :icon="farBookmark"/></span>
<div class="info">
<span class="mobile" v-if="note.viaMobile"><Fa :icon="faMobileAlt"/></span>
<router-link class="created-at" :to="notePage(note)">
<MkA class="created-at" :to="notePage(note)">
<MkTime :time="note.createdAt"/>
</router-link>
</MkA>
<span class="visibility" v-if="note.visibility !== 'public'">
<Fa v-if="note.visibility === 'home'" :icon="faHome"/>
<Fa v-if="note.visibility === 'followers'" :icon="faUnlock"/>

View File

@@ -18,9 +18,9 @@
<Fa :icon="faRetweet"/>
<i18n-t keypath="renotedBy" tag="span">
<template #user>
<router-link class="name" :to="userPage(note.user)" v-user-preview="note.userId">
<MkA class="name" :to="userPage(note.user)" v-user-preview="note.userId">
<MkUserName :user="note.user"/>
</router-link>
</MkA>
</template>
</i18n-t>
<div class="info">
@@ -40,7 +40,8 @@
<MkAvatar class="avatar" :user="appearNote.user"/>
<div class="main">
<XNoteHeader class="header" :note="appearNote" :mini="true"/>
<div class="body" ref="noteBody">
<MkInstanceTicker v-if="showTicker" class="ticker" :instance="appearNote.user.instance"/>
<div class="body">
<p v-if="appearNote.cw != null" class="cw">
<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$store.state.i" :custom-emojis="appearNote.emojis"/>
<XCwButton v-model:value="showContent" :note="appearNote"/>
@@ -48,18 +49,18 @@
<div class="content" v-show="appearNote.cw == null || showContent">
<div class="text">
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $t('private') }})</span>
<router-link class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><Fa :icon="faReply"/></router-link>
<MkA class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><Fa :icon="faReply"/></MkA>
<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$store.state.i" :custom-emojis="appearNote.emojis"/>
<a class="rp" v-if="appearNote.renote != null">RN:</a>
</div>
<div class="files" v-if="appearNote.files.length > 0">
<XMediaList :media-list="appearNote.files" :parent-element="noteBody"/>
<XMediaList :media-list="appearNote.files"/>
</div>
<XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/>
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="detail" class="url-preview"/>
<div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div>
</div>
<router-link v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><Fa :icon="faSatelliteDish"/> {{ appearNote.channel.name }}</router-link>
<MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><Fa :icon="faSatelliteDish"/> {{ appearNote.channel.name }}</MkA>
</div>
<footer class="footer">
<XReactionsViewer :note="appearNote" ref="reactionsViewer"/>
@@ -91,9 +92,9 @@
<div v-else class="_panel muted" @click="muted = false">
<i18n-t keypath="userSaysSomething" tag="small">
<template #name>
<router-link class="name" :to="userPage(appearNote.user)" v-user-preview="appearNote.userId">
<MkA class="name" :to="userPage(appearNote.user)" v-user-preview="appearNote.userId">
<MkUserName :user="appearNote.user"/>
</router-link>
</MkA>
</template>
</i18n-t>
</div>
@@ -101,7 +102,7 @@
<script lang="ts">
import { computed, defineAsyncComponent, defineComponent, markRaw, ref } from 'vue';
import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug } from '@fortawesome/free-solid-svg-icons';
import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug, faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
import { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { parse } from '../../mfm/parse';
import { sum, unique } from '../../prelude/array';
@@ -139,12 +140,13 @@ export default defineComponent({
XCwButton,
XPoll,
MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
MkInstanceTicker: defineAsyncComponent(() => import('@/components/instance-ticker.vue')),
},
inject: {
inChannel: {
default: null
}
},
},
props: {
@@ -174,7 +176,6 @@ export default defineComponent({
showContent: false,
isDeleted: false,
muted: false,
noteBody: this.$refs.noteBody,
faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faBiohazard, faPlug, faSatelliteDish
};
},
@@ -258,6 +259,12 @@ export default defineComponent({
} else {
return null;
}
},
showTicker() {
if (this.$store.state.device.instanceTicker === 'always') return true;
if (this.$store.state.device.instanceTicker === 'remote' && this.appearNote.user.instance) return true;
return false;
}
},
@@ -301,8 +308,6 @@ export default defineComponent({
if (this.$store.getters.isSignedIn) {
this.connection.on('_connected_', this.onStreamConnected);
}
this.noteBody = this.$refs.noteBody;
},
beforeUnmount() {
@@ -493,9 +498,9 @@ export default defineComponent({
react(viaKeyboard = false) {
pleaseLogin();
this.blur();
os.popup(defineAsyncComponent(() => import('@/components/reaction-picker.vue')), {
showFocus: viaKeyboard,
os.popup(import('@/components/emoji-picker.vue'), {
src: this.$refs.reactButton,
compact: !this.$store.state.device.useFullReactionPicker
}, {
done: reaction => {
if (reaction) {
@@ -581,11 +586,6 @@ export default defineComponent({
});
menu = [{
type: 'link',
icon: faInfoCircle,
text: this.$t('details'),
to: '/notes/' + this.appearNote.id
}, null, {
icon: faCopy,
text: this.$t('copyContent'),
action: this.copyContent
@@ -637,6 +637,21 @@ export default defineComponent({
}]
: []
),
...(this.appearNote.userId != this.$store.state.i.id ? [
null,
{
icon: faExclamationCircle,
text: this.$t('reportAbuse'),
action: () => {
const u = `${url}/notes/${this.appearNote.id}`;
os.popup(import('@/components/abuse-report-window.vue'), {
user: this.appearNote.user,
initialComment: `Note: ${u}\n-----\n`
}, {}, 'closed');
}
}]
: []
),
...(this.appearNote.userId == this.$store.state.i.id || this.$store.state.i.isModerator || this.$store.state.i.isAdmin ? [
null,
this.appearNote.userId == this.$store.state.i.id ? {
@@ -708,6 +723,7 @@ export default defineComponent({
os.modalMenu([{
text: this.$t('unrenote'),
icon: faTrashAlt,
danger: true,
action: () => {
os.api('notes/delete', {
noteId: this.note.id

View File

@@ -18,34 +18,34 @@
</div>
<div class="tail">
<header>
<router-link v-if="notification.user" class="name" :to="userPage(notification.user)" v-user-preview="notification.user.id"><MkUserName :user="notification.user"/></router-link>
<MkA v-if="notification.user" class="name" :to="userPage(notification.user)" v-user-preview="notification.user.id"><MkUserName :user="notification.user"/></MkA>
<span v-else>{{ notification.header }}</span>
<MkTime :time="notification.createdAt" v-if="withTime"/>
<MkTime :time="notification.createdAt" v-if="withTime" class="time"/>
</header>
<router-link v-if="notification.type === 'reaction'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<MkA v-if="notification.type === 'reaction'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Fa :icon="faQuoteLeft"/>
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
<Fa :icon="faQuoteRight"/>
</router-link>
<router-link v-if="notification.type === 'renote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)">
</MkA>
<MkA v-if="notification.type === 'renote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)">
<Fa :icon="faQuoteLeft"/>
<Mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.renote.emojis"/>
<Fa :icon="faQuoteRight"/>
</router-link>
<router-link v-if="notification.type === 'reply'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
</MkA>
<MkA v-if="notification.type === 'reply'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
</router-link>
<router-link v-if="notification.type === 'mention'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
</MkA>
<MkA v-if="notification.type === 'mention'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
</router-link>
<router-link v-if="notification.type === 'quote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
</MkA>
<MkA v-if="notification.type === 'quote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
</router-link>
<router-link v-if="notification.type === 'pollVote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
</MkA>
<MkA v-if="notification.type === 'pollVote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Fa :icon="faQuoteLeft"/>
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
<Fa :icon="faQuoteRight"/>
</router-link>
</MkA>
<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ $t('youGotNewFollower') }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span>
<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ $t('followRequestAccepted') }}</span>
<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ $t('receiveFollowRequest') }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ $t('accept') }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ $t('reject') }}</button></div></span>
@@ -260,7 +260,7 @@ export default defineComponent({
overflow: hidden;
}
> .mk-time {
> .time {
margin-left: auto;
font-size: 0.9em;
}

View File

@@ -1,5 +1,5 @@
<template>
<router-link :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1">
<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1">
<div class="thumbnail" v-if="page.eyeCatchingImage" :style="`background-image: url('${page.eyeCatchingImage.thumbnailUrl}')`"></div>
<article>
<header>
@@ -11,7 +11,7 @@
<p>{{ userName(page.user) }}</p>
</footer>
</article>
</router-link>
</MkA>
</template>
<script lang="ts">

View File

@@ -1,24 +1,34 @@
<template>
<XWindow ref="window" :initial-width="400" :initial-height="450" :can-resize="true" @closed="$emit('closed')">
<XWindow ref="window"
:initial-width="700"
:initial-height="500"
:can-resize="true"
:close-right="true"
:contextmenu="contextmenu"
@closed="$emit('closed')"
>
<template #header>
<XHeader :info="pageInfo" :with-back="false"/>
</template>
<template #buttons>
<button class="_button" @click="expand" v-tooltip="$t('showInPage')"><Fa :icon="faExpandAlt"/></button>
<button class="_button" @click="popout" v-tooltip="$t('popout')"><Fa :icon="faExternalLinkAlt"/></button>
<button class="_button" @click="back()" v-if="history.length > 0"><Fa :icon="faChevronLeft"/></button>
<button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button>
</template>
<div style="min-height: 100%; background: var(--bg);">
<div class="yrolvcoq" style="min-height: 100%; background: var(--bg);">
<component :is="component" v-bind="props" :ref="changePage"/>
</div>
</XWindow>
</template>
<script lang="ts">
import { defineComponent, markRaw } from 'vue';
import { faExternalLinkAlt, faExpandAlt } from '@fortawesome/free-solid-svg-icons';
import { defineComponent } from 'vue';
import { faExternalLinkAlt, faExpandAlt, faLink, faChevronLeft, faColumns } from '@fortawesome/free-solid-svg-icons';
import XWindow from '@/components/ui/window.vue';
import XHeader from '@/ui/_common_/header.vue';
import { popout } from '@/scripts/popout';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import { resolve } from '@/router';
import { url } from '@/config';
export default defineComponent({
components: {
@@ -26,8 +36,22 @@ export default defineComponent({
XHeader,
},
inject: {
sideViewHook: {
default: null
}
},
provide() {
return {
navHook: (path) => {
this.navigate(path);
}
};
},
props: {
initialUrl: {
initialPath: {
type: String,
required: true,
},
@@ -38,7 +62,7 @@ export default defineComponent({
initialProps: {
type: Object,
required: false,
default: {},
default: () => {},
},
},
@@ -47,21 +71,53 @@ export default defineComponent({
data() {
return {
pageInfo: null,
url: this.initialUrl,
path: this.initialPath,
component: this.initialComponent,
props: this.initialProps,
faExternalLinkAlt, faExpandAlt,
history: [],
faChevronLeft,
};
},
provide() {
return {
navHook: (url, component, props) => {
this.url = url;
this.component = markRaw(component);
this.props = props;
}
};
computed: {
url(): string {
return url + this.path;
},
contextmenu() {
return [{
type: 'label',
text: this.path,
}, {
icon: faExpandAlt,
text: this.$t('showInPage'),
action: this.expand
}, this.sideViewHook ? {
icon: faColumns,
text: this.$t('openInSideView'),
action: () => {
this.sideViewHook(this.path);
this.$refs.window.close();
}
} : undefined, {
icon: faExternalLinkAlt,
text: this.$t('popout'),
action: this.popout
}, null, {
icon: faExternalLinkAlt,
text: this.$t('openInNewTab'),
action: () => {
window.open(this.url, '_blank');
this.$refs.window.close();
}
}, {
icon: faLink,
text: this.$t('copyLink'),
action: () => {
copyToClipboard(this.url);
}
}];
},
},
methods: {
@@ -72,15 +128,33 @@ export default defineComponent({
}
},
navigate(path, record = true) {
if (record) this.history.push(this.path);
this.path = path;
const { component, props } = resolve(path);
this.component = component;
this.props = props;
},
back() {
this.navigate(this.history.pop(), false);
},
expand() {
this.$router.push(this.url);
this.$router.push(this.path);
this.$refs.window.close();
},
popout() {
popout(this.url, this.$el);
popout(this.path, this.$el);
this.$refs.window.close();
},
},
});
</script>
<style lang="scss" scoped>
.yrolvcoq {
--section-padding: 16px;
}
</style>

View File

@@ -5,10 +5,12 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import * as os from '@/os';
import { defineComponent, defineAsyncComponent } from 'vue';
export default defineComponent({
components: {
XBlock: defineAsyncComponent(() => import('./page.block.vue'))
},
props: {
value: {
required: true
@@ -23,8 +25,5 @@ export default defineComponent({
required: true
}
},
beforeCreate() {
this.$options.components.XBlock = require('./page.block.vue').default;
},
});
</script>

View File

@@ -44,14 +44,7 @@ export default defineComponent({
},
methods: {
upload() {
return new Promise((ok) => {
const dialog = os.dialog({
type: 'waiting',
text: this.$t('uploading') + '...',
showOkButton: false,
showCancelButton: false,
cancelableByBgClick: false
});
const promise = new Promise((ok) => {
const canvas = this.hpml.canvases[this.value.canvasId];
canvas.toBlob(blob => {
const data = new FormData();
@@ -67,11 +60,12 @@ export default defineComponent({
})
.then(response => response.json())
.then(f => {
dialog.close();
ok(f);
})
});
});
os.promiseDialog(promise);
return promise;
},
async post() {
this.posting = true;

View File

@@ -9,10 +9,13 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, defineAsyncComponent } from 'vue';
import * as os from '@/os';
export default defineComponent({
components: {
XBlock: defineAsyncComponent(() => import('./page.block.vue'))
},
props: {
value: {
required: true
@@ -27,9 +30,6 @@ export default defineComponent({
required: true
}
},
beforeCreate() {
this.$options.components.XBlock = require('./page.block.vue').default;
},
});
</script>

View File

@@ -5,7 +5,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import MkTextarea from '../ui/textarea.vue';
import * as os from '@/os';
export default defineComponent({
components: {

View File

@@ -12,7 +12,6 @@ import { faHeart } from '@fortawesome/free-regular-svg-icons';
import XBlock from './page.block.vue';
import { Hpml } from '@/scripts/hpml/evaluator';
import { url } from '@/config';
import * as os from '@/os';
export default defineComponent({
components: {

View File

@@ -57,7 +57,6 @@ import MkInput from './ui/input.vue';
import MkSelect from './ui/select.vue';
import MkSwitch from './ui/switch.vue';
import MkButton from './ui/button.vue';
import * as os from '@/os';
export default defineComponent({
components: {
@@ -78,8 +77,8 @@ export default defineComponent({
data() {
return {
choices: ['', ''],
multiple: false,
choices: this.poll.choices,
multiple: this.poll.multiple,
expiration: 'infinite',
atDate: formatDateTimeString(addTime(new Date(), 1, 'day'), 'yyyy-MM-dd'),
atTime: '00:00',
@@ -90,26 +89,6 @@ export default defineComponent({
},
watch: {
poll: {
handler(poll) {
if (poll == null) return;
if (poll.choices.length == 0) return;
this.choices = poll.choices;
if (poll.choices.length == 1) this.choices = this.choices.concat('');
this.multiple = poll.multiple;
if (poll.expiresAt) {
this.expiration = 'at';
this.atDate = this.atTime = poll.expiresAt;
} else if (typeof poll.expiredAfter === 'number') {
this.expiration = 'after';
this.after = poll.expiredAfter;
} else {
this.expiration = 'infinite';
}
},
deep: true,
immediate: true
},
choices: {
handler() {
this.$emit('updated', this.get());
@@ -136,6 +115,24 @@ export default defineComponent({
this.$emit('updated', this.get());
},
},
unit: {
handler() {
this.$emit('updated', this.get());
},
},
},
created() {
const poll = this.poll;
if (poll.expiresAt) {
this.expiration = 'at';
this.atDate = this.atTime = poll.expiresAt;
} else if (typeof poll.expiredAfter === 'number') {
this.expiration = 'after';
this.after = poll.expiredAfter / 1000;
} else {
this.expiration = 'infinite';
}
},
methods: {

View File

@@ -1,6 +1,6 @@
<template>
<MkModal ref="modal" @click="$refs.modal.close()" @closed="$emit('closed')" :position="'top'">
<MkPostForm @done="$refs.modal.close()" @esc="$refs.modal.close()" v-bind="$attrs"/>
<MkPostForm @posted="$refs.modal.close()" @cancel="$refs.modal.close()" @esc="$refs.modal.close()" v-bind="$attrs"/>
</MkModal>
</template>

View File

@@ -125,7 +125,7 @@ export default defineComponent({
},
},
emits: ['posted', 'done', 'esc'],
emits: ['posted', 'cancel', 'esc'],
data() {
return {
@@ -135,8 +135,8 @@ export default defineComponent({
poll: null,
useCw: false,
cw: null,
localOnly: false,
visibility: 'public',
localOnly: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.localOnly : this.$store.state.settings.defaultNoteLocalOnly,
visibility: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.visibility : this.$store.state.settings.defaultNoteVisibility,
visibleUsers: [],
autocomplete: null,
draghover: false,
@@ -202,12 +202,6 @@ export default defineComponent({
}
},
watch: {
localOnly() {
this.$store.commit('deviceUser/setLocalOnly', this.localOnly);
}
},
mounted() {
if (this.initialText) {
this.text = this.initialText;
@@ -239,11 +233,9 @@ export default defineComponent({
}
}
// デフォルト公開範囲
if (this.channel == null) {
this.applyVisibility(this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.visibility : this.$store.state.settings.defaultNoteVisibility);
this.localOnly = this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.localOnly : this.$store.state.settings.defaultNoteLocalOnly;
if (this.channel) {
this.visibility = 'public';
this.localOnly = true; // TODO: チャンネルが連合するようになった折には消す
}
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
@@ -295,7 +287,7 @@ export default defineComponent({
this.text = draft.data.text;
this.useCw = draft.data.useCw;
this.cw = draft.data.cw;
this.applyVisibility(draft.data.visibility);
this.visibility = draft.data.visibility;
this.localOnly = draft.data.localOnly;
this.files = (draft.data.files || []).filter(e => e);
if (draft.data.poll) {
@@ -386,30 +378,32 @@ export default defineComponent({
this.saveDraft();
},
async setVisibility() {
setVisibility() {
if (this.channel) {
// TODO: information dialog
return;
}
os.popup(await import('./visibility-picker.vue'), {
os.popup(import('./visibility-picker.vue'), {
currentVisibility: this.visibility,
currentLocalOnly: this.localOnly,
src: this.$refs.visibilityButton
}, {
changeVisibility: visibility => {
this.applyVisibility(visibility);
this.visibility = visibility;
if (this.$store.state.settings.rememberNoteVisibility) {
this.$store.commit('deviceUser/setVisibility', visibility);
}
},
changeLocalOnly: localOnly => {
this.localOnly = localOnly;
if (this.$store.state.settings.rememberNoteVisibility) {
this.$store.commit('deviceUser/setLocalOnly', localOnly);
}
}
}, 'closed');
},
applyVisibility(v: string) {
this.visibility = (noteVisibilities as unknown as string[]).includes(v) ? v : 'public'; // v11互換性のため
},
addVisibleUser() {
os.selectUser().then(user => {
this.visibleUsers.push(user);
@@ -556,23 +550,27 @@ export default defineComponent({
this.posting = true;
os.api('notes/create', data).then(() => {
this.clear();
this.deleteDraft();
this.$emit('posted');
this.$nextTick(() => {
this.deleteDraft();
this.$emit('posted');
if (this.text && this.text != '') {
const hashtags = parse(this.text).filter(x => x.node.type === 'hashtag').map(x => x.node.props.hashtag);
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags.concat(history))));
}
this.posting = false;
});
}).catch(err => {
}).then(() => {
this.posting = false;
this.$emit('done');
os.dialog({
type: 'error',
text: err.message + '\n' + (err as any).id,
});
});
if (this.text && this.text != '') {
const hashtags = parse(this.text).filter(x => x.node.type === 'hashtag').map(x => x.node.props.hashtag);
const history = JSON.parse(localStorage.getItem('hashtags') || '[]') as string[];
localStorage.setItem('hashtags', JSON.stringify(unique(hashtags.concat(history))));
}
},
cancel() {
this.$emit('done');
this.$emit('cancel');
},
insertMention() {

View File

@@ -1,9 +1,9 @@
<template>
<MkEmoji :emoji="reaction.startsWith(':') ? null : reaction" :name="reaction.startsWith(':') ? reaction.substr(1, reaction.length - 2) : null" :customEmojis="customEmojis" :is-reaction="true" :normal="true" :no-style="noStyle"/>
<MkEmoji :emoji="reaction" :custom-emojis="customEmojis" :is-reaction="true" :normal="true" :no-style="noStyle"/>
</template>
<script lang="ts">
import { defineComponent } from 'vue';import * as os from '@/os';
import { defineComponent } from 'vue';
export default defineComponent({
props: {

View File

@@ -1,214 +0,0 @@
<template>
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
<div class="rdfaahpb _popup" v-hotkey="keymap">
<div class="buttons" ref="buttons" :class="{ showFocus }">
<button class="_button" v-for="(reaction, i) in rs" :key="reaction" @click="react(reaction)" :tabindex="i + 1" :title="reaction" v-particle><XReactionIcon :reaction="reaction"/></button>
</div>
<input class="text" ref="text" v-model.trim="text" :placeholder="$t('enterEmoji')" @keyup.enter="reactText" @input="tryReactText">
</div>
</MkModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { emojiRegex } from '../../misc/emoji-regex';
import XReactionIcon from '@/components/reaction-icon.vue';
import MkModal from '@/components/ui/modal.vue';
import { Autocomplete } from '@/scripts/autocomplete';
export default defineComponent({
components: {
XReactionIcon,
MkModal,
},
props: {
reactions: {
required: false
},
showFocus: {
type: Boolean,
required: false,
default: false
},
src: {
required: false
},
},
emits: ['done', 'closed'],
data() {
return {
rs: this.reactions || this.$store.state.settings.reactions,
text: null,
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(this.rs[0]),
'2': () => this.react(this.rs[1]),
'3': () => this.react(this.rs[2]),
'4': () => this.react(this.rs[3]),
'5': () => this.react(this.rs[4]),
'6': () => this.react(this.rs[5]),
'7': () => this.react(this.rs[6]),
'8': () => this.react(this.rs[7]),
'9': () => this.react(this.rs[8]),
'0': () => this.react(this.rs[9]),
};
},
},
watch: {
focus(i) {
this.$refs.buttons.children[i].focus({
preventScroll: true
});
}
},
mounted() {
this.$nextTick(() => {
this.focus = 0;
});
// TODO: detach when unmount
new Autocomplete(this.$refs.text, this, { model: 'text' });
},
methods: {
close() {
this.$emit('done');
this.$refs.modal.close();
},
react(reaction) {
this.$emit('done', reaction);
this.$refs.modal.close();
},
reactText() {
if (!this.text) return;
this.react(this.text);
},
tryReactText() {
if (!this.text) return;
if (!this.text.match(emojiRegex)) return;
this.reactText();
},
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.children[this.focus].click();
},
}
});
</script>
<style lang="scss" scoped>
.rdfaahpb {
> .buttons {
padding: 6px 6px 0 6px;
width: 212px;
box-sizing: border-box;
text-align: center;
@media (max-width: 1025px) {
padding: 8px 8px 0 8px;
width: 256px;
}
&.showFocus {
> button:focus {
position: relative;
z-index: 1;
&:after {
content: "";
pointer-events: none;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border: 2px solid var(--focus);
border-radius: 4px;
}
}
}
> button {
padding: 0;
width: 40px;
height: 40px;
font-size: 24px;
border-radius: 2px;
@media (max-width: 1025px) {
width: 48px;
height: 48px;
font-size: 26px;
}
> * {
height: 1em;
}
&:hover {
background: rgba(0, 0, 0, 0.05);
}
&:active {
background: var(--accent);
box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
}
}
}
> .text {
width: 208px;
padding: 8px;
margin: 0 0 6px 0;
box-sizing: border-box;
text-align: center;
font-size: 16px;
outline: none;
border: none;
background: transparent;
color: var(--fg);
@media (max-width: 1025px) {
width: 256px;
margin: 4px 0 8px 0;
}
}
}
</style>

View File

@@ -3,7 +3,7 @@
<div class="bqxuuuey">
<div class="info">
<div>{{ reaction.replace('@.', '') }}</div>
<XReactionIcon :reaction="reaction" :custom-emojis="emojis" class="icon"/>
<XReactionIcon :reaction="reaction" :custom-emojis="emojis" class="icon" :no-style="true"/>
</div>
<template v-if="users.length <= 10">
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
@@ -66,7 +66,6 @@ export default defineComponent({
> .icon {
display: block;
width: 60px;
height: 60px;
margin: 0 auto;
}
}

View File

@@ -11,33 +11,30 @@
<transition name="nav">
<nav class="nav" :class="{ iconOnly, hidden }" v-show="showing">
<div>
<button class="item _button account" @click="openAccountMenu" v-if="$store.getters.isSignedIn">
<button class="item _button account" @click="openAccountMenu">
<MkAvatar :user="$store.state.i" class="avatar"/><MkAcct class="text" :user="$store.state.i"/>
</button>
<button class="item _button index active" @click="top()" v-if="$route.name === 'index'">
<Fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
</button>
<router-link class="item index" active-class="active" to="/" exact v-else>
<Fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
</router-link>
<MkA class="item index" active-class="active" to="/" exact>
<Fa :icon="faHome" fixed-width/><span class="text">{{ $t('timeline') }}</span>
</MkA>
<template v-for="item in menu">
<div v-if="item === '-'" class="divider"></div>
<component v-else-if="menuDef[item] && (menuDef[item].show !== false)" :is="menuDef[item].to ? 'router-link' : 'button'" class="item _button" :class="item" active-class="active" v-on="menuDef[item].action ? { click: menuDef[item].action } : {}" :to="menuDef[item].to">
<component v-else-if="menuDef[item] && (menuDef[item].show !== false)" :is="menuDef[item].to ? 'MkA' : 'button'" class="item _button" :class="item" active-class="active" v-on="menuDef[item].action ? { click: menuDef[item].action } : {}" :to="menuDef[item].to">
<Fa :icon="menuDef[item].icon" fixed-width/><span class="text">{{ $t(menuDef[item].title) }}</span>
<i v-if="menuDef[item].indicated"><Fa :icon="faCircle"/></i>
</component>
</template>
<div class="divider"></div>
<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$store.getters.isSignedIn && ($store.state.i.isAdmin || $store.state.i.isModerator)" @click="oepnInstanceMenu">
<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$store.state.i.isAdmin || $store.state.i.isModerator" @click="oepnInstanceMenu">
<Fa :icon="faServer" fixed-width/><span class="text">{{ $t('instance') }}</span>
</button>
<button class="item _button" @click="more">
<Fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $t('more') }}</span>
<i v-if="otherNavItemIndicated"><Fa :icon="faCircle"/></i>
</button>
<router-link class="item" active-class="active" to="/settings">
<MkA class="item" active-class="active" to="/settings">
<Fa :icon="faCog" fixed-width/><span class="text">{{ $t('settings') }}</span>
</router-link>
</MkA>
</div>
</nav>
</transition>
@@ -46,7 +43,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { faGripVertical, faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer, faInfoCircle, faQuestionCircle, faProjectDiagram, faStream } from '@fortawesome/free-solid-svg-icons';
import { faGripVertical, faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer, faInfoCircle, faQuestionCircle, faProjectDiagram, faStream, faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
import { faBell, faEnvelope, faLaugh, faComments } from '@fortawesome/free-regular-svg-icons';
import { host, instanceName } from '@/config';
import { search } from '@/scripts/search';
@@ -74,7 +71,6 @@ export default defineComponent({
},
otherNavItemIndicated(): boolean {
if (!this.$store.getters.isSignedIn) return false;
for (const def in this.menuDef) {
if (this.menu.includes(def)) continue;
if (this.menuDef[def].indicated) return true;
@@ -120,10 +116,6 @@ export default defineComponent({
this.showing = true;
},
top() {
window.scroll({ top: 0, behavior: 'smooth' });
},
search() {
if (this.searching) return;
@@ -217,6 +209,11 @@ export default defineComponent({
text: this.$t('announcements'),
to: '/instance/announcements',
icon: faBroadcastTower,
}, {
type: 'link',
text: this.$t('abuseReports'),
to: '/instance/abuses',
icon: faExclamationCircle,
}, {
type: 'link',
text: this.$t('logs'),
@@ -241,7 +238,7 @@ export default defineComponent({
icon: faQuestionCircle,
}, {
type: 'link',
text: this.$t('aboutX', { x: instanceName || host }),
text: this.$t('aboutX', { x: instanceName }),
to: '/about',
icon: faInfoCircle,
}, {
@@ -252,8 +249,8 @@ export default defineComponent({
}], ev.currentTarget || ev.target);
},
async addAcount() {
os.popup(await import('./signin-dialog.vue'), {}, {
addAcount() {
os.popup(import('./signin-dialog.vue'), {}, {
done: res => {
this.$store.dispatch('addAcount', res);
os.success();
@@ -261,8 +258,8 @@ export default defineComponent({
}, 'closed');
},
async createAccount() {
os.popup(await import('./signup-dialog.vue'), {}, {
createAccount() {
os.popup(import('./signup-dialog.vue'), {}, {
done: res => {
this.$store.dispatch('addAcount', res);
this.switchAccountWithToken(res.i);
@@ -270,7 +267,7 @@ export default defineComponent({
}, 'closed');
},
async switchAccount(account: any) {
switchAccount(account: any) {
const token = this.$store.state.device.accounts.find((x: any) => x.id === account.id).token;
this.switchAccountWithToken(token);
},
@@ -417,9 +414,9 @@ export default defineComponent({
> .item {
position: relative;
display: block;
padding-left: 32px;
padding-left: 24px;
font-size: $ui-font-size;
line-height: 3.2rem;
line-height: 3rem;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;

View File

@@ -7,9 +7,7 @@
>
<template #header>{{ $t('login') }}</template>
<div class="_section">
<MkSignin :auto-set="autoSet" @login="onLogin"/>
</div>
<MkSignin :auto-set="autoSet" @login="onLogin"/>
</XModalWindow>
</template>

View File

@@ -1,43 +1,47 @@
<template>
<form class="eppvobhk" :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
<div class="normal-signin" v-if="!totpLogin">
<MkInput v-model:value="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:value="onUsernameChange">
<span>{{ $t('username') }}</span>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</MkInput>
<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required>
<span>{{ $t('password') }}</span>
<template #prefix><Fa :icon="faLock"/></template>
</MkInput>
<MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
<a class="_panelButton" style="margin: 8px auto;" v-if="meta && meta.enableTwitterIntegration" :href="`${apiUrl}/signin/twitter`"><Fa :icon="faTwitter" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Twitter' }) }}</a>
<a class="_panelButton" style="margin: 8px auto;" v-if="meta && meta.enableGithubIntegration" :href="`${apiUrl}/signin/github`"><Fa :icon="faGithub" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'GitHub' }) }}</a>
<a class="_panelButton" style="margin: 8px auto;" v-if="meta && meta.enableDiscordIntegration" :href="`${apiUrl}/signin/discord`"><Fa :icon="faDiscord" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Discord' }) }}</a>
</div>
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
<p>{{ $t('tapSecurityKey') }}</p>
<MkButton @click="queryKey" v-if="!queryingKey">
{{ $t('retry') }}
</MkButton>
</div>
<div class="or-hr" v-if="user && user.securityKeys">
<p class="or-msg">{{ $t('or') }}</p>
</div>
<div class="twofa-group totp-group">
<p style="margin-bottom:0;">{{ $t('twoStepAuthentication') }}</p>
<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="user && user.usePasswordLessLogin" required>
<div class="auth _section">
<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
<div class="normal-signin" v-if="!totpLogin">
<MkInput v-model:value="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:value="onUsernameChange">
<span>{{ $t('username') }}</span>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</MkInput>
<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required>
<span>{{ $t('password') }}</span>
<template #prefix><Fa :icon="faLock"/></template>
</MkInput>
<MkInput v-model:value="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
<span>{{ $t('token') }}</span>
<template #prefix><Fa :icon="faGavel"/></template>
</MkInput>
<MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
<MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
</div>
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
<p>{{ $t('tapSecurityKey') }}</p>
<MkButton @click="queryKey" v-if="!queryingKey">
{{ $t('retry') }}
</MkButton>
</div>
<div class="or-hr" v-if="user && user.securityKeys">
<p class="or-msg">{{ $t('or') }}</p>
</div>
<div class="twofa-group totp-group">
<p style="margin-bottom:0;">{{ $t('twoStepAuthentication') }}</p>
<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="user && user.usePasswordLessLogin" required>
<span>{{ $t('password') }}</span>
<template #prefix><Fa :icon="faLock"/></template>
</MkInput>
<MkInput v-model:value="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
<span>{{ $t('token') }}</span>
<template #prefix><Fa :icon="faGavel"/></template>
</MkInput>
<MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
</div>
</div>
</div>
<div class="social _section">
<a class="_borderButton _vMargin" v-if="meta && meta.enableTwitterIntegration" :href="`${apiUrl}/signin/twitter`"><Fa :icon="faTwitter" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Twitter' }) }}</a>
<a class="_borderButton _vMargin" v-if="meta && meta.enableGithubIntegration" :href="`${apiUrl}/signin/github`"><Fa :icon="faGithub" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'GitHub' }) }}</a>
<a class="_borderButton _vMargin" v-if="meta && meta.enableDiscordIntegration" :href="`${apiUrl}/signin/discord`"><Fa :icon="faDiscord" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Discord' }) }}</a>
</div>
</form>
</template>
@@ -203,14 +207,16 @@ export default defineComponent({
<style lang="scss" scoped>
.eppvobhk {
> .avatar {
margin: 0 auto 0 auto;
width: 64px;
height: 64px;
background: #ddd;
background-position: center;
background-size: cover;
border-radius: 100%;
> .auth {
> .avatar {
margin: 0 auto 0 auto;
width: 64px;
height: 64px;
background: #ddd;
background-position: center;
background-size: cover;
border-radius: 100%;
}
}
}
</style>

View File

@@ -3,9 +3,9 @@
<div class="body">
<span v-if="note.isHidden" style="opacity: 0.5">({{ $t('private') }})</span>
<span v-if="note.deletedAt" style="opacity: 0.5">({{ $t('deleted') }})</span>
<router-link class="reply" v-if="note.replyId" :to="`/notes/${note.replyId}`"><Fa :icon="faReply"/></router-link>
<MkA class="reply" v-if="note.replyId" :to="`/notes/${note.replyId}`"><Fa :icon="faReply"/></MkA>
<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$store.state.i" :custom-emojis="note.emojis"/>
<router-link class="rp" v-if="note.renoteId" :to="`/notes/${note.renoteId}`">RN: ...</router-link>
<MkA class="rp" v-if="note.renoteId" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
</div>
<details v-if="note.files.length > 0">
<summary>({{ $t('withNFiles', { n: note.files.length }) }})</summary>

View File

@@ -1,6 +1,6 @@
<template>
<div class="pxhvhrfw" v-size="{ max: [500] }">
<button v-for="item in items" class="_button" @click="$emit('update:value', item.value)" :class="{ active: value === item.value }" :key="item.value"><Fa v-if="item.icon" :icon="item.icon" class="icon"/>{{ item.label }}</button>
<button v-for="item in items" class="_button" @click="$emit('update:value', item.value)" :class="{ active: value === item.value }" :disabled="value === item.value" :key="item.value"><Fa v-if="item.icon" :icon="item.icon" class="icon"/>{{ item.label }}</button>
</div>
</template>
@@ -23,19 +23,26 @@ export default defineComponent({
<style lang="scss" scoped>
.pxhvhrfw {
display: flex;
max-width: var(--baseContentWidth);
margin: 0 auto;
> button {
flex: 1;
padding: 15px 12px 12px 12px;
border-bottom: solid 3px transparent;
&:disabled {
opacity: 1 !important;
cursor: default;
}
&.active {
color: var(--accent);
border-bottom-color: var(--accent);
}
&:not(.active):hover {
color: var(--fgHighlighted);
}
> .icon {
margin-right: 6px;
}

View File

@@ -0,0 +1,70 @@
<template>
<XWindow ref="window"
:initial-width="370"
:initial-height="450"
:can-resize="true"
@close="$refs.window.close()"
@closed="$emit('closed')"
>
<template #header>Req Viewer</template>
<div class="rlkneywz">
<MkTab v-model:value="tab" :items="[{ label: 'Request', value: 'req', }, { label: 'Response', value: 'res', }]" style="border-bottom: solid 1px var(--divider);"/>
<code v-if="tab === 'req'">{{ reqStr }}</code>
<code v-if="tab === 'res'">{{ resStr }}</code>
</div>
</XWindow>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import * as JSON5 from 'json5';
import XWindow from '@/components/ui/window.vue';
import MkTab from '@/components/tab.vue';
export default defineComponent({
components: {
XWindow,
MkTab,
},
props: {
req: {
required: true,
}
},
emits: ['closed'],
data() {
return {
tab: 'req',
reqStr: JSON5.stringify(this.req.req, null, '\t'),
resStr: JSON5.stringify(this.req.res, null, '\t'),
}
},
methods: {
}
});
</script>
<style lang="scss" scoped>
.rlkneywz {
display: flex;
flex-direction: column;
height: 100%;
> code {
display: block;
flex: 1;
padding: 8px;
overflow: auto;
font-size: 0.9em;
tab-size: 2;
white-space: pre;
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
}
}
</style>

View File

@@ -0,0 +1,231 @@
<template>
<XWindow ref="window" :initial-width="650" :initial-height="420" :can-resize="true" @closed="$emit('closed')">
<template #header>
<Fa :icon="faTerminal" style="margin-right: 0.5em;"/>Task Manager
</template>
<div class="qljqmnzj">
<MkTab v-model:value="tab" :items="[{ label: 'Windows', value: 'windows', }, { label: 'Stream', value: 'stream', }, { label: 'Stream (Pool)', value: 'streamPool', }, { label: 'API', value: 'api', }]" style="border-bottom: solid 1px var(--divider);"/>
<div class="content">
<div v-if="tab === 'windows'" class="windows" v-follow>
<div class="header">
<div>#ID</div>
<div>Component</div>
<div>Action</div>
</div>
<div v-for="p in popups">
<div>#{{ p.id }}</div>
<div>{{ p.component.name ? p.component.name : '<anonymous>' }}</div>
<div><button class="_textButton" @click="killPopup(p)">Kill</button></div>
</div>
</div>
<div v-if="tab === 'stream'" class="stream" v-follow>
<div class="header">
<div>#ID</div>
<div>Ch</div>
<div>Handle</div>
<div>In</div>
<div>Out</div>
</div>
<div v-for="c in connections">
<div>#{{ c.id }}</div>
<div>{{ c.channel }}</div>
<div v-if="c.users !== null">(shared)<span v-if="c.name">{{ ' ' + c.name }}</span></div>
<div v-else>{{ c.name ? c.name : '<anonymous>' }}</div>
<div>{{ c.in }}</div>
<div>{{ c.out }}</div>
</div>
</div>
<div v-if="tab === 'streamPool'" class="streamPool" v-follow>
<div class="header">
<div>#ID</div>
<div>Ch</div>
<div>Users</div>
</div>
<div v-for="p in pools">
<div>#{{ p.id }}</div>
<div>{{ p.channel }}</div>
<div>{{ p.users }}</div>
</div>
</div>
<div v-if="tab === 'api'" class="api" v-follow>
<div class="header">
<div>#ID</div>
<div>Endpoint</div>
<div>State</div>
</div>
<div v-for="req in apiRequests" @click="showReq(req)">
<div>#{{ req.id }}</div>
<div>{{ req.endpoint }}</div>
<div class="state" :class="req.state">{{ req.state }}</div>
</div>
</div>
</div>
<footer>
<div><span class="label">Windows</span>{{ popups.length }}</div>
<div><span class="label">Stream</span>{{ connections.length }}</div>
<div><span class="label">Stream (Pool)</span>{{ pools.length }}</div>
</footer>
</div>
</XWindow>
</template>
<script lang="ts">
import { defineComponent, markRaw, onBeforeUnmount, ref, shallowRef } from 'vue';
import { faTerminal } from '@fortawesome/free-solid-svg-icons';
import XWindow from '@/components/ui/window.vue';
import MkTab from '@/components/tab.vue';
import MkButton from '@/components/ui/button.vue';
import follow from '@/directives/follow-append';
import * as os from '@/os';
export default defineComponent({
components: {
XWindow,
MkTab,
MkButton,
},
directives: {
follow
},
props: {
},
emits: ['closed'],
setup() {
const connections = shallowRef([]);
const pools = shallowRef([]);
const refreshStreamInfo = () => {
console.log(os.stream.sharedConnectionPools, os.stream.sharedConnections, os.stream.nonSharedConnections);
const conn = os.stream.sharedConnections.map(c => ({
id: c.id, name: c.name, channel: c.channel, users: c.pool.users, in: c.inCount, out: c.outCount,
})).concat(os.stream.nonSharedConnections.map(c => ({
id: c.id, name: c.name, channel: c.channel, users: null, in: c.inCount, out: c.outCount,
})));
conn.sort((a, b) => (a.id > b.id) ? 1 : -1);
connections.value = conn;
pools.value = os.stream.sharedConnectionPools;
};
const interval = setInterval(refreshStreamInfo, 1000);
onBeforeUnmount(() => {
clearInterval(interval);
});
const killPopup = p => {
os.popups.value = os.popups.value.filter(x => x !== p);
};
const showReq = req => {
os.popup(import('./taskmanager.api-window.vue'), {
req: req
}, {
}, 'closed');
};
return {
tab: ref('stream'),
popups: os.popups,
apiRequests: os.apiRequests,
connections,
pools,
killPopup,
showReq,
faTerminal,
};
},
});
</script>
<style lang="scss" scoped>
.qljqmnzj {
display: flex;
flex-direction: column;
height: 100%;
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
> .content {
flex: 1;
overflow: auto;
> div {
display: table;
width: 100%;
padding: 16px;
box-sizing: border-box;
> div {
display: table-row;
&:nth-child(even) {
//background: rgba(0, 0, 0, 0.1);
}
&.header {
opacity: 0.7;
}
> div {
display: table-cell;
white-space: nowrap;
&:not(:last-child) {
padding-right: 8px;
}
}
}
&.api {
> div {
&:not(.header) {
cursor: pointer;
&:hover {
color: var(--accent);
}
}
> .state {
&.pending {
color: var(--warn);
}
&.success {
color: var(--success);
}
&.failed {
color: var(--error);
}
}
}
}
}
}
> footer {
display: flex;
width: 100%;
padding: 8px 16px;
box-sizing: border-box;
border-top: solid 1px var(--divider);
font-size: 0.9em;
> div {
flex: 1;
> .label {
opacity: 0.7;
margin-right: 0.5em;
&:after {
content: ":";
}
}
}
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<time class="mk-time" :title="absolute">
<time :title="absolute">
<template v-if="mode == 'relative'">{{ relative }}</template>
<template v-else-if="mode == 'absolute'">{{ absolute }}</template>
<template v-else-if="mode == 'detail'">{{ absolute }} ({{ relative }})</template>

View File

@@ -0,0 +1,133 @@
<template>
<a :href="to" :class="active ? activeClass : null" @click.prevent="nav" @contextmenu.prevent.stop="onContextmenu">
<slot></slot>
</a>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faExpandAlt, faColumns, faExternalLinkAlt, faLink, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
import * as os from '@/os';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import { router } from '@/router';
import { ui, url } from '@/config';
import { popout } from '@/scripts/popout';
export default defineComponent({
inject: {
navHook: {
default: null
},
sideViewHook: {
default: null
}
},
props: {
to: {
type: String,
required: true,
},
activeClass: {
type: String,
required: false,
},
behavior: {
type: String,
required: false,
},
},
computed: {
active() {
if (this.activeClass == null) return false;
const resolved = router.resolve(this.to);
if (resolved.path == this.$route.path) return true;
if (resolved.name == null) return false;
if (this.$route.name == null) return false;
return resolved.name == this.$route.name;
}
},
methods: {
onContextmenu(e) {
if (window.getSelection().toString() !== '') return;
os.contextMenu([{
type: 'label',
text: this.to,
}, {
icon: faWindowMaximize,
text: this.$t('openInWindow'),
action: () => {
os.pageWindow(this.to);
}
}, this.sideViewHook ? {
icon: faColumns,
text: this.$t('openInSideView'),
action: () => {
this.sideViewHook(this.to);
}
} : undefined, {
icon: faExpandAlt,
text: this.$t('showInPage'),
action: () => {
this.$router.push(this.to);
}
}, null, {
icon: faExternalLinkAlt,
text: this.$t('openInNewTab'),
action: () => {
window.open(this.to, '_blank');
}
}, {
icon: faLink,
text: this.$t('copyLink'),
action: () => {
copyToClipboard(`${url}${this.to}`);
}
}], e);
},
window() {
os.pageWindow(this.to);
},
popout() {
popout(this.to);
},
nav() {
if (this.to.startsWith('/my/messaging')) {
if (this.$store.state.device.chatOpenBehavior === 'window') return this.window();
if (this.$store.state.device.chatOpenBehavior === 'popout') return this.popout();
}
if (this.behavior) {
if (this.behavior === 'window') {
return this.window();
}
}
if (this.navHook) {
this.navHook(this.to);
} else {
if (this.$store.state.device.defaultSideView && this.sideViewHook && this.to !== '/') {
return this.sideViewHook(this.to);
}
if (this.$store.state.device.deckNavWindow && (ui === 'deck') && this.to !== '/') {
return this.window();
}
if (ui === 'desktop') {
return this.window();
}
if (this.$router.currentRoute.value.path === this.to) {
window.scroll({ top: 0, behavior: 'smooth' });
} else {
this.$router.push(this.to);
}
}
}
}
});
</script>

View File

@@ -1,7 +1,9 @@
<template>
<div class="nvlagfpb">
<MkMenu :items="items" @close="$emit('closed')" class="_popup _shadow" :align="'left'"/>
</div>
<transition :name="$store.state.device.animation ? 'fade' : ''" appear>
<div class="nvlagfpb" @contextmenu.prevent.stop="() => {}">
<MkMenu :items="items" @close="$emit('closed')" class="_popup _shadow" :align="'left'"/>
</div>
</transition>
</template>
<script lang="ts">
@@ -35,8 +37,30 @@ export default defineComponent({
},
},
mounted() {
this.$el.style.top = this.ev.pageY + 'px';
this.$el.style.left = this.ev.pageX + 'px';
let left = this.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
let top = this.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
const width = this.$el.offsetWidth;
const height = this.$el.offsetHeight;
if (left + width - window.pageXOffset > window.innerWidth) {
left = window.innerWidth - width + window.pageXOffset;
}
if (top + height - window.pageYOffset > window.innerHeight) {
top = window.innerHeight - height + window.pageYOffset;
}
if (top < 0) {
top = 0;
}
if (left < 0) {
left = 0;
}
this.$el.style.top = top + 'px';
this.$el.style.left = left + 'px';
for (const el of Array.from(document.querySelectorAll('body *'))) {
el.addEventListener('mousedown', this.onMousedown);
@@ -60,4 +84,14 @@ export default defineComponent({
position: absolute;
z-index: 65535;
}
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
transform-origin: left top;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
transform: scale(0.9);
}
</style>

View File

@@ -132,7 +132,7 @@ export default defineComponent({
&.max-width_500px {
> header {
> .title {
padding: 8px 10px;
padding: 8px 10px 8px 0;
}
}
}

View File

@@ -12,22 +12,22 @@
<span v-else-if="item.type === 'pending'" :tabindex="i" class="pending item">
<span><MkEllipsis/></span>
</span>
<router-link v-else-if="item.type === 'link'" :to="item.to" @click.passive="close()" :tabindex="i" class="_button item">
<MkA v-else-if="item.type === 'link'" :to="item.to" @click.passive="close()" :tabindex="i" class="_button item">
<Fa v-if="item.icon" :icon="item.icon" fixed-width/>
<MkAvatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><Fa :icon="faCircle"/></i>
</router-link>
</MkA>
<a v-else-if="item.type === 'a'" :href="item.href" :target="item.target" :download="item.download" @click="close()" :tabindex="i" class="_button item">
<Fa v-if="item.icon" :icon="item.icon" fixed-width/>
<span>{{ item.text }}</span>
<i v-if="item.indicate"><Fa :icon="faCircle"/></i>
</a>
<button v-else-if="item.type === 'user'" @click="clicked(item.action)" :tabindex="i" class="_button item">
<button v-else-if="item.type === 'user'" @click="clicked(item.action, $event)" :tabindex="i" class="_button item">
<MkAvatar :user="item.user" class="avatar"/><MkUserName :user="item.user"/>
<i v-if="item.indicate"><Fa :icon="faCircle"/></i>
</button>
<button v-else @click="clicked(item.action)" :tabindex="i" class="_button item" :class="{ danger: item.danger }">
<button v-else @click="clicked(item.action, $event)" :tabindex="i" class="_button item" :class="{ danger: item.danger }">
<Fa v-if="item.icon" :icon="item.icon" fixed-width/>
<MkAvatar v-if="item.avatar" :user="item.avatar" class="avatar"/>
<span>{{ item.text }}</span>
@@ -115,8 +115,8 @@ export default defineComponent({
}
},
methods: {
clicked(fn) {
fn();
clicked(fn, ev) {
fn(ev);
this.close();
},
close() {

View File

@@ -175,7 +175,7 @@ export default defineComponent({
}
.modal-popup-content-enter-active, .modal-popup-content-leave-active {
transition: opacity 0.3s, transform 0.3s !important;
transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1) !important;
}
.modal-popup-content-enter-from, .modal-popup-content-leave-to {
pointer-events: none;

View File

@@ -1,5 +1,5 @@
<template>
<div class="cxiknjgy" :class="{ autoMargin }">
<div class="cxiknjgy">
<slot :items="items"></slot>
<div class="empty" v-if="empty" key="_empty_">
<slot name="empty"></slot>
@@ -31,24 +31,12 @@ export default defineComponent({
pagination: {
required: true
},
autoMargin: {
required: false,
default: true
}
},
});
</script>
<style lang="scss" scoped>
.cxiknjgy {
&.autoMargin > *:not(:last-child) {
margin-bottom: 16px;
@media (max-width: 500px) {
margin-bottom: 8px;
}
}
> .more > .button {
margin-left: auto;
margin-right: auto;

View File

@@ -18,7 +18,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import * as os from '@/os';
export default defineComponent({
props: {
@@ -51,7 +50,8 @@ export default defineComponent({
.novjtctn {
position: relative;
display: inline-block;
margin: 0 32px 0 0;
margin: 8px 20px 0 0;
text-align: left;
cursor: pointer;
transition: all 0.3s;

View File

@@ -0,0 +1,58 @@
<script lang="ts">
import { defineComponent, h } from 'vue';
import MkRadio from '@/components/ui/radio.vue';
export default defineComponent({
components: {
MkRadio
},
props: {
defs: {
required: true
},
modelValue: {
required: false
},
},
data() {
return {
value: this.modelValue,
}
},
watch: {
value() {
this.$emit('update:modelValue', this.value);
}
},
render() {
const label = this.$slots.desc();
const options = this.$slots.default();
return h('div', {
class: 'novjtcto'
}, [
h('div', label),
...options.map(option => h(MkRadio, {
key: option.props.value,
value: option.props.value,
modelValue: this.value,
'onUpdate:modelValue': value => this.value = value,
}, option.children))
]);
}
});
</script>
<style lang="scss" scoped>
.novjtcto {
margin: 32px 0;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
</style>

View File

@@ -26,7 +26,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { faChevronDown } from '@fortawesome/free-solid-svg-icons';
import * as os from '@/os';
export default defineComponent({
props: {

View File

@@ -2,12 +2,13 @@
<div class="adhpbeos" :class="{ focused, filled, tall, pre }">
<div class="input">
<span class="label" ref="label"><slot></slot></span>
<textarea ref="input"
<textarea ref="input" :class="{ code }"
:value="value"
:required="required"
:readonly="readonly"
:pattern="pattern"
:autocomplete="autocomplete"
:spellcheck="!code"
@input="onInput"
@focus="focused = true"
@blur="focused = false"
@@ -20,7 +21,6 @@
<script lang="ts">
import { defineComponent } from 'vue';
import * as os from '@/os';
export default defineComponent({
props: {
@@ -43,6 +43,10 @@ export default defineComponent({
type: String,
required: false
},
code: {
type: Boolean,
required: false
},
tall: {
type: Boolean,
required: false,
@@ -159,6 +163,11 @@ export default defineComponent({
outline: none;
box-shadow: none;
color: var(--fg);
&.code {
tab-size: 2;
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
}
}
}

View File

@@ -2,12 +2,16 @@
<transition :name="$store.state.device.animation ? 'window' : ''" appear @after-leave="$emit('closed')">
<div class="ebkgocck" v-if="showing">
<div class="body _popup _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown">
<div class="header">
<button class="_button" @click="close()"><Fa :icon="faTimes"/></button>
<div class="header" @contextmenu.prevent.stop="onContextmenu">
<slot v-if="closeRight" name="buttons"><button class="_button" style="pointer-events: none;"></button></slot>
<button v-else class="_button" @click="close()"><Fa :icon="faTimes"/></button>
<span class="title" @mousedown.prevent="onHeaderMousedown" @touchstart.prevent="onHeaderMousedown">
<slot name="header"></slot>
</span>
<slot name="buttons"></slot>
<button v-if="closeRight" class="_button" @click="close()"><Fa :icon="faTimes"/></button>
<slot v-else name="buttons"><button class="_button" style="pointer-events: none;"></button></slot>
</div>
<div class="body" v-if="padding">
<div class="_section">
@@ -83,6 +87,15 @@ export default defineComponent({
required: false,
default: false,
},
closeRight: {
type: Boolean,
required: false,
default: false,
},
contextmenu: {
type: Array,
required: false,
}
},
emits: ['closed'],
@@ -106,6 +119,9 @@ export default defineComponent({
z: Number(document.defaultView.getComputedStyle(this.$el, null).zIndex)
});
// 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする
this.top();
window.addEventListener('resize', this.onBrowserResize);
},
@@ -127,6 +143,12 @@ export default defineComponent({
}
},
onContextmenu(e) {
if (this.contextmenu) {
os.contextMenu(this.contextmenu, e);
}
},
// 最前面へ移動
top() {
let z = 0;
@@ -313,11 +335,13 @@ export default defineComponent({
// 高さを適用
applyTransformHeight(height) {
if (height > window.innerHeight) height = window.innerHeight;
(this.$el as any).style.height = height + 'px';
},
// 幅を適用
applyTransformWidth(width) {
if (width > window.innerWidth) width = window.innerWidth;
(this.$el as any).style.width = width + 'px';
},
@@ -371,15 +395,13 @@ export default defineComponent({
width: 100%;
height: 100%;
--section-padding: 16px;
> .header {
$height: 50px;
display: flex;
position: relative;
z-index: 1;
flex-shrink: 0;
box-shadow: 0px 1px var(--divider);
cursor: move;
user-select: none;
height: $height;
@@ -399,6 +421,8 @@ export default defineComponent({
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
cursor: move;
}
}

View File

@@ -8,7 +8,7 @@
</div>
<div v-else class="mk-url-preview" v-size="{ max: [400, 350] }">
<transition name="zoom" mode="out-in">
<component :is="self ? 'router-link' : 'a'" :class="{ compact }" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url" v-if="!fetching">
<component :is="self ? 'MkA' : 'a'" :class="{ compact }" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url" v-if="!fetching">
<div class="thumbnail" v-if="thumbnail" :style="`background-image: url('${thumbnail}')`">
<button class="_button" v-if="!playerEnabled && player.url" @click.prevent="playerEnabled = true" :title="$t('enablePlayer')"><Fa :icon="faPlayCircle"/></button>
</div>

View File

@@ -1,5 +1,5 @@
<template>
<component :is="self ? 'router-link' : 'a'" class="ieqqeuvs _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
<component :is="self ? 'MkA' : 'a'" class="ieqqeuvs _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
>
@@ -71,7 +71,7 @@ export default defineComponent({
if (!document.body.contains(this.$el)) return;
if (this.close) return;
const { dispose } = os.popup(await import('@/components/url-preview-popup.vue'), {
const { dispose } = await os.popup(import('@/components/url-preview-popup.vue'), {
url: this.url,
source: this.$el
});

View File

@@ -3,7 +3,7 @@
<div class="banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
<MkAvatar class="avatar" :user="user" :disable-preview="true"/>
<div class="title">
<router-link class="name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></router-link>
<MkA class="name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
<p class="username"><MkAcct :user="user"/></p>
</div>
<div class="description">
@@ -96,7 +96,7 @@ export default defineComponent({
margin: 0;
line-height: 16px;
font-size: 0.8em;
color: var(--text);
color: var(--fg);
opacity: 0.7;
}
}
@@ -125,7 +125,7 @@ export default defineComponent({
> p {
margin: 0;
font-size: 0.7em;
color: var(--text);
color: var(--fg);
}
> span {

View File

@@ -5,7 +5,7 @@
<div class="banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''"></div>
<MkAvatar class="avatar" :user="user" :disable-preview="true"/>
<div class="title">
<router-link class="name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></router-link>
<MkA class="name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
<p class="username"><MkAcct :user="user"/></p>
</div>
<div class="description">
@@ -151,7 +151,7 @@ export default defineComponent({
margin: 0;
line-height: 16px;
font-size: 0.8em;
color: var(--text);
color: var(--fg);
opacity: 0.7;
}
}
@@ -159,7 +159,7 @@ export default defineComponent({
> .description {
padding: 0 16px;
font-size: 0.8em;
color: var(--text);
color: var(--fg);
}
> .status {
@@ -172,7 +172,7 @@ export default defineComponent({
> p {
margin: 0;
font-size: 0.7em;
color: var(--text);
color: var(--fg);
}
> span {

View File

@@ -14,10 +14,10 @@
<MkInput v-model:value="host" class="input" @update:value="search"><span>{{ $t('host') }}</span><template #prefix>@</template></MkInput>
</div>
</div>
<div class="tbhwbxda _section" :style="users.length > 0 ? 'padding: 0;' : ''">
<div class="tbhwbxda _section result" v-if="username != '' || host != ''" :class="{ hit: users.length > 0 }">
<div class="users" v-if="users.length > 0">
<div class="user" v-for="user in users" :key="user.id" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()">
<MkAvatar :user="user" class="avatar" :disable-link="true"/>
<MkAvatar :user="user" class="avatar"/>
<div class="body">
<MkUserName :user="user" class="name"/>
<MkAcct :user="user" class="acct"/>
@@ -28,6 +28,17 @@
<span>{{ $t('noUsers') }}</span>
</div>
</div>
<div class="tbhwbxda _section recent" v-if="username == '' && host == ''">
<div class="users">
<div class="user" v-for="user in recentUsers" :key="user.id" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()">
<MkAvatar :user="user" class="avatar"/>
<div class="body">
<MkUserName :user="user" class="name"/>
<MkAcct :user="user" class="acct"/>
</div>
</div>
</div>
</div>
</XModalWindow>
</template>
@@ -53,18 +64,23 @@ export default defineComponent({
return {
username: '',
host: '',
recentUsers: [],
users: [],
selected: null,
faTimes, faCheck
};
},
mounted() {
async mounted() {
this.focus();
this.$nextTick(() => {
this.focus();
});
this.recentUsers = await os.api('users/show', {
userIds: this.$store.state.device.recentlyUsedUsers
});
},
methods: {
@@ -90,6 +106,12 @@ export default defineComponent({
ok() {
this.$emit('ok', this.selected);
this.$refs.dialog.close();
// 最近使ったユーザー更新
let recents = this.$store.state.device.recentlyUsedUsers;
recents = recents.filter(x => x !== this.selected.id);
recents.unshift(this.selected.id);
this.$store.commit('device/set', { key: 'recentlyUsedUsers', value: recents.splice(0, 16) });
},
cancel() {
@@ -107,6 +129,14 @@ export default defineComponent({
overflow: auto;
height: 100%;
&.result.hit {
padding: 0;
}
&.recent {
padding: 0;
}
> .inputs {
> .input {
display: inline-block;

View File

@@ -6,13 +6,13 @@
</div>
<div class="users">
<router-link v-for="item in items" class="user" :key="item.id" :to="userPage(extract ? extract(item) : item)">
<MkA v-for="item in items" class="user" :key="item.id" :to="userPage(extract ? extract(item) : item)">
<MkAvatar :user="extract ? extract(item) : item" class="avatar" :disable-link="true"/>
<div class="body">
<MkUserName :user="extract ? extract(item) : item" class="name"/>
<MkAcct :user="extract ? extract(item) : item" class="acct"/>
</div>
</router-link>
</MkA>
</div>
<button class="more _button" v-appear="$store.state.device.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>

View File

@@ -55,11 +55,11 @@ export default defineComponent({
props: {
currentVisibility: {
type: String,
required: false
required: true
},
currentLocalOnly: {
type: Boolean,
required: false
required: true
},
src: {
required: false
@@ -68,7 +68,7 @@ export default defineComponent({
emits: ['change-visibility', 'change-local-only', 'closed'],
data() {
return {
v: this.$store.state.settings.rememberNoteVisibility ? this.$store.state.deviceUser.visibility : (this.currentVisibility || this.$store.state.settings.defaultNoteVisibility),
v: this.currentVisibility,
localOnly: this.currentLocalOnly,
faGlobe, faUnlock, faEnvelope, faHome, faBiohazard, faToggleOn, faToggleOff
}
@@ -81,9 +81,6 @@ export default defineComponent({
methods: {
choose(visibility) {
this.v = visibility;
if (this.$store.state.settings.rememberNoteVisibility) {
this.$store.commit('deviceUser/setVisibility', visibility);
}
this.$emit('change-visibility', visibility);
this.$nextTick(() => {
this.$refs.modal.close();

View File

@@ -1,8 +1,9 @@
<template>
<MkModal ref="modal" @click="type === 'success' ? done() : () => {}" @closed="$emit('closed')">
<div class="iuyakobc" :class="type">
<Fa class="icon" v-if="type === 'success'" :icon="faCheck"/>
<Fa class="icon" v-else-if="type === 'waiting'" :icon="faSpinner" pulse/>
<MkModal ref="modal" @click="success ? done() : () => {}" @closed="$emit('closed')">
<div class="iuyakobc" :class="{ iconOnly: (text == null) || success }">
<Fa class="icon success" v-if="success" :icon="faCheck"/>
<Fa class="icon waiting" v-else :icon="faSpinner" pulse/>
<div class="text" v-if="text && !success">{{ text }}<MkEllipsis/></div>
</div>
</MkModal>
</template>
@@ -18,12 +19,18 @@ export default defineComponent({
},
props: {
type: {
required: true
success: {
type: Boolean,
required: true,
},
showing: {
required: true
}
type: Boolean,
required: true,
},
text: {
type: String,
required: false,
},
},
emits: ['done', 'closed'],
@@ -57,17 +64,32 @@ export default defineComponent({
text-align: center;
background: var(--panel);
border-radius: var(--radius);
width: initial;
font-size: 32px;
width: 250px;
&.success {
color: var(--accent);
&.iconOnly {
padding: 0;
width: 96px;
height: 96px;
> .icon {
height: 100%;
}
}
&.waiting {
> .icon {
> .icon {
font-size: 32px;
&.success {
color: var(--accent);
}
&.waiting {
opacity: 0.7;
}
}
> .text {
margin-top: 16px;
}
}
</style>

View File

@@ -12,5 +12,6 @@ export const lang = localStorage.getItem('lang');
export const langs = _LANGS_;
export const getLocale = async () => Object.fromEntries((await entries(clientDb.i18n)) as [string, string][]);
export const version = _VERSION_;
export const instanceName = siteName === 'Misskey' ? null : siteName;
export const deckmode = localStorage.getItem('deckmode') === 'true';
export const instanceName = siteName === 'Misskey' ? host : siteName;
export const ui = localStorage.getItem('ui');
export const debug = localStorage.getItem('debug') === 'true';

View File

@@ -0,0 +1,25 @@
import { Directive } from 'vue';
import { getScrollContainer, getScrollPosition } from '@/scripts/scroll';
export default {
mounted(src, binding, vn) {
const ro = new ResizeObserver((entries, observer) => {
const pos = getScrollPosition(src);
const container = getScrollContainer(src);
const viewHeight = container.clientHeight;
const height = container.scrollHeight;
if (pos + viewHeight > height - 32) {
container.scrollTop = height;
}
});
ro.observe(src);
// TODO: 新たにプロパティを作るのをやめMapを使う
src._ro_ = ro;
},
unmounted(src, binding, vn) {
src._ro_.unobserve(src);
}
} as Directive;

View File

@@ -23,13 +23,13 @@ export default {
}
};
const show = async e => {
const show = e => {
if (!document.body.contains(el)) return;
if (self._close) return;
if (self.text == null) return;
const showing = ref(true);
popup(await import('@/components/ui/tooltip.vue'), {
popup(import('@/components/ui/tooltip.vue'), {
showing,
text: self.text,
source: el

View File

@@ -18,13 +18,13 @@ export class UserPreview {
}
@autobind
private async show() {
private show() {
if (!document.body.contains(this.el)) return;
if (this.promise) return;
const showing = ref(true);
popup(await import('@/components/user-preview.vue'), {
popup(import('@/components/user-preview.vue'), {
showing,
q: this.user,
source: this.el

View File

@@ -7,11 +7,10 @@ import '@/style.scss';
import { createApp } from 'vue';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import Root from './root.vue';
import widgets from './widgets';
import directives from './directives';
import components from '@/components';
import { version, apiUrl } from '@/config';
import { version, apiUrl, ui } from '@/config';
import { store } from './store';
import { router } from './router';
import { applyTheme } from '@/scripts/theme';
@@ -51,7 +50,7 @@ if (_DEV_) {
document.addEventListener('touchend', () => {}, { passive: true });
if (localStorage.getItem('theme') == null) {
applyTheme(require('@/themes/white.json5'));
applyTheme(require('@/themes/l-white.json5'));
}
//#region SEE: https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
@@ -152,7 +151,13 @@ store.dispatch('instance/fetch').then(() => {
stream.init(store.state.i);
const app = createApp(Root);
const app = createApp(await (
window.location.search === '?zen' ? import('@/ui/zen.vue') :
!store.getters.isSignedIn ? import('@/ui/visitor.vue') :
ui === 'deck' ? import('@/ui/deck.vue') :
ui === 'desktop' ? import('@/ui/desktop.vue') :
import('@/ui/default.vue')
).then(x => x.default));
if (_DEV_) {
app.config.performance = true;
@@ -248,7 +253,7 @@ if (store.getters.isSignedIn) {
}
}
const main = stream.useSharedConnection('main');
const main = stream.useSharedConnection('main', 'System');
// 自分の情報が更新されたとき
main.on('meUpdated', i => {

View File

@@ -2,37 +2,41 @@ import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vu
import { EventEmitter } from 'eventemitter3';
import Stream from '@/scripts/stream';
import { store } from '@/store';
import { apiUrl } from '@/config';
import { apiUrl, debug } from '@/config';
import MkPostFormDialog from '@/components/post-form-dialog.vue';
import MkWaitingDialog from '@/components/waiting-dialog.vue';
import { resolve } from '@/router';
const ua = navigator.userAgent.toLowerCase();
export const isMobile = /mobile|iphone|ipad|android/.test(ua);
export const stream = new Stream();
export const stream = markRaw(new Stream());
export const pendingApiRequestsCount = ref(0);
let apiRequestsCount = 0; // for debug
export const apiRequests = ref([]); // for debug
export const windows = new Map();
export function api(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined) {
pendingApiRequestsCount.value++;
if (_DEV_) {
performance.mark(_PERF_PREFIX_ + 'api:begin');
}
const onFinally = () => {
pendingApiRequestsCount.value--;
if (_DEV_) {
performance.mark(_PERF_PREFIX_ + 'api:end');
performance.measure(_PERF_PREFIX_ + 'api',
_PERF_PREFIX_ + 'api:begin',
_PERF_PREFIX_ + 'api:end');
}
};
const log = debug ? reactive({
id: ++apiRequestsCount,
endpoint,
req: markRaw(data),
res: null,
state: 'pending',
}) : null;
if (debug) {
apiRequests.value.push(log);
if (apiRequests.value.length > 128) apiRequests.value.shift();
}
const promise = new Promise((resolve, reject) => {
// Append a credential
if (store.getters.isSignedIn) (data as any).i = store.state.i.token;
@@ -49,10 +53,21 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st
if (res.status === 200) {
resolve(body);
if (debug) {
log.res = markRaw(body);
log.state = 'success';
}
} else if (res.status === 204) {
resolve();
if (debug) {
log.state = 'success';
}
} else {
reject(body.error);
if (debug) {
log.res = markRaw(body.error);
log.state = 'failed';
}
}
}).catch(reject);
});
@@ -62,17 +77,39 @@ export function api(endpoint: string, data: Record<string, any> = {}, token?: st
return promise;
}
export function apiWithDialog(endpoint: string, data: Record<string, any> = {}, token?: string | null | undefined, onSuccess?: (res: any) => void, onFailure?: (e: Error) => void) {
const showing = ref(true);
const state = ref('waiting');
export function apiWithDialog(
endpoint: string,
data: Record<string, any> = {},
token?: string | null | undefined,
onSuccess?: (res: any) => void,
onFailure?: (e: Error) => void,
) {
const promise = api(endpoint, data, token);
promiseDialog(promise, onSuccess, onFailure ? onFailure : (e) => {
dialog({
type: 'error',
text: e.message + '\n' + (e as any).id,
});
});
return promise;
}
export function promiseDialog<T extends Promise<any>>(
promise: T,
onSuccess?: (res: any) => void,
onFailure?: (e: Error) => void,
text?: string,
): T {
const showing = ref(true);
const success = ref(false);
promise.then(res => {
if (onSuccess) {
showing.value = false;
onSuccess(res);
} else {
state.value = 'success';
success.value = true;
setTimeout(() => {
showing.value = false;
}, 1000);
@@ -89,9 +126,11 @@ export function apiWithDialog(endpoint: string, data: Record<string, any> = {},
}
});
popup(defineAsyncComponent(() => import('@/components/icon-dialog.vue')), {
type: state,
showing: showing
// NOTE: dynamic importすると挙動がおかしくなる(showingの変更が伝播しない)
popup(MkWaitingDialog, {
success: success,
showing: showing,
text: text,
}, {}, 'closed');
return promise;
@@ -101,17 +140,20 @@ function isModule(x: any): x is typeof import('*.vue') {
return x.default != null;
}
let popupIdCount = 0;
export const popups = ref([]) as Ref<{
id: any;
component: any;
props: Record<string, any>;
}[]>;
export function popup(component: Component | typeof import('*.vue'), props: Record<string, any>, events = {}, disposeEvent?: string) {
export async function popup(component: Component | typeof import('*.vue') | Promise<Component | typeof import('*.vue')>, props: Record<string, any>, events = {}, disposeEvent?: string) {
if (component.then) component = await component;
if (isModule(component)) component = component.default;
markRaw(component);
const id = Math.random().toString(); // TODO: uuidとか使う
const id = ++popupIdCount;
const dispose = () => {
if (_DEV_) console.log('os:popup close', id, component, props, events);
// このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ
@@ -137,9 +179,10 @@ export function popup(component: Component | typeof import('*.vue'), props: Reco
};
}
export function pageWindow(url: string, component: Component | typeof import('*.vue'), props: Record<string, any>) {
popup(defineAsyncComponent(() => import('@/components/page-window.vue')), {
initialUrl: url,
export function pageWindow(path: string) {
const { component, props } = resolve(path);
popup(import('@/components/page-window.vue'), {
initialPath: path,
initialComponent: markRaw(component),
initialProps: props,
}, {}, 'closed');
@@ -147,7 +190,7 @@ export function pageWindow(url: string, component: Component | typeof import('*.
export function dialog(props: Record<string, any>) {
return new Promise((resolve, reject) => {
popup(defineAsyncComponent(() => import('@/components/dialog.vue')), props, {
popup(import('@/components/dialog.vue'), props, {
done: result => {
resolve(result ? result : { canceled: true });
},
@@ -161,8 +204,8 @@ export function success() {
setTimeout(() => {
showing.value = false;
}, 1000);
popup(defineAsyncComponent(() => import('@/components/icon-dialog.vue')), {
type: 'success',
popup(import('@/components/waiting-dialog.vue'), {
success: true,
showing: showing
}, {
done: () => resolve(),
@@ -173,8 +216,8 @@ export function success() {
export function waiting() {
return new Promise((resolve, reject) => {
const showing = ref(true);
popup(defineAsyncComponent(() => import('@/components/icon-dialog.vue')), {
type: 'waiting',
popup(import('@/components/waiting-dialog.vue'), {
success: false,
showing: showing
}, {
done: () => resolve(),
@@ -184,7 +227,7 @@ export function waiting() {
export function form(title, form) {
return new Promise((resolve, reject) => {
popup(defineAsyncComponent(() => import('@/components/form-dialog.vue')), { title, form }, {
popup(import('@/components/form-dialog.vue'), { title, form }, {
done: result => {
resolve(result);
},
@@ -194,7 +237,7 @@ export function form(title, form) {
export async function selectUser() {
return new Promise((resolve, reject) => {
popup(defineAsyncComponent(() => import('@/components/user-select-dialog.vue')), {}, {
popup(import('@/components/user-select-dialog.vue'), {}, {
ok: user => {
resolve(user);
},
@@ -204,7 +247,7 @@ export async function selectUser() {
export async function selectDriveFile(multiple: boolean) {
return new Promise((resolve, reject) => {
popup(defineAsyncComponent(() => import('@/components/drive-window.vue')), {
popup(import('@/components/drive-select-dialog.vue'), {
type: 'file',
multiple
}, {
@@ -219,7 +262,7 @@ export async function selectDriveFile(multiple: boolean) {
export async function selectDriveFolder(multiple: boolean) {
return new Promise((resolve, reject) => {
popup(defineAsyncComponent(() => import('@/components/drive-window.vue')), {
popup(import('@/components/drive-select-dialog.vue'), {
type: 'folder',
multiple
}, {
@@ -232,10 +275,11 @@ export async function selectDriveFolder(multiple: boolean) {
});
}
export async function pickEmoji(src?: HTMLElement) {
export async function pickEmoji(src?: HTMLElement, opts) {
return new Promise((resolve, reject) => {
popup(defineAsyncComponent(() => import('@/components/emoji-picker.vue')), {
src
popup(import('@/components/emoji-picker.vue'), {
src,
...opts
}, {
done: emoji => {
resolve(emoji);
@@ -246,7 +290,8 @@ export async function pickEmoji(src?: HTMLElement) {
export function modalMenu(items: any[], src?: HTMLElement, options?: { align?: string; viaKeyboard?: boolean }) {
return new Promise((resolve, reject) => {
const { dispose } = popup(defineAsyncComponent(() => import('@/components/ui/modal-menu.vue')), {
let dispose;
popup(import('@/components/ui/modal-menu.vue'), {
items,
src,
align: options?.align,
@@ -256,6 +301,8 @@ export function modalMenu(items: any[], src?: HTMLElement, options?: { align?: s
resolve();
dispose();
},
}).then(res => {
dispose = res.dispose;
});
});
}
@@ -263,7 +310,8 @@ export function modalMenu(items: any[], src?: HTMLElement, options?: { align?: s
export function contextMenu(items: any[], ev: MouseEvent) {
ev.preventDefault();
return new Promise((resolve, reject) => {
const { dispose } = popup(defineAsyncComponent(() => import('@/components/ui/context-menu.vue')), {
let dispose;
popup(import('@/components/ui/context-menu.vue'), {
items,
ev,
}, {
@@ -271,6 +319,8 @@ export function contextMenu(items: any[], ev: MouseEvent) {
resolve();
dispose();
},
}).then(res => {
dispose = res.dispose;
});
});
}
@@ -278,11 +328,18 @@ export function contextMenu(items: any[], ev: MouseEvent) {
export function post(props: Record<string, any>) {
return new Promise((resolve, reject) => {
// NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない
const { dispose } = popup(MkPostFormDialog, props, {
// NOTE: ただ、dynamic importしない場合、MkPostFormDialogインスタンスが使いまわされ、
// Vueが渡されたコンポーネントに内部的に__propsというプロパティを生やす影響で、
// 複数のpost formを開いたときに場合によってはエラーになる
// もちろん複数のpost formを開けること自体Misskeyサイドのバグなのだが
let dispose;
popup(MkPostFormDialog, props, {
closed: () => {
resolve();
dispose();
},
}).then(res => {
dispose = res.dispose;
});
});
}

View File

@@ -22,10 +22,8 @@ export default defineComponent({
data() {
return {
INFO: {
header: [{
title: this.$t('error'),
icon: faExclamationTriangle
}]
title: this.$t('error'),
icon: faExclamationTriangle
},
faExclamationTriangle
};

Some files were not shown because too many files have changed in this diff Show More