Compare commits

..

119 Commits

Author SHA1 Message Date
syuilo
e4bcdd7b4d Merge branch 'develop' 2021-01-23 20:06:22 +09:00
syuilo
bce65e2140 12.68.0 2021-01-23 20:06:07 +09:00
syuilo
7747ec5b6d Update ja-JP.yml 2021-01-23 20:05:44 +09:00
syuilo
c3b797b414 Update ja-JP.yml 2021-01-23 19:59:05 +09:00
syuilo
0343c5e692 New Crowdin updates (#7095)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Korean)

* New translations aiscript.md (Korean)

* 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 (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 (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations stream.md (Chinese Simplified)

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

* New translations stream.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

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

* New translations theme.md (Chinese Traditional)

* New translations theme.md (Chinese Traditional)

* New translations theme.md (Chinese Traditional)

* New translations theme.md (Chinese Traditional)

* New translations theme.md (Chinese Traditional)

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

* New translations mfm.md (Chinese Traditional)

* New translations keyboard-shortcut.md (Chinese Traditional)

* New translations keyboard-shortcut.md (Chinese Traditional)

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

* New translations keyboard-shortcut.md (Chinese Traditional)

* New translations pages.md (Chinese Traditional)

* New translations timelines.md (Chinese Traditional)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations keyboard-shortcut.md (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Arabic)
2021-01-23 19:58:07 +09:00
MeiMei
1621e5d6fb nginxのサンプルにコメント追加 (#7113) 2021-01-23 19:57:43 +09:00
syuilo
6311474fa7 Update ja-JP.yml 2021-01-23 18:57:34 +09:00
syuilo
e0e373c357 🎨 2021-01-23 18:16:26 +09:00
syuilo
9fff26b900 Implement drive setting page 2021-01-23 18:14:57 +09:00
syuilo
d439bc8c2f 🎨 2021-01-23 17:26:54 +09:00
syuilo
fe52f7c392 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2021-01-23 16:52:50 +09:00
syuilo
82207598f0 Add misskey update page 2021-01-23 16:52:45 +09:00
syuilo
a2bef1fa52 🎨 2021-01-23 16:51:52 +09:00
sobadon
3762cdc8c0 グループの編集画面をウィンドウで開けないバグを修正 (#7100) 2021-01-18 00:53:48 +09:00
coord_e
b224760aba Fix to wait for the removal of item in i/registry/remove (#7101) 2021-01-18 00:52:41 +09:00
syuilo
cbdd14fa82 Delete docker.yml
docker-hub上でビルドするようにした
2021-01-16 13:48:15 +09:00
syuilo
96d6e0f8a2 Fix theme saving 2021-01-16 13:47:07 +09:00
syuilo
a44ad63440 Better boot handling 2021-01-16 13:46:46 +09:00
syuilo
c3592e8c4a Update boot.js 2021-01-16 01:39:44 +09:00
syuilo
d153d3b761 12.67.1 2021-01-15 23:23:16 +09:00
Ebise Lutica
60561578d8 投稿フォームで、IME入力中もカウントを更新するように (#7080)
* 投稿フォームでIME入力中も文字数が反映されるように

* compositionendのハンドル忘れを修正

* Remove console.log

* Remove trimmedLength()
2021-01-15 23:20:47 +09:00
syuilo
b8bc96c181 New Crowdin updates (#7056)
* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Arabic)

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

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Russian)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Ukrainian)

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

* New translations ja-JP.yml (Indonesian)

* New translations stream.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations theme.md (Polish)

* New translations timelines.md (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations reversi-bot.md (Polish)

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

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations stream.md (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

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

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

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations follow.md (Chinese Traditional)

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

* New translations follow.md (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 (German)

* New translations ja-JP.yml (English)

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

* New translations theme.md (Chinese Simplified)

* New translations theme.md (Chinese Simplified)

* New translations theme.md (Chinese Simplified)

* New translations theme.md (Chinese Simplified)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

* New translations stream.md (Chinese Simplified)

* New translations stream.md (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 (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 Traditional)

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

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

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Ukrainian)

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

* New translations ja-JP.yml (Russian)

* 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 (Ukrainian)

* New translations ja-JP.yml (Ukrainian)

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

* New translations ja-JP.yml (Ukrainian)

* New translations stream.md (Chinese Simplified)

* New translations ja-JP.yml (Ukrainian)

* New translations stream.md (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 (Portuguese)

* New translations keyboard-shortcut.md (Portuguese)

* New translations mute.md (Portuguese)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Arabic)

* New translations timelines.md (Arabic)

* New translations aiscript.md (Arabic)

* New translations timelines.md (Arabic)
2021-01-15 23:19:28 +09:00
syuilo
005d6b4ccf pageのノート埋め込みの「詳細な表示」オプションが機能していない問題を修正 2021-01-15 23:18:14 +09:00
syuilo
f972fb6920 🎨 2021-01-15 22:56:08 +09:00
syuilo
77078f2ce2 Fix bug 2021-01-15 22:38:39 +09:00
syuilo
dae6d60163 🎨 2021-01-14 23:37:57 +09:00
syuilo
264d6afe5d Update README.md 2021-01-13 22:07:52 +09:00
syuilo
52c1d87fa2 12.67.0 2021-01-13 22:04:26 +09:00
syuilo
dccafca58d Update deps 2021-01-13 22:02:40 +09:00
syuilo
52c12f96d5 Resolve #7074 2021-01-13 21:43:06 +09:00
syuilo
114a9fbdb2 テーマをレジストリに保存するように 2021-01-11 22:31:17 +09:00
syuilo
20e67e7edd Tweak tab size 2021-01-11 21:01:54 +09:00
syuilo
889b52e813 アカウントの閉鎖をクライアントから行えるように 2021-01-11 20:57:48 +09:00
syuilo
6c975275f8 Registry (#7073)
* wip

* wip

* wip

* wip

* wip

* Update registry.value.vue

* wip

* wip

* wip

* wip

* typo
2021-01-11 20:38:34 +09:00
syuilo
1286dee1ab fix context menu 2021-01-11 18:49:39 +09:00
syuilo
fee953b0df Improve theme editor 2021-01-09 22:02:26 +09:00
syuilo
26e7454f0e Improve theme editor 2021-01-09 21:47:07 +09:00
syuilo
edbaa07867 簡易テーマエディタ実装 2021-01-09 17:18:45 +09:00
syuilo
f7ebf14501 Add ping api 2021-01-09 09:25:27 +09:00
syuilo
d00928cf3f 🎨 2021-01-08 21:44:02 +09:00
syuilo
b3a198cae6 Sentry integration 2021-01-08 21:43:56 +09:00
takonomura
8b87d56a5a NoteDetailed で一部 Renote した人の情報になっているのを修正 (#7063) 2021-01-07 12:22:21 +09:00
sobadon
62ac3400da 検索のタイトルが変更されない問題を修正 (#7062) 2021-01-07 12:21:34 +09:00
syuilo
d54f71774e 12.66.0 2021-01-04 17:39:18 +09:00
syuilo
a47358f590 New Crowdin updates (#7046)
* 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 (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 aiscript.md (German)

* New translations theme.md (German)

* New translations reaction.md (German)

* New translations theme.md (German)

* New translations theme.md (German)

* New translations ja-JP.yml (German)

* New translations theme.md (German)

* New translations timelines.md (German)

* New translations ja-JP.yml (German)

* New translations mute.md (German)

* New translations timelines.md (German)

* New translations mute.md (German)

* New translations reaction.md (German)

* New translations pages.md (German)

* New translations reaction.md (German)

* New translations follow.md (German)

* New translations pages.md (German)

* New translations custom-emoji.md (German)

* New translations follow.md (German)

* New translations custom-emoji.md (English)

* New translations deck.md (German)

* New translations mfm.md (German)

* New translations create-plugin.md (German)

* New translations deck.md (German)

* New translations create-plugin.md (German)

* New translations create-plugin.md (English)

* New translations create-plugin.md (German)

* New translations keyboard-shortcut.md (German)

* New translations keyboard-shortcut.md (German)

* New translations create-plugin.md (German)

* New translations keyboard-shortcut.md (German)

* New translations create-plugin.md (German)

* New translations create-plugin.md (German)

* New translations reversi-bot.md (German)

* New translations create-plugin.md (English)

* New translations reversi-bot.md (English)

* New translations create-plugin.md (German)

* New translations reversi-bot.md (German)

* New translations reversi-bot.md (German)

* New translations stream.md (German)

* New translations reversi-bot.md (English)

* New translations reversi-bot.md (German)

* New translations reversi-bot.md (German)

* New translations reversi-bot.md (German)

* New translations reversi-bot.md (English)

* New translations reversi-bot.md (German)

* New translations reversi-bot.md (English)

* New translations reversi-bot.md (German)

* New translations reversi-bot.md (German)

* New translations reversi-bot.md (German)

* New translations pages.md (Chinese Simplified)

* New translations pages.md (Chinese Simplified)

* New translations pages.md (Chinese Simplified)

* New translations pages.md (Chinese Simplified)

* New translations reaction.md (Chinese Simplified)

* New translations reaction.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations ja-JP.yml (German)

* New translations api.md (German)

* New translations keyboard-shortcut.md (German)

* New translations api.md (German)

* New translations stream.md (German)

* New translations reversi-bot.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations ja-JP.yml (German)

* New translations reversi-bot.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

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

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

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

* New translations stream.md (German)

* New translations ja-JP.yml (German)

* New translations reversi-bot.md (German)

* New translations stream.md (German)

* New translations stream.md (German)

* New translations stream.md (German)

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

* New translations stream.md (German)

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

* New translations stream.md (German)

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

* New translations stream.md (German)

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

* New translations stream.md (English)

* New translations stream.md (German)

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

* New translations stream.md (German)

* New translations stream.md (German)

* New translations stream.md (German)

* New translations stream.md (English)

* New translations stream.md (German)

* New translations api.md (German)

* New translations api.md (English)

* New translations api.md (German)

* New translations api.md (German)

* New translations api.md (English)

* New translations api.md (German)

* New translations api.md (German)

* New translations api.md (German)

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

* New translations create-plugin.md (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 reversi-bot.md (Chinese Simplified)

* New translations reversi-bot.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations ja-JP.yml (Kabyle)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* 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 (Polish)

* New translations custom-emoji.md (Polish)

* New translations mute.md (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations follow.md (Polish)

* New translations ja-JP.yml (Polish)

* New translations create-plugin.md (Polish)

* New translations reaction.md (Polish)

* New translations theme.md (Polish)

* New translations timelines.md (Polish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Polish)

* New translations aiscript.md (Polish)

* New translations keyboard-shortcut.md (Polish)

* New translations pages.md (Polish)

* New translations reversi-bot.md (Polish)

* New translations stream.md (Polish)

* New translations theme.md (Polish)

* New translations ja-JP.yml (Polish)

* New translations timelines.md (Polish)

* New translations deck.md (Polish)

* New translations ja-JP.yml (Indonesian)

* New translations aiscript.md (Indonesian)

* New translations api.md (Indonesian)

* New translations create-plugin.md (Indonesian)

* New translations custom-emoji.md (Indonesian)

* New translations follow.md (Indonesian)

* New translations keyboard-shortcut.md (Indonesian)

* New translations mute.md (Indonesian)

* New translations pages.md (Indonesian)

* New translations reaction.md (Indonesian)

* New translations reversi-bot.md (Indonesian)

* New translations stream.md (Indonesian)

* New translations theme.md (Indonesian)

* New translations timelines.md (Indonesian)

* New translations deck.md (Indonesian)

* New translations mfm.md (Indonesian)

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

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

* New translations ja-JP.yml (Indonesian)

* New translations keyboard-shortcut.md (Indonesian)

* New translations ja-JP.yml (Indonesian)

* New translations reaction.md (Indonesian)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Indonesian)

* New translations mute.md (Indonesian)

* New translations ja-JP.yml (Indonesian)

* New translations custom-emoji.md (Indonesian)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (Indonesian)
2021-01-04 17:38:53 +09:00
syuilo
8f2cf066b6 ジョブキューウィジェット追加 2021-01-04 17:36:50 +09:00
syuilo
e3e5a4272b Fix search.ts 2021-01-03 23:58:24 +09:00
syuilo
2dcb3af70f Improve usability 2021-01-03 23:52:09 +09:00
syuilo
3a19e9f80e 🎨 2021-01-03 23:49:49 +09:00
syuilo
c6fe798092 サーバー情報ウィジェット 2021-01-03 22:38:32 +09:00
MeiMei
e74a47916d Fix CSS injection at MFM speed (#7051) 2021-01-03 12:51:50 +09:00
takonomura
761071e1ce チャンネルページから別チャンネルページに移動時にタイムラインが切り替わらないのを修正 (#7050) 2021-01-02 23:08:16 +09:00
marihachi
9e3610d513 hpml refactoring (#7047) 2021-01-02 23:03:15 +09:00
syuilo
393ac6c203 noteコンポーネントを二つに分けたり 2021-01-01 22:41:20 +09:00
syuilo
b7a79d25b0 Update welcome.entrance.a.vue 2021-01-01 01:31:29 +09:00
syuilo
486f045751 12.65.7 2020-12-31 19:47:15 +09:00
syuilo
d6e3ec3218 New Crowdin updates (#7036)
* New translations pages.md (English)

* New translations pages.md (English)

* New translations pages.md (English)

* New translations deck.md (English)

* New translations deck.md (English)

* New translations deck.md (English)

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

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

* New translations keyboard-shortcut.md (English)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (English)

* New translations keyboard-shortcut.md (English)

* New translations api.md (English)

* New translations create-plugin.md (English)

* New translations stream.md (English)

* New translations deck.md (English)

* New translations api.md (English)

* New translations reversi-bot.md (English)

* New translations reversi-bot.md (English)

* New translations reversi-bot.md (English)

* New translations reversi-bot.md (English)

* New translations stream.md (English)

* New translations reversi-bot.md (English)

* New translations reversi-bot.md (English)

* New translations stream.md (English)

* New translations api.md (English)

* New translations stream.md (English)

* New translations create-plugin.md (English)

* New translations create-plugin.md (English)

* New translations create-plugin.md (English)

* New translations create-plugin.md (English)

* New translations create-plugin.md (English)

* New translations aiscript.md (English)

* New translations create-plugin.md (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (Kannada)

* New translations ja-JP.yml (Kabyle)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (English)

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

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

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

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

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

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

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations api.md (English)

* New translations api.md (English)

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

* New translations api.md (English)

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

* New translations api.md (English)

* New translations stream.md (English)

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

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

* New translations stream.md (English)

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

* New translations stream.md (English)

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

* New translations stream.md (English)

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

* New translations stream.md (English)

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

* New translations stream.md (English)

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

* New translations stream.md (English)

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

* New translations reaction.md (Chinese Traditional)

* New translations stream.md (English)

* New translations stream.md (English)

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

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

* New translations stream.md (English)

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

* New translations stream.md (English)

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

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

* New translations timelines.md (Chinese Simplified)

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

* New translations create-plugin.md (Chinese Simplified)

* New translations follow.md (Chinese Simplified)

* New translations timelines.md (Chinese Simplified)

* New translations deck.md (Chinese Simplified)

* New translations follow.md (Chinese Simplified)

* New translations deck.md (Chinese Simplified)

* New translations mfm.md (Chinese Simplified)

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

* New translations follow.md (Chinese Simplified)

* New translations keyboard-shortcut.md (Chinese Simplified)

* New translations ja-JP.yml (Russian)

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

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Thai)

* New translations aiscript.md (Thai)

* New translations api.md (Thai)

* New translations create-plugin.md (Thai)

* New translations custom-emoji.md (Thai)

* New translations follow.md (Thai)

* New translations keyboard-shortcut.md (Thai)

* New translations mute.md (Thai)

* New translations pages.md (Thai)

* New translations reaction.md (Thai)

* New translations reversi-bot.md (Thai)

* New translations stream.md (Thai)

* New translations theme.md (Thai)

* New translations timelines.md (Thai)

* New translations deck.md (Thai)

* New translations mfm.md (Thai)

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

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

* New translations keyboard-shortcut.md (Chinese Simplified)

* New translations keyboard-shortcut.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations keyboard-shortcut.md (Chinese Simplified)

* New translations keyboard-shortcut.md (Chinese Simplified)

* New translations keyboard-shortcut.md (Chinese Simplified)

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

* New translations custom-emoji.md (Chinese Simplified)

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

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

* New translations mute.md (Chinese Simplified)

* New translations pages.md (Chinese Simplified)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

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

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Chinese Traditional)
2020-12-31 19:45:30 +09:00
syuilo
078b043cae Add font MFM syntax 2020-12-31 19:42:23 +09:00
syuilo
296cbc2e5a 🎨 2020-12-31 19:10:38 +09:00
syuilo
a74beaac36 🎨 2020-12-31 19:01:12 +09:00
syuilo
79f8eb910f Update boot.js 2020-12-31 17:32:46 +09:00
syuilo
c91eef0030 12.65.6 2020-12-31 12:17:12 +09:00
syuilo
7bf9d726d0 ピン留めされたノートを詳細表示しないように 2020-12-31 12:14:56 +09:00
syuilo
1946ff8ed4 Improve note auto collapse algorithm 2020-12-31 12:12:24 +09:00
syuilo
b4d85d4f69 🎨 2020-12-31 11:46:07 +09:00
syuilo
a03702d2bd Better note auto collapse algorithm 2020-12-31 11:45:58 +09:00
syuilo
71d7de4989 Update vue 🚀 2020-12-31 11:45:35 +09:00
syuilo
35d9e13dbb Improve emoji picker usability 2020-12-31 11:45:27 +09:00
syuilo
b427842679 12.65.5 2020-12-31 03:04:24 +09:00
syuilo
4ae172be57 Add blur MFM syntax 2020-12-31 03:02:09 +09:00
syuilo
e6705b1a65 Add MFM syntax 2020-12-31 02:51:51 +09:00
syuilo
bc22cabdb5 🎨 2020-12-31 02:43:24 +09:00
syuilo
7128b9f16a 🎨 2020-12-31 00:22:20 +09:00
syuilo
8c0490fef1 インスタンス情報に統計追加したり 2020-12-31 00:11:06 +09:00
syuilo
882a81636d 🎨 2020-12-30 19:26:47 +09:00
syuilo
24b9be76ba 🎨 2020-12-30 19:24:01 +09:00
syuilo
e763c6e661 プロモーション増殖バグ修正 2020-12-30 17:31:59 +09:00
syuilo
899e2c73d7 12.65.4 2020-12-30 13:14:02 +09:00
syuilo
10cb15b000 nanka iroiro 2020-12-30 13:07:16 +09:00
syuilo
873d4bd707 clean up 2020-12-30 11:08:41 +09:00
syuilo
97dea72c94 サイトのタイトルが変わらない問題を修正 2020-12-30 11:01:33 +09:00
syuilo
7d49f260b8 12.65.3 2020-12-30 10:17:51 +09:00
syuilo
3055e6d8c7 localeとthemeのキャッシュクリア実装 2020-12-30 10:15:16 +09:00
syuilo
2c93246860 Tweak note collapse threshold 2020-12-30 10:03:57 +09:00
syuilo
360c820b9d Resolve #3620 2020-12-30 09:58:57 +09:00
syuilo
87847c6ed5 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-12-30 09:27:23 +09:00
syuilo
4874f54d4d 🎨 2020-12-30 09:27:19 +09:00
dependabot-preview[bot]
ff73efcc08 Merge pull request #7040 from syuilo/dependabot/npm_and_yarn/vue-color-2.8.1 2020-12-29 20:30:32 +00:00
dependabot-preview[bot]
4ee64cbd9e Merge pull request #7039 from syuilo/dependabot/npm_and_yarn/typescript-4.1.3 2020-12-29 20:30:29 +00:00
dependabot-preview[bot]
0c40a86fca Bump vue-color from 2.7.1 to 2.8.1
Bumps [vue-color](https://github.com/xiaokaike/vue-color) from 2.7.1 to 2.8.1.
- [Release notes](https://github.com/xiaokaike/vue-color/releases)
- [Commits](https://github.com/xiaokaike/vue-color/compare/v2.7.1...v2.8.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-29 20:22:07 +00:00
dependabot-preview[bot]
f92eed0549 Bump typescript from 4.1.2 to 4.1.3
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.1.2...v4.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-29 20:22:00 +00:00
dependabot-preview[bot]
69ed8cc409 Merge pull request #7038 from syuilo/dependabot/npm_and_yarn/re2-1.15.9 2020-12-29 20:21:08 +00:00
dependabot-preview[bot]
4fff1279db Bump re2 from 1.15.8 to 1.15.9
Bumps [re2](https://github.com/uhop/node-re2) from 1.15.8 to 1.15.9.
- [Release notes](https://github.com/uhop/node-re2/releases)
- [Commits](https://github.com/uhop/node-re2/compare/1.15.8...1.15.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-29 20:13:45 +00:00
syuilo
d80699e454 12.65.2 2020-12-29 22:01:49 +09:00
syuilo
deee1dbf53 New Crowdin updates (#7031)
* 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 (German)

* New translations ja-JP.yml (English)

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

* New translations deck.md (Chinese Simplified)

* New translations deck.md (Chinese Simplified)

* New translations deck.md (Chinese Simplified)

* New translations mfm.md (Chinese Simplified)

* New translations ja-JP.yml (Italian)

* New translations aiscript.md (Haitian Creole)

* New translations deck.md (Haitian Creole)

* New translations timelines.md (Haitian Creole)

* New translations theme.md (Haitian Creole)

* New translations stream.md (Haitian Creole)

* New translations reversi-bot.md (Haitian Creole)

* New translations reaction.md (Haitian Creole)

* New translations pages.md (Haitian Creole)

* New translations mute.md (Haitian Creole)

* New translations keyboard-shortcut.md (Haitian Creole)

* New translations follow.md (Haitian Creole)

* New translations custom-emoji.md (Haitian Creole)

* New translations create-plugin.md (Haitian Creole)

* New translations api.md (Haitian Creole)

* New translations ja-JP.yml (Haitian Creole)

* New translations aiscript.md (Italian)

* New translations mfm.md (Italian)

* New translations deck.md (Italian)

* New translations timelines.md (Italian)

* New translations theme.md (Italian)

* New translations stream.md (Italian)

* New translations reversi-bot.md (Italian)

* New translations reaction.md (Italian)

* New translations pages.md (Italian)

* New translations mute.md (Italian)

* New translations keyboard-shortcut.md (Italian)

* New translations follow.md (Italian)

* New translations custom-emoji.md (Italian)

* New translations create-plugin.md (Italian)

* New translations api.md (Italian)

* New translations mfm.md (Haitian Creole)
2020-12-29 22:00:52 +09:00
syuilo
067588845b UI tweaks 2020-12-29 22:00:02 +09:00
syuilo
b177b0cafc タイムラインの上に設置した投稿フォームでコンテキストメニューが表示できない問題を修正 2020-12-29 21:18:35 +09:00
syuilo
5679777010 Improve usability 2020-12-29 21:08:16 +09:00
syuilo
a70c24cd1e テキストを選択した状態でノートのコンテキストメニューが開けない問題を修正 2020-12-29 21:08:08 +09:00
syuilo
357f3dd258 12.65.1 2020-12-29 17:45:57 +09:00
syuilo
d815838762 カラムのフレキシブル設定を上下分割されていても適用するように 2020-12-29 17:40:53 +09:00
syuilo
4ceef78599 モバイルの下のやつ改修 2020-12-29 17:31:46 +09:00
syuilo
d5811633ea タイムラインの上のバーを調整 2020-12-29 17:11:12 +09:00
syuilo
32265da72c LTL/GTLの有効状態を反映 2020-12-29 16:58:14 +09:00
syuilo
ef94236e7f Fix doc link 2020-12-29 16:45:35 +09:00
syuilo
2924f0e434 12.65.0 2020-12-29 11:56:03 +09:00
syuilo
1e419a9e1f New Crowdin updates (#7027)
* New translations ja-JP.yml (Chinese Traditional)

* New translations follow.md (English)

* New translations timelines.md (English)

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

* New translations follow.md (English)

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

* New translations custom-emoji.md (English)

* New translations mfm.md (English)

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

* New translations theme.md (English)

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

* New translations ja-JP.yml (English)

* New translations theme.md (English)

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

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

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

* New translations theme.md (English)

* New translations theme.md (English)

* New translations theme.md (English)

* New translations theme.md (English)

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

* New translations reaction.md (English)

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

* New translations reaction.md (English)

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

* New translations mute.md (English)

* New translations reaction.md (English)

* New translations mute.md (English)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)
2020-12-29 11:55:03 +09:00
syuilo
097ece9dc9 ✌️ 2020-12-29 11:54:26 +09:00
syuilo
6b6483c9fc タイトルバー非表示オプション 2020-12-29 11:47:36 +09:00
syuilo
22049b10ff Improve timeline page 2020-12-29 11:33:21 +09:00
syuilo
de86644cb6 fix ui 2020-12-29 10:31:04 +09:00
syuilo
657aac5bc5 失敗するのでlatestに 2020-12-29 07:23:04 +09:00
dependabot-preview[bot]
7ae3640d1a Merge pull request #7029 from syuilo/dependabot/npm_and_yarn/webpack-cli-4.3.0 2020-12-28 20:31:26 +00:00
dependabot-preview[bot]
6fb42857c6 Bump webpack-cli from 4.2.0 to 4.3.0
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.2.0...webpack-cli@4.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-28 20:23:15 +00:00
dependabot-preview[bot]
abf21349cd Merge pull request #7028 from syuilo/dependabot/npm_and_yarn/types/ratelimiter-3.4.1 2020-12-28 20:22:54 +00:00
dependabot-preview[bot]
ed17af8339 Bump @types/ratelimiter from 3.4.0 to 3.4.1
Bumps [@types/ratelimiter](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ratelimiter) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ratelimiter)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-28 20:13:42 +00:00
syuilo
67f391f6d4 12.64.2 2020-12-28 23:41:54 +09:00
syuilo
c6b25f317e locale migration 2020-12-28 23:41:41 +09:00
syuilo
d91fa1a37a fix bug 2020-12-28 23:41:09 +09:00
283 changed files with 13593 additions and 2773 deletions

View File

@@ -4,7 +4,7 @@ executors:
docker:
working_directory: /tmp/workspace
docker:
- image: docker:12.10.1
- image: docker:latest
jobs:
docker:

View File

@@ -1,18 +0,0 @@
name: Docker build
on:
push:
branches:
- master
- develop
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Pull previous build result (for cache)
run: docker pull misskey/misskey:latest
- name: Build docker container
run: docker build --cache-from misskey/misskey:latest -t misskey/misskey .

View File

@@ -62,6 +62,8 @@ Organize and store your files! Want to post a picture you have already uploaded?
...and more! Experience Misskey with your own eyes at [misskey.io](https://misskey.io/) or join one of the [other instances](https://joinmisskey.github.io/) that are available.
To recive updates of this repo, follow [@repo@misskey.io](https://misskey.io/@repo) on fediverse.
Screen shots
----------------------------------------------------------------
### Profile page

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(0.413372,0,0,0.469741,64.564,40.5821)">
<rect x="-156.189" y="-86.393" width="619.297" height="544.981" style="fill:rgb(27,30,31);"/>
</g>
<g transform="matrix(0.898356,0,0,0.898356,-130.722,-120.968)">
<g transform="matrix(0.5,0.866025,-0.866025,0.5,288,-166.277)">
<path d="M390.877,136.653C389.457,134.193 386.831,132.677 383.99,132.677C381.149,132.677 378.524,134.193 377.103,136.653C373.093,143.599 368.146,152.168 364.604,158.303C361.749,163.248 361.749,169.34 364.604,174.285C368.142,180.414 373.084,188.972 377.092,195.915C378.515,198.379 381.144,199.898 383.99,199.898C386.836,199.898 389.466,198.379 390.889,195.915C394.897,188.972 399.838,180.414 403.377,174.284C406.232,169.34 406.232,163.248 403.377,158.303C399.835,152.168 394.888,143.599 390.877,136.653Z" style="fill:white;"/>
</g>
<g transform="matrix(1,0,0,1,-96,166.277)">
<path d="M390.877,136.653C389.457,134.193 386.831,132.677 383.99,132.677C381.149,132.677 378.524,134.193 377.103,136.653C373.093,143.599 368.146,152.168 364.604,158.303C361.749,163.248 361.749,169.34 364.604,174.285C368.142,180.414 373.084,188.972 377.092,195.915C378.515,198.379 381.144,199.898 383.99,199.898C386.836,199.898 389.466,198.379 390.889,195.915C394.897,188.972 399.838,180.414 403.377,174.284C406.232,169.34 406.232,163.248 403.377,158.303C399.835,152.168 394.888,143.599 390.877,136.653Z" style="fill:white;"/>
</g>
<g transform="matrix(0.5,-0.866025,0.866025,0.5,-96,498.831)">
<path d="M390.877,136.653C389.457,134.193 386.831,132.677 383.99,132.677C381.149,132.677 378.524,134.193 377.103,136.653C373.093,143.599 368.146,152.168 364.604,158.303C361.749,163.248 361.749,169.34 364.604,174.285C368.142,180.414 373.084,188.972 377.092,195.915C378.515,198.379 381.144,199.898 383.99,199.898C386.836,199.898 389.466,198.379 390.889,195.915C394.897,188.972 399.838,180.414 403.377,174.284C406.232,169.34 406.232,163.248 403.377,158.303C399.835,152.168 394.888,143.599 390.877,136.653Z" style="fill:white;"/>
</g>
<g transform="matrix(1,0,0,1,-95.9902,55.4086)">
<path d="M390.877,136.653C389.457,134.193 386.831,132.677 383.99,132.677C381.149,132.677 378.524,134.193 377.103,136.653C373.093,143.599 368.146,152.168 364.604,158.303C361.749,163.248 361.749,169.34 364.604,174.285C368.142,180.414 373.084,188.972 377.092,195.915C378.515,198.379 381.144,199.898 383.99,199.898C386.836,199.898 389.466,198.379 390.889,195.915C394.897,188.972 399.838,180.414 403.377,174.284C406.232,169.34 406.232,163.248 403.377,158.303C399.835,152.168 394.888,143.599 390.877,136.653ZM385.681,139.653C385.332,139.049 384.688,138.677 383.99,138.677C383.293,138.677 382.648,139.049 382.299,139.653C378.289,146.599 373.342,155.168 369.8,161.303C368.017,164.391 368.017,168.196 369.8,171.285C373.339,177.414 378.28,185.972 382.288,192.915C382.639,193.523 383.288,193.898 383.99,193.898C384.692,193.898 385.341,193.523 385.692,192.915C389.701,185.972 394.642,177.414 398.181,171.284C399.964,168.196 399.964,164.391 398.181,161.303L385.681,139.653Z" style="fill:rgb(150,208,74);"/>
</g>
<g transform="matrix(0.5,-0.866025,0.866025,0.5,-2.64322e-11,554.256)">
<path d="M390.877,136.653C389.457,134.193 386.831,132.677 383.99,132.677C381.149,132.677 378.524,134.193 377.103,136.653C373.093,143.599 368.146,152.168 364.604,158.303C361.749,163.248 361.749,169.34 364.604,174.285C368.142,180.414 373.084,188.972 377.092,195.915C378.515,198.379 381.144,199.898 383.99,199.898C386.836,199.898 389.466,198.379 390.889,195.915C394.897,188.972 399.838,180.414 403.377,174.284C406.232,169.34 406.232,163.248 403.377,158.303C399.835,152.168 394.888,143.599 390.877,136.653ZM385.681,139.653C385.332,139.049 384.688,138.677 383.99,138.677C383.293,138.677 382.648,139.049 382.299,139.653C378.289,146.599 373.342,155.168 369.8,161.303C368.017,164.391 368.017,168.196 369.8,171.285C373.339,177.414 378.28,185.972 382.288,192.915C382.639,193.523 383.288,193.898 383.99,193.898C384.692,193.898 385.341,193.523 385.692,192.915C389.701,185.972 394.642,177.414 398.181,171.284C399.964,168.196 399.964,164.391 398.181,161.303L385.681,139.653Z" style="fill:rgb(150,208,74);"/>
</g>
<g transform="matrix(0.5,0.866025,-0.866025,0.5,192,-110.851)">
<path d="M390.877,136.653C389.457,134.193 386.831,132.677 383.99,132.677C381.149,132.677 378.524,134.193 377.103,136.653C373.093,143.599 368.146,152.168 364.604,158.303C361.749,163.248 361.749,169.34 364.604,174.285C368.142,180.414 373.084,188.972 377.092,195.915C378.515,198.379 381.144,199.898 383.99,199.898C386.836,199.898 389.466,198.379 390.889,195.915C394.897,188.972 399.838,180.414 403.377,174.284C406.232,169.34 406.232,163.248 403.377,158.303C399.835,152.168 394.888,143.599 390.877,136.653ZM385.681,139.653C385.332,139.049 384.688,138.677 383.99,138.677C383.293,138.677 382.648,139.049 382.299,139.653C378.289,146.599 373.342,155.168 369.8,161.303C368.017,164.391 368.017,168.196 369.8,171.285C373.339,177.414 378.28,185.972 382.288,192.915C382.639,193.523 383.288,193.898 383.99,193.898C384.692,193.898 385.341,193.523 385.692,192.915C389.701,185.972 394.642,177.414 398.181,171.284C399.964,168.196 399.964,164.391 398.181,161.303L385.681,139.653Z" style="fill:rgb(150,208,74);"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
assets/mi-white.afdesign Normal file

Binary file not shown.

BIN
assets/mi.afdesign Normal file

Binary file not shown.

View File

@@ -50,11 +50,13 @@ server {
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_redirect off;
# If it's behind another reverse proxy or CDN, remove the following.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_http_version 1.1;
proxy_redirect off;
# For WebSocket
proxy_set_header Upgrade $http_upgrade;

View File

@@ -45,6 +45,7 @@ copyUsername: "انسخ اسم المستخدم"
searchUser: "ابحث عن مستخدمين"
reply: "رد"
loadMore: "عرض المزيد"
showMore: "عرض المزيد"
youGotNewFollower: "يتابعك"
receiveFollowRequest: "تلقيت طلب متابعة"
followRequestAccepted: "قُبل طلب المتابعة"
@@ -175,16 +176,23 @@ remove: "حذف"
removed: "تم حذفه بنجاح"
removeAreYouSure: "متأكد من أنك تريد حذف {x}؟"
deleteAreYouSure: "متأكد من أنك تريد حذف {x}؟"
resetAreYouSure: "هل تريد إعادة التعيين؟"
saved: "تم حفظه"
messaging: "الدردشة"
upload: "تحميل"
fromDrive: "من المخزن"
fromUrl: "من عنوان URL"
uploadFromUrl: "التحميل عبر URL"
uploadFromUrlDescription: "رابط الملف المراد تحميله "
uploadFromUrlRequested: "الرفع مطلوب"
uploadFromUrlMayTakeTime: "سيستغرق بعض الوقت لاتمام الرفع "
explore: "استكشاف"
games: "ألعاب Misskey"
messageRead: "مقروءة"
noMoreHistory: "لا يوجد المزيد من التاريخ"
startMessaging: "ابدأ الدردشة"
nUsersRead: "تمت القراءة من {n}"
agreeTo: "اوافق على {0}"
tos: "شروط الخدمة"
start: "البداية"
home: "الرئيسي"
@@ -201,6 +209,7 @@ light: "فاتح"
dark: "داكن"
lightThemes: "الحلة الفاتحة"
darkThemes: "الحلة الداكنة"
syncDeviceDarkMode: "مطابقة الوضع المضلمومع اعدادات الجهاز"
drive: "قرص التخرين"
fileName: "اسم الملف"
selectFile: "اختر ملفًا"
@@ -218,14 +227,17 @@ emptyFolder: "هذا المجلد فارغ"
unableToDelete: "لا يمكن حذفه"
inputNewFileName: "ادخل الإسم الجديد للملف"
inputNewFolderName: "ادخل الإسم الجديد للمجلد"
hasChildFilesOrFolders: "الان الملف غير فارغ. لا يمكن حذفه"
copyUrl: "انسخ عنوان URL"
rename: "إعادة التسمية"
avatar: "الصورة الرمزية"
banner: "الصورة الرأسية"
nsfw: "محتوى حساس"
whenServerDisconnected: "عند فقدان الاتصال بالخادم"
disconnectedFromServer: "قُطِع الإتصال بالخادم"
reload: "انعش"
doNothing: "تجاهل"
reloadConfirm: "هل ترغب في تحديث الجدول الزمني؟"
watch: "راقب"
unwatch: "إلغاء المراقبة"
accept: "السماح"
@@ -243,10 +255,12 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "الصفحات"
integration: "دمج"
connectSerice: "أوصل"
disconnectSerice: "قطع الاتصال"
enableLocalTimeline: "تفعيل الخيط المحلي"
enableGlobalTimeline: "تفعيل الخيط الزمني الشامل"
disablingTimelinesInfo: "سيتمكن المسؤولون ومن تعديل دائمًا و من الوصول إلى جميع المخططات الزمنية ، حتى إذا لم يتم تمكينها."
registration: "إنشاء حساب"
enableRegistration: "تفعيل إنشاء الحسابات الجديدة"
invite: "دعوة"
@@ -288,6 +302,8 @@ resetPassword: "أعد تعيين كلمتك السرية"
newPasswordIs: "كلمتك السرية الجديدة هي {password}"
share: "شارِك"
notFound: "غير موجود"
cacheClear: "مسح ذاكرة التخزين المؤقت"
markAsReadAllNotifications: "وضع جميع الإشعارات كأنها مقروءة"
help: "المساعدة"
inputMessageHere: "اكتب رسالتك هنا"
close: "اغلق"
@@ -328,6 +344,7 @@ aboutX: "عن {x}"
useOsNativeEmojis: "استخدم الإيموجيات الخاصة بنظام التشغيل"
youHaveNoGroups: "لا تمتلك أية فِرَق"
noHistory: "السجل فارغ"
signinHistory: "تاريخ تسجيل الدخول"
doing: "انتظر لحظة"
category: "الفئات"
tags: "الوسوم"
@@ -336,6 +353,7 @@ createAccount: "أنشئ حسابًا"
existingAcount: "الحسابات الموجودة"
regenerate: "أعِد التوليد"
fontSize: "حجم الخط"
openImageInNewTab: "إفتح الصورة بصفحة جديدة"
dashboard: "لوحة التحكم"
local: "المحلي"
remote: "بُعدي"
@@ -364,6 +382,9 @@ state: "الحالة"
sort: "ترتيب حسب"
output: "الخارجة"
updateRemoteUser: "تحديث المعلومات عن المستخدم البعيد"
deleteAllFiles: "حذف كافة الملفات"
userSuspended: "تم تعليق هذا المستخدم."
userSilenced: "تم إسكات هذا المستخدم."
sidebar: "الشريط الجانبي"
addItem: "إضافة عنصر"
rooms: "الغرفة"
@@ -373,14 +394,35 @@ addedRelays: "المرحلات التي تم إضافتها"
deletedNote: "ملاحظة محذوفة"
invisibleNote: "ملاحظة مخفية"
poll: "استطلاع رأي"
useCw: "إخفاء المحتوى"
themeEditor: "مصمم القوالب"
manage: "إدارة "
plugins: "الإضافات"
pluginInstallWarn: "يرجى تنصيب إضافات ذات مصدر موثوق منه فقط."
width: "العرض"
height: "الإرتفاع"
large: "كبير"
medium: "متوسط"
small: "صغير"
permission: "أذونات"
enableAll: "تشغيل الكل"
disableAll: "تعطيل الكل"
tokenRequested: "منح حق الوصول إلى الحساب"
notificationType: "أنواع الإشعارات"
edit: "التعديل"
email: "البريد الإلكتروني "
emailAddress: "عنوان البريد الالكتروني"
smtpHost: "المضيف"
smtpPort: "المنفذ"
smtpUser: "اسم المستخدم"
smtpPass: "الكلمة السرية"
makeActive: "تفعيل"
display: "المظهر"
copy: "نسخ"
metrics: "المقاييس"
public: "للعامة"
currentVersion: "الإصدار الحالي"
latestVersion: "آخر نسخة مستقرة"
_mfm:
mention: "أشر الى"
quote: "اقتبس"
@@ -453,6 +495,7 @@ _widgets:
activity: "النشاط"
photos: "الصور"
federation: "الفديرالية"
jobQueue: "قائمة الانتظار"
_cw:
hide: "إخفاء"
show: "عرض المزيد"

View File

@@ -1,5 +1,6 @@
---
_lang_: "Deutsch"
headlineMisskey: "Durch Notizen verbundenes Netzwerk"
introMisskey: "Willkommen! Misskey ist eine dezentralisierte Open-Source Microblogging-Platform.\nVerfasse \"Notizen\" um mitzuteilen, was gerade passiert oder um Ereignisse mit Anderen zu teilen. 📡\nMit \"Reaktionen\" kannst du außerdem schnell deine Gefühle über Notizen anderer Benutzer zum Ausdruck bringen. 👍\nLass uns eine neue Welt erforschen! 🚀"
monthAndDay: "{day}/{month}"
search: "Suchen"
@@ -49,6 +50,7 @@ copyUsername: "Benutzernamen kopieren"
searchUser: "Benutzersuche"
reply: "Antworten"
loadMore: "Mehr anzeigen"
showMore: "Mehr anzeigen"
youGotNewFollower: "Du hast einen neuen Follower"
receiveFollowRequest: "Follow-Anfrage erhalten"
followRequestAccepted: "Follow-Anfrage akzeptiert"
@@ -182,7 +184,7 @@ clearCachedFiles: "Cache leeren"
clearCachedFilesConfirm: "Sollen alle im Cache gespeicherten Dateien von anderen Instanzen wirklich gelöscht werden?"
blockedInstances: "Blockierte Instanzen"
blockedInstancesDescription: "Gib den Hostnamen der Instanz an, die blockiert werden soll. Blockierte Instanzen können nicht mehr mit dieser kommunizieren."
muteAndBlock: "Stummgeschaltet / Blockiert"
muteAndBlock: "Stummschaltungen / Blockierungen"
mutedUsers: "Stummgeschaltete Benutzer"
blockedUsers: "Blockierte Benutzer"
noUsers: "Keine Benutzer"
@@ -243,7 +245,7 @@ nUsersRead: "Von {n} gelesen"
agreeTo: "Ich stimme {0} zu"
tos: "Nutzungsbedingungen"
start: "Anfangen"
home: "Home"
home: "Startseite"
remoteUserCaution: "Diese Informationen sind möglicherweise veraltet, da der Benutzer von einer anderen Instanz stammt."
activity: "Aktivität"
images: "Bilder"
@@ -316,7 +318,7 @@ enableRegistration: "Registration neuer Benutzer erlauben"
invite: "Einladen"
proxyRemoteFiles: "Dateien anderer Instanzen durch Proxy leiten"
proxyRemoteFilesDescription: "Wenn diese Einstellung aktiviert ist, dann werden Dateien von anderen Instanzen, welche entweder nicht lokal gespeichert sind oder durch Überschreiten des Speicherlimits gelöscht wurden, durch einen Proxy geleitet. Hierbei wird auch ein Vorschaubild generiert. \n Dies hat keinen Effekt auf den Speicherplatz des Servers."
driveCapacityPerLocalAccount: "Drivekapazität pro lokales Benutzerkonto"
driveCapacityPerLocalAccount: "Drive-Kapazität pro lokales Benutzerkonto"
driveCapacityPerRemoteAccount: "Drive-Kapazität pro Benutzer anderer Instanzen"
inMb: "In Megabytes"
iconUrl: "Icon-URL"
@@ -491,7 +493,7 @@ showFixedPostForm: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik an
newNoteRecived: "Es gibt neue Notizen"
sounds: "Töne"
listen: "Anhören"
none: "Keine"
none: "Nichts"
showInPage: "In Seite anzeigen"
popout: "Pop-Up"
volume: "Lautstärke"
@@ -536,7 +538,7 @@ invisibleNote: "Private Notiz"
enableInfiniteScroll: "Automatisch mehr Notizen laden"
visibility: "Sichtbarkeit"
poll: "Umfrage"
useCw: "Inhalt verstecken"
useCw: "Inhalt verdecken"
enablePlayer: "Video-Player öffnen"
disablePlayer: "Video-Player schließen"
expandTweet: "Tweet ausklappen"
@@ -562,7 +564,7 @@ enableAll: "Alle aktivieren"
disableAll: "Alle deaktivieren"
tokenRequested: "Benutzerkontozugriff gewähren"
pluginTokenRequestedDescription: "Dieses Plugin wird die hier konfigurierten Berechtigungen verwenden können."
notificationType: "Benachrichtigungstyp"
notificationType: "Benachrichtigungsart"
edit: "Bearbeiten"
useStarForReactionFallback: "Verwende ★ falls das Reaktions-Emoji unbekannt ist"
emailConfig: "Email-Server Konfiguration"
@@ -625,7 +627,7 @@ createNew: "Neu erstellen"
optional: "Optional"
createNewClip: "Neuen Clip erstellen"
public: "Öffentlich"
i18nInfo: "Misskey wird durch freiwillige Helfer in viele verschiedene Sprachen übersetzt. Unter {link} kannst du mithelfen."
i18nInfo: "Misskey wird durch freiwillige Helfer in viele verschiedene Sprachen übersetzt. Auf {link} kannst du mithelfen."
manageAccessTokens: "Zugriffstoken verwalten"
accountInfo: "Benutzerkonto-Informationen"
notesCount: "Anzahl von Notizen"
@@ -669,7 +671,33 @@ left: "Links"
center: "Mitte"
wide: "Breit"
narrow: "Schmal"
reloadToApplySetting: "Einstellungen treten nach einer Aktualisierung der Seite in Kraft. Jetzt aktualisieren?"
reloadToApplySetting: "Diese Einstellung tritt nach einer Aktualisierung der Seite in Kraft. Jetzt aktualisieren?"
showTitlebar: "Titelleiste anzeigen"
clearCache: "Cache leeren"
onlineUsersCount: "{n} Benutzer sind online"
nUsers: "{n} Benutzer"
nNotes: "{n} Notizen"
sendErrorReports: "Fehlerberichte senden"
sendErrorReportsDescription: "Ist diese Option aktiviert, so werden beim Auftreten von Fehlern detaillierte Fehlerinformationen an Misskey weitergegeben, was zur Verbesserung der Qualität von Misskey beiträgt."
myTheme: "Mein Farbthema"
backgroundColor: "Hintergrund"
accentColor: "Akzentfarbe"
textColor: "Text"
saveAs: "Speichern als…"
advanced: "Fortgeschritten"
value: "Wert"
updatedAt: "Zuletzt geändert am"
saveConfirm: "Änderungen speichern?"
deleteConfirm: "Wirklich löschen?"
invalidValue: "Ungültiger Wert."
registry: "Registry"
closeAccount: "Benutzerkonto schließen"
_registry:
scope: "Scope"
key: "Schlüssel"
keys: "Schlüssel"
domain: "Domain"
createKey: "Schlüssel erstellen"
_aboutMisskey:
about: "Misskey ist Open-Source-Software die von syuilo seit 2014 entwickelt wird."
contributors: "Hauptmitwirkende"
@@ -692,7 +720,7 @@ _mfm:
hashtag: "Hashtag"
hashtagDescription: "Mit einer Raute und Text kann ein Hashtag angegeben werden."
url: "URL"
urlDescription: "URLs können angezeigt werden."
urlDescription: "Zeigt URLs an."
link: "Link"
linkDescription: "Ein spezifizierter Textabschnitt kann als URL angezeigt werden."
bold: "Fett"
@@ -731,6 +759,16 @@ _mfm:
twitchDescription: "Verleiht eine sehr stark zuckende Animation."
spin: "Animation (Rotieren)"
spinDescription: "Verleiht eine rotierende Animation."
x2: "Groß"
x2Description: "Lässt Inhalte größer angezeigt werden."
x3: "Sehr groß"
x3Description: "Lässt Inhalte noch größer angezeigt werden."
x4: "Am größten"
x4Description: "Lässt Inhalte noch größer als größer als groß angezeigt werden."
blur: "Weichzeichner"
blurDescription: "Inhalte durch Weihzeichnung verschwimmen lassen. Durch das Bewegen des Mauszeigers auf den Inhalt wird er klar angezeigt."
font: "Schriftart"
fontDescription: "Setzt die Schriftart des Inhaltes fest."
_reversi:
reversi: "Reversi"
gameSettings: "Spieleinstellungen"
@@ -815,7 +853,7 @@ _theme:
refConst: "Konstante referenzieren"
key: "Schlüssel"
func: "Funktionen"
funcKind: "Funktionstyp"
funcKind: "Funktionsart"
argument: "Parameter"
basedProp: "Referenzierte Eigenschaft"
alpha: "Transparenz"
@@ -852,9 +890,9 @@ _theme:
infoFg: "Text von Informationen"
infoWarnBg: "Hintergrund von Warnungen"
infoWarnFg: "Text von Informationen"
cwBg: "Hintergrund von versteckten Inhalten"
cwFg: "Text von versteckten Inhalten"
cwHoverBg: "Hintergrund von versteckten Inhalten (Mouseover)"
cwBg: "Hintergrund von verdeckten Inhalten"
cwFg: "Text von verdeckten Inhalten"
cwHoverBg: "Hintergrund von verdeckten Inhalten (Mouseover)"
toastBg: "Hintergrund von Benachrichtigungen"
toastFg: "Text von Benachrichtigungen"
buttonBg: "Hintergrund von Schaltflächen"
@@ -898,7 +936,7 @@ _tutorial:
title: "Wie du Misskey verwendest"
step1_1: "Willkommen!"
step1_2: "Diese Seite ist die \"Chronik\". Sie zeigt dir deine geschrieben \"Notizen\" sowie die aller Benutzer, denen du \"folgst\" in chronologischer Reihenfolge."
step1_3: "Deine Chronik sollte momentan leer sein, da du bis jetzt nocht keine Notizen geschrieben hast und auch noch keinen Benutzern folgst."
step1_3: "Deine Chronik sollte momentan leer sein, da du bis jetzt noch keine Notizen geschrieben hast und auch noch keinen Benutzern folgst."
step2_1: "Lass uns zuerst dein Profil vervollständigen, bevor du Notizen schreibst oder jemandem folgst."
step2_2: "Informationen darüber, wer du bist, macht es anderen leichter zu wissen, ob sie deine Notizen sehen wollen und ob sie dir folgen möchten."
step3_1: "Mit dem Einrichten deines Profils fertig?"
@@ -991,6 +1029,9 @@ _widgets:
postForm: "Neue Notiz anfertigen"
slideshow: "Diashow"
button: "Knopf"
onlineUsers: "Benutzer Online"
jobQueue: "Job-Warteschlange"
serverMetric: "Servermetriken"
_cw:
hide: "Ausblenden"
show: "Mehr anzeigen"
@@ -1100,7 +1141,7 @@ _rooms:
clearConfirm: "Möchtest du wirklich alle Möbel entfernen?"
leaveConfirm: "Es gibt ungespeicherte Änderungen. Möchtest du wirklich gehen?"
chooseImage: "Bild auswählen"
roomType: "Raumtyp"
roomType: "Raumart"
carpetColor: "Teppichfarbe"
_roomType:
default: "Standard"
@@ -1480,8 +1521,9 @@ _deck:
swapRight: "Nach rechts verschieben"
swapUp: "Nach oben verschieben"
swapDown: "Nach unten verschieben"
stackLeft: "Nach links stapeln"
stackLeft: "Auf linke Spalte stapeln"
popRight: "Nach rechts vom Stapel nehmen"
profile: "Profil"
_columns:
main: "Hauptspalte"
widgets: "Widgets"

View File

@@ -1,6 +1,7 @@
---
_lang_: "English"
introMisskey: "Welcome! Misskey is an open source, and also a decentralized microblogging service.\nCreate \"notes\" to share what is happening now, or to share it with everyone around you 📡\nWith \"reactions\", you can also quickly express your feelings about everyone's notes 👍\nLet's explore a new world 🚀"
headlineMisskey: "Network connected by notes"
introMisskey: "Welcome! Misskey is an open source, decentralized microblogging service.\nCreate \"notes\" to share what is happening now, or to share it with everyone around you. 📡\nWith \"reactions\", you can also quickly express your feelings about everyone's notes. 👍\nLet's explore a new world! 🚀"
monthAndDay: "{month}/{day}"
search: "Search"
notifications: "Notifications"
@@ -49,6 +50,7 @@ copyUsername: "Copy username"
searchUser: "User search"
reply: "Reply"
loadMore: "Load more"
showMore: "Load more"
youGotNewFollower: "Followed you"
receiveFollowRequest: "Follow request received"
followRequestAccepted: "Follow request accepted"
@@ -66,7 +68,7 @@ exportRequested: "You have requested an export. This may take a while. After the
importRequested: "You requested an import. This may take a while."
lists: "Lists"
noLists: "You don't have any lists"
note: "Notes"
note: "Note"
notes: "Notes"
following: "Following"
followers: "Followers"
@@ -77,7 +79,7 @@ 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."
pageLoadErrorDescription: "This is normally caused by network errors or the browser's cache. Try clearing the cache and then try again after waiting a little while."
enterListName: "List name"
privacy: "Privacy"
makeFollowManuallyApprove: "Follow requests require approval"
@@ -251,7 +253,7 @@ birthday: "Birthday"
yearsOld: "{age} years old"
registeredDate: "Joined on"
location: "Location"
theme: "Theme"
theme: "Themes"
themeForLightMode: "Theme to use in Light Mode"
themeForDarkMode: "Theme to use in Dark Mode"
light: "Light"
@@ -312,7 +314,7 @@ enableLocalTimeline: "Enable local timeline"
enableGlobalTimeline: "Enable global timeline"
disablingTimelinesInfo: "Admins and Mods will always have access to all timelines, even if they are not enabled."
registration: "Register"
enableRegistration: "Enable new user registeration"
enableRegistration: "Enable new user registration"
invite: "Invite"
proxyRemoteFiles: "Proxy remote files"
proxyRemoteFilesDescription: "If enabled, remote files that (1) are not stored locally or (2) got deleted from exceeding storage limit will be locally proxied (with thumbnails). This does not affect the server's storage."
@@ -344,8 +346,8 @@ antennaSource: "Antenna source"
antennaKeywords: "Keywords to receive"
antennaExcludeKeywords: "Keywords to exclude"
antennaKeywordsDescription: "Separate with spaces for AND condition. Separate with line breaks for OR."
notifyAntenna: "Notify newer notes"
withFileAntenna: "Filter only notes with file attached"
notifyAntenna: "Notify for new notes"
withFileAntenna: "Filter only notes with file(s) attached"
serviceworker: "ServiceWorker"
enableServiceworker: "Enable ServiceWorker"
antennaUsersDescription: "List one username per line"
@@ -644,8 +646,8 @@ no: "No"
driveFilesCount: "Amount of drive files"
driveUsage: "Drive space usage"
noCrawle: "Reject crawler indexing"
noCrawleDescription: "Ask search engines not to index your profile page, notes, Pages, etc"
lockedAccountInfo: "Unless you set your note visiblity to \"Followers only\", your notes are visible to anyone even if you require followers to be manually approved."
noCrawleDescription: "Ask search engines not to index your profile page, notes, Pages, etc."
lockedAccountInfo: "Unless you set your note visiblity to \"Followers only\", your notes are visible to anyone, even if you require followers to be manually approved."
alwaysMarkSensitive: "Mark NSFW by default"
loadRawImages: "Display image attachments fully instead of thumbnails"
disableShowingAnimatedImages: "Don't play animated images"
@@ -669,7 +671,33 @@ left: "Left"
center: "Center"
wide: "Wide"
narrow: "Narrow"
reloadToApplySetting: "Settings will be applied upon page reload. Reload now?"
reloadToApplySetting: "This setting will be applied upon page reload. Reload now?"
showTitlebar: "Show title bar"
clearCache: "Clear cache"
onlineUsersCount: "{n} people are online"
nUsers: "{n} Users"
nNotes: "{n} Notes"
sendErrorReports: "Send error reports"
sendErrorReportsDescription: "When turned on, detailed error information will be shared with Misskey when a problem occurs, helping to improve the quality of Misskey."
myTheme: "My theme"
backgroundColor: "Background"
accentColor: "Accent"
textColor: "Text"
saveAs: "Save as..."
advanced: "Advanced"
value: "Value"
updatedAt: "Updated at"
saveConfirm: "Save changes?"
deleteConfirm: "Really delete?"
invalidValue: "Invalid value."
registry: "Registry"
closeAccount: "Close account"
_registry:
scope: "Scope"
key: "Key"
keys: "Keys"
domain: "Domain"
createKey: "Create key"
_aboutMisskey:
about: "Misskey is open-source software being developed by syuilo since 2014."
contributors: "Main contributors"
@@ -731,6 +759,16 @@ _mfm:
twitchDescription: "Infuses a strongly twitching animation."
spin: "Animation (Spin)"
spinDescription: "Infuses a spinning animation."
x2: "Big"
x2Description: "Displays content bigger."
x3: "Very big"
x3Description: "Displays content even bigger."
x4: "Extremely big"
x4Description: "Displays content even bigger than bigger than big."
blur: "Blur"
blurDescription: "Content can be blurred via this effect. It will be clearly displayed by hovering your cursor above it."
font: "Font"
fontDescription: "Sets the font to display contents in."
_reversi:
reversi: "Reversi"
gameSettings: "Game settings"
@@ -991,6 +1029,9 @@ _widgets:
postForm: "Compose a note"
slideshow: "Slideshow"
button: "Button"
onlineUsers: "Online users"
jobQueue: "Job Queue"
serverMetric: "Server metrics"
_cw:
hide: "Hide"
show: "Load more"
@@ -1480,8 +1521,9 @@ _deck:
swapRight: "Swap to right"
swapUp: "Swap with above"
swapDown: "Swap with below"
stackLeft: "Stack on the left"
stackLeft: "Stack on left column"
popRight: "Pop to the right"
profile: "Profile"
_columns:
main: "Main"
widgets: "Widgets"

View File

@@ -49,6 +49,7 @@ copyUsername: "Copiar nombre de usuario"
searchUser: "Búsqueda de usuarios"
reply: "Responder"
loadMore: "Ver más"
showMore: "Ver más"
youGotNewFollower: "te ha seguido"
receiveFollowRequest: "Recibiste una solicitud de seguimiento"
followRequestAccepted: "La solicitud de seguimiento fue aceptada"
@@ -646,6 +647,14 @@ driveUsage: "Uso del drive"
noCrawle: "Rechazar indexación del crawler"
noCrawleDescription: "Pedir a los motores de búsqueda que no indexen tu perfil, notas, páginas, etc."
clips: "Clip"
clearCache: "Limpiar caché"
backgroundColor: "Fondo"
accentColor: "Acento"
textColor: "Texto"
value: "Valores"
_registry:
key: "Clave"
keys: "Clave"
_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."
@@ -663,6 +672,7 @@ _mfm:
search: "Buscar"
flip: "Echar de un capirotazo"
flipDescription: "Voltea el contenido hacia arriba / abajo o hacia la izquierda / derecha."
font: "Fuente"
_reversi:
reversi: "Reversi"
gameSettings: "Configuración del juego"
@@ -918,6 +928,7 @@ _widgets:
federation: "Federación"
postForm: "Formulario"
button: "Botón"
jobQueue: "Cola de trabajos"
_cw:
hide: "Ocultar"
show: "Ver más"

View File

@@ -1,5 +1,6 @@
---
_lang_: "Français"
headlineMisskey: "Réseau relié par des notes"
introMisskey: "Bienvenue ! Misskey est un service de microblogage décentralisé, libre et ouvert.\nÉcrivez des « notes » et partagez ce qui se passe à linstant présent, autour de vous avec les autres 📡\nLa fonction « réactions », vous permet également dajouter une réaction rapide aux notes des autres utilisateur·rice·s 👍\nExplorons un nouveau monde 🚀"
monthAndDay: "{day}/{month}"
search: "Rechercher"
@@ -33,6 +34,9 @@ addUser: "Ajouter un·e utilisateur·rice"
favorite: "Ajouter aux favoris"
favorites: "Favoris"
unfavorite: "Retirer des favoris"
favorited: "Ajouter à mes favoris"
alreadyFavorited: "Déjà ajouté aux favoris."
cantFavorite: "Impossible d'ajouter aux favoris."
pin: "Épingler sur le profil"
unpin: "Désépingler"
copyContent: "Copier le contenu"
@@ -46,6 +50,7 @@ copyUsername: "Copier le nom dutilisateur·rice"
searchUser: "Chercher un·e utilisateur·rice"
reply: "Répondre"
loadMore: "Afficher plus …"
showMore: "Afficher plus …"
youGotNewFollower: "Vous suit"
receiveFollowRequest: "Demande dabonnement reçue"
followRequestAccepted: "La demande dabonnement a été acceptée"
@@ -74,6 +79,7 @@ error: "Erreur"
somethingHappened: "Une erreur est survenue"
retry: "Réessayer"
pageLoadError: "Le chargement de la page a échoué"
pageLoadErrorDescription: "Cela est généralement causé par le cache du navigateur ou par un problème réseau. Veuillez vider votre cache ou attendre un peu et réessayer."
enterListName: "Nom de la liste"
privacy: "Confidentialité"
makeFollowManuallyApprove: "Accepter manuellement les demandes dabonnement"
@@ -86,6 +92,8 @@ followRequestPending: "En attente dapprobation"
enterEmoji: "ou entrez un émoji"
renote: "Renote"
unrenote: "Annuler Renote"
renoted: "Republier"
cantRenote: "Ce message ne peut pas être republié."
quote: "Citer"
pinnedNote: "Note épinglée"
you: "Vous"
@@ -123,7 +131,9 @@ settingGuide: "Configuration proposée"
cacheRemoteFiles: "Mise en cache des fichiers distants"
cacheRemoteFilesDescription: "Lorsque cette option est désactivée, les fichiers distants sont chargés directement depuis linstance distante. La désactiver diminuera certes lutilisation de lespace de stockage local mais augmentera le trafic réseau puisque les miniatures ne seront plus générées."
flagAsBot: "Ce compte est un robot"
flagAsBotDescription: "Si ce compte est contrôlé par un programme, définissez cette option. Si elle est activée, elle agira comme un marqueur pour les autres développeurs afin d'éviter des chaînes d'interaction sans fin avec d'autres robots et d'ajuster les systèmes internes de Misskey pour traiter ce compte comme un robot."
flagAsCat: "Ce compte est un chat"
flagAsCatDescription: "Activez cette option pour que ce compte soit marqué comme un chat."
autoAcceptFollowed: "Accepter automatiquement les demandes dabonnement venant dutilisateur·rice·s que vous suivez"
addAcount: "Ajouter un compte"
loginFailed: "Échec de la connexion"
@@ -214,6 +224,7 @@ remove: "Supprimer"
removed: "Supprimé"
removeAreYouSure: "Supprimer «{x}» ?"
deleteAreYouSure: "Supprimer «{x}» ?"
resetAreYouSure: "Réinitialiser ?"
saved: "Enregistré"
messaging: "Discuter"
upload: "Téléverser"
@@ -428,6 +439,7 @@ useOsNativeEmojis: "Utiliser les émojis natifs du système"
youHaveNoGroups: "Vous navez aucun groupe"
joinOrCreateGroup: "Soyez invité à rejoindre les groupes ou vous pouvez créer votre propre groupe."
noHistory: "Pas d'historique"
signinHistory: "Historique de connexion"
disableAnimatedMfm: "Désactiver MFM ayant des animations"
doing: "Attends une seconde"
category: "Catégories"
@@ -478,6 +490,7 @@ listen: "Écouter"
none: "Rien"
popout: "Fenêtre contextuelle"
volume: "Volume"
masterVolume: "Volume"
details: "Détails"
chooseEmoji: "Choisissez un émoji"
unableToProcess: "Lopération na pas pu être complétée."
@@ -534,6 +547,9 @@ undeck: "Quitter le deck"
useBlurEffectForModal: "Utiliser un effet de flou pour les modals"
width: "Largeur"
height: "Hauteur"
large: "Grand"
medium: "Moyen"
small: "Petit"
generateAccessToken: "Générer un jeton d'accès"
permission: "Autorisations "
enableAll: "Tout activer"
@@ -546,6 +562,8 @@ useStarForReactionFallback: "Utiliser ★ comme alternative si lémoji de ré
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."
email: "E-mail "
emailAddress: "Adresses e-mail"
smtpConfig: "Paramètres du serveur SMTP"
smtpHost: "Hôte"
smtpPort: "Port"
@@ -570,25 +588,84 @@ create: "Créer"
notificationSetting: "Paramètres des notifications "
notificationSettingDesc: "Sélectionnez le type de notification à afficher"
useGlobalSetting: "Utiliser paramètre général"
useGlobalSettingDesc: "S'il est activé, les paramètres de notification de votre compte seront utilisés. S'il est désactivé, des configurations individuelles peuvent être effectuées."
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"
abuseReports: "Signalements"
reportAbuse: "Signalements"
reportAbuseOf: "Signaler {name}"
send: "Envoyer"
abuseMarkAsResolved: "Marquer le signalement comme résolu"
openInNewTab: "Ouvrir dans un nouvel onglet"
waitingFor: "En attente de {x}"
random: "Aléatoire"
clip: "Clip"
createNewClip: "Créer un nouveau clip"
public: "Public"
manageAccessTokens: "Gérer les jetons d'accès"
accountInfo: " Informations du compte "
notesCount: "Nombre de notes"
repliesCount: "Nombre de réponses envoyées"
renotesCount: "Nombre de notes repartagées"
repliedCount: "Nombre de réponses reçues"
followingCount: "Nombre de comptes suivis"
driveFilesCount: "Nombre de fichiers dans le Drive"
noCrawle: "Refuser l'indexation par les robots"
noCrawleDescription: "Demandez aux moteurs de recherche de ne pas indexer votre page de profil, vos notes, vos pages, etc."
lockedAccountInfo: "À moins que vous ne définissiez la visibilité de votre note sur \"Abonné-e-s\", vos notes sont visibles par tous, même si vous exigez que les followers soient approuvés manuellement."
loadRawImages: "Affichage complet des images jointes au lieu des vignettes"
disableShowingAnimatedImages: "Désactiver l'animation des images"
verificationEmailSent: "Un e-mail de vérification a été envoyé. Veuillez accéder au lien pour compléter la vérification."
notSet: "Non défini"
emailVerified: "Votre adresse e-mail a été vérifiée."
clips: "Clip"
experimentalFeatures: "Fonctionnalités expérimentales"
makeExplorable: "Rendre le compte visible sur la page \"Découvrir\"."
makeExplorableDescription: "Si vous désactivez cette option, votre compte n'apparaîtra pas sur la page \"Découvrir\"."
showGapBetweenNotesInTimeline: "Afficher un écart entre les notes sur la Timeline"
left: "Gauche"
showTitlebar: "Afficher la barre de titre"
clearCache: "Vider le cache"
onlineUsersCount: "{n} utilisateur(s) en ligne"
nNotes: "{n} Notes"
sendErrorReports: "Envoyer les rapports derreur"
sendErrorReportsDescription: "Lorsqu'il est activé, des informations détaillées sur les erreurs sont partagées avec Misskey lorsqu'un problème survient, ce qui contribue à améliorer la qualité de Misskey."
myTheme: "Mes thèmes"
backgroundColor: "Arrière-plan"
textColor: "Texte"
saveAs: "Enregistrer sous ..."
value: "Valeur"
saveConfirm: "Voulez-vous sauvegarder les modifications?"
closeAccount: "Fermer le compte"
_registry:
key: "Clé "
keys: "Clé "
_aboutMisskey:
about: "Misskey est un logiciel libre et ouvert, développé par syuilo depuis 2014."
contributors: "Principaux contributeurs"
allContributors: "Tous les contributeurs"
source: "Code source"
translation: "Traduire Misskey"
donate: "Soutenir Misskey"
morePatrons: "Nous apprécions vraiment le soutien de nombreuses autres personnes non mentionnées ici. Merci à toutes et à tous ! 🥰"
_mfm:
mention: "Mentionner"
hashtag: "Hashtags"
link: "Lien"
center: "Centrée"
quote: "Citer"
quoteDescription: "Affiche le contenu sous forme de citation."
emoji: "Émojis personnalisés"
search: "Rechercher"
font: "Police de caractères"
_reversi:
total: "Total"
_serverDisconnectedBehavior:
reload: "Rechargement automatique"
quiet: "Afficher un avertissement discret"
_channel:
create: "Créer un canal"
edit: "Éditer le canal"
@@ -610,6 +687,7 @@ _theme:
manage: "Gestion des thèmes"
code: "Code du thème"
installed: "{name} a été installé"
installedThemes: "Thèmes installés"
alreadyInstalled: "Ce thème est déjà installé"
invalid: "Le format du thème n'est pas valide"
make: "Créer un thème"
@@ -763,6 +841,7 @@ _widgets:
federation: "Fédération"
postForm: "Formulaire à publier"
button: "Bouton"
jobQueue: "File dattente"
_cw:
hide: "Masquer"
show: "Afficher plus …"

1
locales/ht-HT.yml Normal file
View File

@@ -0,0 +1 @@
---

219
locales/id-ID.yml Normal file
View File

@@ -0,0 +1,219 @@
---
_lang_: "Bahasa Jepang"
monthAndDay: "{day} {month}"
search: "Pencarian"
notifications: "Notifikasi"
username: "Nama Pengguna"
password: "Kata sandi"
ok: "OK"
gotIt: "Saya mengerti"
cancel: "Batalkan"
enterUsername: "Masukkan nama pengguna"
renotedBy: "direnote oleh {user}"
noNotes: "Tidak ada notes"
noNotifications: "Tidak ada notifikasi"
settings: "Pengaturan"
basicSettings: "Pengaturan umum"
otherSettings: "Pengaturan lainnya"
openInWindow: "Buka di jendela"
profile: "Profil"
timeline: "Linimasa"
noAccountDescription: "Pengguna ini belum menulis bio"
login: "Masuk"
loggingIn: "Sedang masuk"
logout: "Keluar"
signup: "Daftar"
uploading: "Sedang mengunggah"
save: "Simpan"
users: "Pengguna"
addUser: "Tambah pengguna"
favorite: "Favorit"
favorites: "Favorit"
unfavorite: "Hapus favorit"
favorited: "Ditambahkan ke favorit"
alreadyFavorited: "Telah ditambahkan ke favorit"
cantFavorite: "Tidak dapat menambahkan ke favorit"
pin: "Sematkan ke profil"
unpin: "Lepas sematan dari profil"
copyContent: "Salin konten"
copyLink: "Salin tautan"
delete: "Hapus"
deleteAndEdit: "Hapus dan sunting"
addToList: "Tambahkan ke daftar"
sendMessage: "Kirim pesan"
copyUsername: "Salin nama pengguna"
searchUser: "Cari pengguna"
reply: "Balas"
cantReRenote: "Renote tidak dapat direnote"
quote: "Kutip"
pinnedNote: "Note yang disematkan"
you: "Anda"
clickToShow: "Klik untuk melihat"
sensitive: "Konten sensitif"
add: "Tambahkan"
reaction: "Reaksi"
reactionSettingDescription: "Masukkan reaksi favorit yang ingin anda sematkan pada bilah reaksi"
reactionSettingDescription2: "Geser untuk memindah urutkan, klik untuk menghapus, tekan \"+\" untuk menambahkan"
rememberNoteVisibility: "Ingat pengaturan visibilitas note"
attachCancel: "Hapus lampiran"
markAsSensitive: "Tandai sebagai konten sensitif"
unmarkAsSensitive: "Hapus tanda konten sensitif"
enterFileName: "Masukkan nama berkas"
mute: "Bisukan"
unmute: "Hapus bisukan"
block: "Blokir"
unblock: "Buka blokir"
suspend: "Bekukan"
unsuspend: "Buka pembekuan"
blockConfirm: "Apakah anda yakin ingin memblokir akun ini?"
unblockConfirm: "Apakah anda yakin ingin membuka blokir akun ini?"
suspendConfirm: "Apakah anda yakin ingin membekukan akun ini?"
unsuspendConfirm: "Apakah anda yakin ingin membuka pembekuan akun ini?"
selectList: "Pilih daftar"
selectAntenna: "Pilih Antena"
selectWidget: "Pilih gawit"
editWidgets: "Sunting gawit"
editWidgetsExit: "Selesai"
customEmojis: "Emoji kustom"
emoji: "Emoji"
emojiName: "Nama emoji"
emojiUrl: "URL Emoji"
addEmoji: "Tambahkan emoji"
settingGuide: "Pengaturan rekomendasi"
cacheRemoteFiles: "Tembolokkan berkas remote"
flagAsBot: "Atur akun ini sebagai Bot"
flagAsCat: "Atur akun ini sebagai kucing"
autoAcceptFollowed: "Setujui otomatis permintaan mengikuti dari pengguna yang anda ikuti"
addAcount: "Tambahkan akun"
loginFailed: "Gagal untuk masuk"
showOnRemote: "Lihat profil asli"
general: "Umum"
wallpaper: "Wallpaper"
setWallpaper: "Atur wallpaper"
removeWallpaper: "Hapus wallpaper"
intro: "Instalasi Misskey telah selesai! Mohon untuk membuat pengguna admin."
done: "Selesai"
processing: "Memproses"
preview: "Pratinjau"
default: "Bawaan"
noCustomEmojis: "Tidak ada emoji kustom"
federating: "memfederasi"
blocked: "Diblokir"
all: "Semua"
subscribing: "Berlangganan"
publishing: "Sedang menyiarkan langsung"
notResponding: "Tidak ada respon"
instanceFollowing: "Mengikuti instance"
instanceFollowers: "Pengikut instance"
instanceUsers: "Pengguna pada instance ini"
changePassword: "Ubah kata sandi"
security: "Keamanan"
retypedNotMatch: "Input tidak sama"
currentPassword: "Kata sandi saat ini"
newPassword: "Kata sandi baru"
newPasswordRetype: "Ulangi kata sandi baru"
attachFile: "Lampirkan berkas"
more: "Lagi !"
featured: "Sorotan"
usernameOrUserId: "Nama pengguna atau User ID"
noSuchUser: "Pengguna tidak ditemukan"
lookup: "Mencari"
announcements: "Pengumuman"
imageUrl: "URL Gambar"
remove: "Hapus"
removed: "Telah dihapus"
removeAreYouSure: "Apakah anda yakin ingin menghapus \"{x}\"?"
deleteAreYouSure: "Apakah anda yakin ingin menghapus \"{x}\"?"
saved: "Telah disimpan"
messaging: "Pesan"
upload: "Unggah"
fromDrive: "Dari Drive"
fromUrl: "Dari URL"
uploadFromUrl: "Unggah dari URL"
uploadFromUrlDescription: "URL berkas yang ingin anda unggah"
uploadFromUrlRequested: "Pengunggahan telah diminta"
uploadFromUrlMayTakeTime: "Membutuhkan beberapa waktu hingga pengunggahan selesai"
explore: "Jelajahi"
games: "Permainan Misskey"
messageRead: "Telah dibaca"
noMoreHistory: "Tidak ada sejarah lagi"
startMessaging: "Mulai mengirim pesan"
nUsersRead: "Dibaca oleh {n}"
nsfw: "Konten sensitif"
watch: "Tonton"
unwatch: "Batal tonton"
accept: "Terima"
reject: "Tolak"
normal: "Normal"
instanceName: "Nama instance"
instanceDescription: "Tentang instance"
maintainerName: "Pengelola"
maintainerEmail: "Surel pengelola"
tosUrl: "URL Syarat dan Ketentuan"
thisYear: "Tahun ini"
thisMonth: "Bulan ini"
today: "Hari ini"
dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Halaman"
integration: "Integrasi"
connectSerice: "Sambungkan"
disconnectSerice: "Putuskan"
enableLocalTimeline: "Nyalakan linimasa lokal"
enableGlobalTimeline: "Nyalakan linimasa global"
registration: "Pendaftaran"
enableRegistration: "Nyalakan pendaftaran pengguna baru"
invite: "Undang"
proxyRemoteFiles: "Proksi berkas remote"
driveCapacityPerLocalAccount: "Kapasitas drive per pengguna lokal"
driveCapacityPerRemoteAccount: "Kapasitas drive per pengguna remote"
inMb: "dalam Megabytes"
iconUrl: "URL Gambar ikon"
bannerUrl: "URL Banner"
basicInfo: "Informasi Umum"
pinnedUsers: "Pengguna yang disematkan"
pinnedPages: "Halaman yang disematkan"
pinnedNotes: "Note yang disematkan"
hcaptcha: "hCaptcha"
enableHcaptcha: "Nyalakan hCaptcha"
hcaptchaSiteKey: "Site Key"
hcaptchaSecretKey: "Secret Key"
recaptcha: "reCAPTCHA"
enableRecaptcha: "Nyalakan reCAPTCHA"
recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret Key"
antennas: "Antena"
manageAntennas: "Pengelola Antena"
notFound: "Tidak dapat ditemukan"
invites: "Undang"
invitations: "Undang"
smtpUser: "Nama Pengguna"
smtpPass: "Kata sandi"
_mfm:
quote: "Kutip"
emoji: "Emoji kustom"
search: "Pencarian"
_sfx:
notification: "Notifikasi"
chat: "Pesan"
_widgets:
notifications: "Notifikasi"
timeline: "Linimasa"
_profile:
username: "Nama Pengguna"
_exportOrImport:
muteList: "Bisukan"
blockingList: "Blokir"
_rooms:
_roomType:
default: "Bawaan"
_notification:
_types:
quote: "Kutip"
reaction: "Reaksi"
_deck:
_columns:
notifications: "Notifikasi"
tl: "Linimasa"
antenna: "Antena"

1
locales/it-IT.yml Normal file
View File

@@ -0,0 +1 @@
---

View File

@@ -1,5 +1,6 @@
_lang_: "日本語"
headlineMisskey: "ノートでつながるネットワーク"
introMisskey: "ようこそMisskeyは、オープンソースの分散型マイクロブログサービスです。\n「ート」を作成して、いま起こっていることを共有したり、あなたについて皆に発信しよう📡\n「リアクション」機能で、皆のートに素早く反応を追加することもできます👍\n新しい世界を探検しよう🚀"
monthAndDay: "{month}月 {day}日"
search: "検索"
@@ -49,6 +50,7 @@ copyUsername: "ユーザー名をコピー"
searchUser: "ユーザーを検索"
reply: "返信"
loadMore: "もっと見る"
showMore: "もっと見る"
youGotNewFollower: "フォローされました"
receiveFollowRequest: "フォローリクエストされました"
followRequestAccepted: "フォローが承認されました"
@@ -670,6 +672,40 @@ center: "中央"
wide: "広い"
narrow: "狭い"
reloadToApplySetting: "設定はページリロード後に反映されます。今すぐリロードしますか?"
showTitlebar: "タイトルバーを表示する"
clearCache: "キャッシュをクリア"
onlineUsersCount: "{n}人がオンライン"
nUsers: "{n}ユーザー"
nNotes: "{n}ノート"
sendErrorReports: "エラーリポートを送信"
sendErrorReportsDescription: "オンにすると、問題が発生したときにエラーの詳細情報がMisskeyに共有され、ソフトウェアの品質向上に役立てることができます。エラー情報には、OSのバージョン、ブラウザの種類、行動履歴などが含まれます。"
myTheme: "マイテーマ"
backgroundColor: "背景"
accentColor: "アクセント"
textColor: "文字"
saveAs: "名前を付けて保存"
advanced: "高度"
value: "値"
updatedAt: "更新日時"
saveConfirm: "保存しますか?"
deleteConfirm: "削除しますか?"
invalidValue: "有効な値ではありません。"
registry: "レジストリ"
closeAccount: "アカウントを閉鎖する"
currentVersion: "現在のバージョン"
latestVersion: "最新のバージョン"
youAreRunningUpToDateClient: "お使いのクライアントは最新です。"
newVersionOfClientAvailable: "新しいバージョンのクライアントが利用可能です。"
usageAmount: "使用量"
capacity: "容量"
inUse: "使用中"
_registry:
scope: "スコープ"
key: "キー"
keys: "キー"
domain: "ドメイン"
createKey: "キーを作成"
_aboutMisskey:
about: "Misskeyはsyuiloによって2014年から開発されている、オープンソースのソフトウェアです。"
@@ -729,11 +765,21 @@ _mfm:
bounce: "アニメーション(バウンド)"
bounceDescription: "ぽよんぽよん弾むようなアニメーションを与えます。"
shake: "アニメーション(ぶるぶる)"
shakeDescription: "ぶるぶるるアニメーションを与えます。"
shakeDescription: "ぶるぶる震えるアニメーションを与えます。"
twitch: "アニメーション(ブレ)"
twitchDescription: "激しくブレるアニメーションを与えます。"
spin: "アニメーション(回転)"
spinDescription: "回転するアニメーションを与えます。"
x2: "大きく"
x2Description: "内容を大きく表示します。"
x3: "とても大きく"
x3Description: "内容をとても大きく表示します。"
x4: "究極に大きく"
x4Description: "内容を究極に大きく表示します。"
blur: "ぼかし"
blurDescription: "内容をぼかすことができます。ポインターを上に乗せるとはっきり見えるようになります。"
font: "フォント"
fontDescription: "内容のフォントを指定することができます。"
_reversi:
reversi: "リバーシ"
@@ -1012,6 +1058,9 @@ _widgets:
postForm: "投稿フォーム"
slideshow: "スライドショー"
button: "ボタン"
onlineUsers: "オンラインユーザー"
jobQueue: "ジョブキュー"
serverMetric: "サーバーメトリクス"
_cw:
hide: "隠す"
@@ -1530,6 +1579,7 @@ _deck:
swapDown: "下に移動"
stackLeft: "左に重ねる"
popRight: "右に出す"
profile: "プロファイル"
_columns:
main: "メイン"

View File

@@ -49,6 +49,7 @@ copyUsername: "ユーザー名をコピー"
searchUser: "ユーザーを検索"
reply: "返す"
loadMore: "もっとあるやろ!"
showMore: "もっとあるやろ!"
youGotNewFollower: "フォローされたで"
receiveFollowRequest: "フォローリクエストされたで"
followRequestAccepted: "フォローが承認されたで"
@@ -454,6 +455,7 @@ remote: "リモート"
smtpHost: "ホスト"
smtpUser: "ユーザー名"
smtpPass: "パスワード"
clearCache: "キャッシュにさいなら"
_mfm:
mention: "メンション"
quote: "引用"
@@ -497,6 +499,7 @@ _widgets:
timeline: "タイムライン"
activity: "アクティビティ"
federation: "連合"
jobQueue: "ジョブキュー"
_cw:
show: "もっとあるやろ!"
_poll:

View File

@@ -13,6 +13,7 @@ delete: "Kkes"
addToList: "Rnu ɣer tebdart"
reply: "Err"
loadMore: "Wali ugar"
showMore: "Wali ugar"
youGotNewFollower: "Yeṭṭafaṛ-ik·em-id"
mention: "Bder"
import: "Kter"
@@ -38,6 +39,7 @@ smtpPass: "Awal uffir"
_mfm:
mention: "Bder"
search: "Nadi"
font: "Tasefsit"
_theme:
keys:
mention: "Bder"

View File

@@ -40,6 +40,7 @@ sendMessage: "ಸಂದೇಶ ಕಳುಹಿಸು"
copyUsername: "ಬಳಕೆಹೆಸರು ನಕಲಿಸು"
reply: "ಉತ್ತರಿಸು"
loadMore: "ಇನ್ನಷ್ಟು ನೋಡು"
showMore: "ಇನ್ನಷ್ಟು ನೋಡು"
youGotNewFollower: "ಹಿಂಬಾಲಿಸಿದರು"
receiveFollowRequest: "ಹಿಂಬಾಲನೆ ವಿನಂತಿ ಬಂದಿದೆ"
followRequestAccepted: "ಹಿಂಬಾಲನೆ ವಿನಂತಿ ಸ್ವೀಕರಿಸಲಾಯಿತು"

View File

@@ -33,6 +33,9 @@ addUser: "유저 추가"
favorite: "즐겨찾기"
favorites: "즐겨찾기"
unfavorite: "즐겨찾기에서 제거"
favorited: "즐겨찾기에 등록했습니다"
alreadyFavorited: "이미 즐겨찾기에 등록되어 있습니다"
cantFavorite: "즐겨찾기에 등록하지 못했습니다"
pin: "프로필에 고정"
unpin: "프로필에서 고정 해제"
copyContent: "내용 복사"
@@ -46,6 +49,7 @@ copyUsername: "유저명 복사"
searchUser: "사용자 검색"
reply: "답글"
loadMore: "더 보기"
showMore: "더 보기"
youGotNewFollower: "새로운 팔로워가 있습니다"
receiveFollowRequest: "새로운 팔로우 요청이 있습니다"
followRequestAccepted: "팔로우가 수락되었습니다"
@@ -87,6 +91,9 @@ followRequestPending: "팔로우 허가 대기중"
enterEmoji: "이모지 입력"
renote: "Renote"
unrenote: "Renote 취소"
renoted: "Renote 하였습니다"
cantRenote: "이 게시물은 Renote할 수 없습니다."
cantReRenote: "Renote를 Renote할 수 없습니다."
quote: "인용"
pinnedNote: "고정해놓은 노트"
you: "당신"
@@ -95,6 +102,7 @@ sensitive: "열람주의"
add: "추가"
reaction: "리액션"
reactionSettingDescription: "리액션 선택 상자에 표시할 리액션을 설정합니다."
reactionSettingDescription2: "끌어서 순서 변경, 클릭해서 삭제, +를 눌러서 추가할 수 있습니다."
rememberNoteVisibility: "공개 범위를 기억하기"
attachCancel: "첨부 취소"
markAsSensitive: "열람주의로 설정"
@@ -319,6 +327,7 @@ pinnedUsers: "고정된 유저"
pinnedUsersDescription: "\"발견하기\" 페이지 등에 고정하고 싶은 유저를 한 줄에 한 명씩 적습니다."
pinnedPages: "고정한 페이지"
pinnedPagesDescription: "인스턴스의 대문에 고정하고 싶은 페이지의 경로를 한 줄에 하나씩 적습니다."
pinnedClipId: "고정할 클립의 ID"
pinnedNotes: "고정해놓은 노트"
hcaptcha: "hCaptcha"
enableHcaptcha: "hCaptcha 활성화"
@@ -452,6 +461,7 @@ remote: "리모트"
total: "합계"
weekOverWeekChanges: "지난주보다"
dayOverDayChanges: "어제보다"
appearance: "모양"
clientSettings: "클라이언트 설정"
accountSettings: "계정 설정"
promotion: "프로모션"
@@ -555,7 +565,10 @@ notificationType: "알림 유형"
edit: "편집"
useStarForReactionFallback: "알 수 없는 리액션 이모지 대신 ★ 사용"
emailConfig: "메일 서버 설정"
enableEmail: "이메일 송신 기능 활성화"
emailConfigInfo: "가입 시 메일 주소 확인이나 비밀번호 초기화 시에 사용합니다."
email: "이메일"
emailAddress: "메일 주소"
smtpConfig: "SMTP 서버 설정"
smtpHost: "호스트"
smtpPort: "포트"
@@ -566,9 +579,11 @@ smtpSecure: "SMTP 연결에 Implicit SSL/TTS 사용"
smtpSecureInfo: "STARTTLS 사용 시에는 해제합니다."
testEmail: "이메일 전송 테스트"
wordMute: "단어 뮤트"
userSaysSomething: "{name}님이 무언가를 말했습니다"
makeActive: "활성화"
display: "표시"
copy: "복사"
metrics: "통계"
overview: "요약"
logs: "로그"
delayed: "지연"
@@ -577,6 +592,7 @@ channel: "채널"
create: "생성"
notificationSetting: "알림 설정"
notificationSettingDesc: "표시할 알림의 종류를 선택해 주세요."
useGlobalSetting: "글로벌 설정을 사용하기"
useGlobalSettingDesc: "활성화하면 계정의 알림 설정이 적용되니다. 비활성화하면 개별적으로 설정할 수 있게 됩니다."
other: "기타"
regenerateLoginToken: "로그인 토큰을 재생성"
@@ -592,7 +608,12 @@ reportAbuseOf: "{name}을 신고하기"
fillAbuseReportDescription: "신고하려는 이유를 자세히 알려주세요. 특정 게시물을 신고할 때에는 게시물의 URL도 포함해 주세요."
abuseReported: "신고를 보냈습니다. 신고해 주셔서 감사합니다."
send: "전송"
abuseMarkAsResolved: "해결됨으로 표시"
openInNewTab: "새 탭에서 열기"
openInSideView: "사이드뷰로 열기"
editTheseSettingsMayBreakAccount: "이 설정을 변경하면 계정이 손상될 수 있습니다."
instanceTicker: "노트의 인스턴스 정보"
waitingFor: "{x}을(를) 기다리고 있습니다"
random: "랜덤"
system: "시스템"
switchUi: "UI 전환"
@@ -602,21 +623,94 @@ createNew: "새로 만들기"
optional: "옵션"
createNewClip: "새 클립 만들기"
public: "공개"
i18nInfo: "Misskey는 자원봉사자들에 의해 다양한 언어로 번역되고 있습니다. {link}에서 번역에 참가할 수 있습니다."
manageAccessTokens: "액세스 토큰 관리"
accountInfo: "계정 정보"
notesCount: "노트 수"
repliesCount: "답글 수"
renotesCount: "Renote 수"
repliedCount: "받은 답글 수"
renotedCount: "받은 Renote 수"
followingCount: "팔로우 수"
followersCount: "팔로워 수"
sentReactionsCount: "보낸 리액션 수"
receivedReactionsCount: "받은 리액션 수"
pollVotesCount: "투표한 횟수"
pollVotedCount: "투표받은 횟수"
yes: "예"
no: "아니오"
driveFilesCount: "드라이브 파일 개수"
driveUsage: "드라이브 사용량"
noCrawle: "검색엔진의 인덱싱 거부"
noCrawleDescription: "검색엔진에 사용자 페이지, 노트, 페이지 등의 콘텐츠를 인덱싱되지 않게 합니다."
lockedAccountInfo: "팔로우를 승인으로 승인받더라도 노트의 공개 범위를 '팔로워'로 하지 않는 한 누구나 당신의 노트를 볼 수 있습니다."
alwaysMarkSensitive: "미디어를 항상 열람 주의로 설정"
loadRawImages: "첨부한 이미지의 썸네일을 원본화질로 표시"
disableShowingAnimatedImages: "움직이는 이미지를 자동으로 재생하지 않음"
verificationEmailSent: "확인 메일을 발송하였습니다. 설정을 완료하려면 메일에 첨부된 링크를 확인해 주세요."
notSet: "설정되지 않음"
emailVerified: "메일 주소가 확인되었습니다."
noteFavoritesCount: "즐겨찾기한 노트 수"
pageLikesCount: "좋아요 한 Page 수"
pageLikedCount: "Page에 받은 좋아요 수"
reversiCount: "리버시 대국 횟수"
contact: "연락처"
useSystemFont: "시스템 기본 글꼴을 사용"
clips: "클립"
experimentalFeatures: "실험실"
developer: "개발자"
makeExplorable: "\"발견하기\"에 내 계정 보이기"
makeExplorableDescription: "비활성화하면 \"발견하기\"에 나의 계정을 표시하지 않습니다."
showGapBetweenNotesInTimeline: "타임라인의 노트 사이를 띄워서 표시"
duplicate: "복제"
left: "왼쪽"
center: "가운데"
wide: "넓게"
narrow: "좁게"
reloadToApplySetting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?"
showTitlebar: "타이틀 바를 표시하기"
clearCache: "캐시 비우기"
onlineUsersCount: "{n}명이 접속 중"
nUsers: "{n} 유저"
nNotes: "{n} 노트"
sendErrorReports: "오류 보고서 보내기"
sendErrorReportsDescription: "이 설정을 활성화하면, 문제가 발생했을 때 오류에 대한 상세 정보를 Misskey에 보내어 더 나은 소프트웨어를 만드는 데에 도움을 줄 수 있습니다."
myTheme: "내 테마"
backgroundColor: "배경 색"
accentColor: "강조 색상"
textColor: "문자 색"
saveAs: "다른 이름으로 저장"
advanced: "고급"
value: "값"
updatedAt: "수정한 날짜"
saveConfirm: "저장하시겠습니까?"
deleteConfirm: "삭제하시겠습니까?"
invalidValue: "올바른 값이 아닙니다."
registry: "레지스트리"
closeAccount: "계정 폐쇄"
_registry:
scope: "범위"
key: "키"
keys: "키"
domain: "도메인"
createKey: "키 생성"
_aboutMisskey:
about: "Misskey는 syuilo에 의해서 2014년부터 개발되어 온 오픈소스 소프트웨어 입니다."
contributors: "주요 기여자"
allContributors: "모든 기여자"
source: "소스 코드"
translation: "Misskey를 번역하기"
donate: "Misskey에 기부하기"
morePatrons: "이 외에도 다른 많은 분들이 도움을 주시고 계십니다. 감사합니다🥰"
patrons: "후원자"
_nsfw:
respect: "열람주의로 설정된 미디어 숨기기"
ignore: "열람 주의 미디어 항상 표시"
force: "미디어 항상 숨기기"
_mfm:
cheatSheet: "MFM 도움말"
intro: "MFM는 Misskey의 다양한 곳에서 사용할 수 있는 전용 마크업 언어입니다. 여기에서는 MFM에서 사용할 수 있는 구문을 확인할 수 있습니다."
dummy: "Misskey로 연합우주의 세계가 펼쳐집니다"
mention: "멘션"
mentionDescription: "골뱅이표(@) 뒤에 사용자명을 넣어 특정 유저를 나타낼 수 있습니다."
hashtag: "해시태그"
@@ -624,12 +718,16 @@ _mfm:
url: "URL"
urlDescription: "URL을 나타낼 수 있습니다."
link: "링크"
linkDescription: "문장의 특정 범위를 URL로 표시합니다."
bold: "굵음/볼드체"
boldDescription: "문자를 굵게 강조합니다."
smallDescription: "내용을 작고 연하게 보이게 합니다."
center: "가운데 정렬"
centerDescription: "내용을 가운데 정렬로 보이게 합니다."
inlineCode: "코드(인라인)"
inlineCodeDescription: "여러 행의 코드를 문법 강조를 적용하여 인라인으로 표시합니다."
blockCode: "코드(블록)"
blockCodeDescription: "여러 행의 코드를 문법 강조를 적용하여 블록으로 표시합니다."
inlineMath: "수식(인라인)"
inlineMathDescription: "수식(KaTeX)를 인라인으로 보이게 합니다."
blockMath: "수식(블록)"
@@ -642,6 +740,14 @@ _mfm:
flip: "플립"
flipDescription: "내용을 상하 또는 좌우로 반전시킵니다."
jump: "애니메이션(점프)"
x2: "크게"
x2Description: "내용을 크게 표시합니다."
x3: "더 크게"
x3Description: "내용을 더 크게 표시합니다."
x4: "매우 크게"
x4Description: "내용을 매우 크게 표시합니다."
font: "폰트"
fontDescription: "내용의 글꼴을 지정할 수 있습니다."
_reversi:
reversi: "리버시"
gameSettings: "대국 설정"
@@ -654,6 +760,8 @@ _reversi:
waitingForOther: "상대의 준비가 완료될 때까지 기다리고 있습니다"
waitingForMe: "당신의 준비 완료를 기다리고 있습니다"
ready: "준비 완료"
cancelReady: "준비 취소"
opponentTurn: "상대의 차례입니다"
myTurn: "당신의 차례입니다"
turnOf: "{name}님의 차례입니다"
pastTurnOf: "{name}님의 차례"
@@ -676,11 +784,17 @@ _instanceTicker:
none: "보이지 않음"
remote: "리모트 유저에게만 보이기"
always: "항상 보이기"
_serverDisconnectedBehavior:
reload: "자동으로 새로고침"
dialog: "경고창 표시"
quiet: "조용히 경고"
_channel:
create: "채널 생성"
edit: "채널 편집"
setBanner: "배너 설정"
removeBanner: "배너 삭제"
featured: "트렌드"
owned: "관리중"
following: "팔로잉"
usersCount: "{n}명 참여 중"
notesCount: "{n}노트"
@@ -690,6 +804,9 @@ _sidebar:
_wordMute:
muteWords: "뮤트할 단어"
muteWordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정됩니다。"
muteWordsDescription2: "정규 표현식을 사용하려면 키워드를 빗금표(/)로 감싸 주세요."
softDescription: "지정한 조건의 노트를 타임라인에서 숨깁니다."
hardDescription: "지정한 조건의 노트를 타임라인에 추가하지 않습니다. 타임라인에 추가되지 않은 노트는 조건을 변경해도 표시되지 않습니다."
mutedNotes: "뮤트된 노트"
_theme:
explore: "테마 찾아보기"
@@ -697,6 +814,8 @@ _theme:
manage: "테마 관리"
code: "테마 코드"
installed: "{name} 테마가 설치되었습니다"
installedThemes: "설치된 테마"
builtinThemes: "표준 테마"
alreadyInstalled: "이미 설치된 테마입니다"
invalid: "테마 형식이 올바르지 않습니다"
make: "테마 만들기"
@@ -711,8 +830,11 @@ _theme:
func: "함수"
funcKind: "함수 종류"
argument: "매개변수"
inputConstantName: "상수 이름을 입력하세요"
importInfo: "여기에 테마 코드를 붙여 넣어 에디터로 불러올 수 있습니다."
deleteConstantConfirm: "상수 {const}를 삭제하시겠습니까?"
keys:
accent: "강조 색상"
link: "링크"
hashtag: "해시태그"
mention: "멘션"
@@ -725,6 +847,9 @@ _sfx:
chat: "대화"
chatBg: "대화 (백그라운드)"
antenna: "안테나 수신"
channel: "채널 알림"
reversiPutBlack: "리버시: 흑돌을 두었을 때"
reversiPutWhite: "리버시: 백돌을 두었을 때"
_ago:
unknown: "알 수 없음"
future: "미래"
@@ -800,6 +925,8 @@ _permissions:
"write:page-likes": "페이지의 좋아요를 추가하거나 삭제합니다"
"read:user-groups": "유저 그룹을 조회합니다"
"write:user-groups": "유저 그룹을 만들거나, 초대하거나, 이름을 변경하거나, 양도하거나, 삭제합니다"
"read:channels": "채널을 보기"
"write:channels": "채널을 변경하기"
_auth:
shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
shareAccessAsk: "이 애플리케이션이 계정에 접근하는 것을 허용하시겠습니까?"
@@ -834,7 +961,10 @@ _widgets:
digitalClock: "디지털 시계"
federation: "연합"
postForm: "글 입력란"
slideshow: "슬라이드 쇼"
button: "버튼"
jobQueue: "작업 대기열"
serverMetric: "서버 통계"
_cw:
hide: "숨기기"
show: "더 보기"
@@ -876,6 +1006,7 @@ _visibility:
_postForm:
replyPlaceholder: "이 노트에 답글..."
quotePlaceholder: "이 노트를 인용..."
channelPlaceholder: "채널에 게시하기..."
_placeholders:
a: "지금 무엇을 하고 있나요?"
b: "무슨 일이 일어나고 있나요?"

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +1,31 @@
---
_lang_: "Português"
monthAndDay: "{day}/{month}"
search: "Pesquisar"
notifications: "Notificações"
username: "Nome de usuário"
password: "Senha"
ok: "OK"
cancel: "Cancelar"
enterUsername: "Digite o nome de usuário"
renotedBy: "Repostado por {user}"
settings: "Configurações"
basicSettings: "Configurações básicas"
otherSettings: "Outras configurações"
profile: "Perfil"
timeline: "Timeline"
smtpUser: "Nome de usuário"
smtpPass: "Senha"
_mfm:
search: "Pesquisar"
_sfx:
notification: "Notificações"
_widgets:
notifications: "Notificações"
timeline: "Timeline"
_profile:
username: "Nome de usuário"
_deck:
_columns:
notifications: "Notificações"
tl: "Timeline"

View File

@@ -49,6 +49,7 @@ copyUsername: "Скопировать имя пользователя"
searchUser: "Поиск людей"
reply: "Ответить"
loadMore: "Показать еще"
showMore: "Показать еще"
youGotNewFollower: "Новый подписчик"
receiveFollowRequest: "Получен запрос на подписку"
followRequestAccepted: "Запрос на подписку принят"
@@ -667,6 +668,19 @@ showGapBetweenNotesInTimeline: "Показывать разделитель ме
duplicate: "Дубликат"
left: "Влево"
center: "По центру"
reloadToApplySetting: "Это настройка вступает в силу при загрузке страницы. Перезагрузить сейчас?"
showTitlebar: "Показать заголовок"
clearCache: "Очистить кэш"
onlineUsersCount: "Пользователей сейчас в сети: {n}"
nUsers: "Пользователей: {n}"
nNotes: "Заметок: {n}"
backgroundColor: "Фон"
accentColor: "Акцент"
textColor: "Текст"
value: "Значения"
_registry:
key: "Ключ"
keys: "Ключ"
_aboutMisskey:
about: "Misskey — программа с открытым исходным кодом, которую разрабатывает syuilo с 2014 года."
contributors: "Основные соавторы"
@@ -728,6 +742,7 @@ _mfm:
twitchDescription: "Заставляет трястись как одержимого"
spin: "Вращение"
spinDescription: "Так можно крутить содержимое в разных направлениях."
font: "Шрифт"
_reversi:
reversi: "Реверси"
gameSettings: "Настройки игры"
@@ -988,6 +1003,8 @@ _widgets:
postForm: "Форма отправки"
slideshow: "Показ слайдов"
button: "Кнопка"
onlineUsers: "Пользователи сейчас с сети"
jobQueue: "Очередь заданий"
_cw:
hide: "Спрятать"
show: "Показать еще"

1
locales/th-TH.yml Normal file
View File

@@ -0,0 +1 @@
---

View File

@@ -1,6 +1,7 @@
---
_lang_: "Українська"
introMisskey: "Ласкаво просимо! Misskey - децентралізована служба мікроблогів з відкритим кодом.\nСтворюйте \"нотатки\", щоб поділитися тим, що відбувається, і розповісти всім про себе 📡\nЗа допомогою \"реакцій\" ви також можете швидко висловити свої почуття щодо нотаток інших 👍\nДавайте досліджувати новий світ 🚀"
headlineMisskey: "Мережа об'єднана записами"
introMisskey: "Ласкаво просимо! Misskey - децентралізована служба мікроблогів з відкритим кодом.\nСтворюйте \"записи\", щоб поділитися тим, що відбувається, і розповісти всім про себе 📡\nЗа допомогою \"реакцій\" ви також можете швидко висловити свої почуття щодо записів інших 👍\nДавайте досліджувати новий світ 🚀"
monthAndDay: "{month}/{day}"
search: "Пошук"
notifications: "Сповіщення"
@@ -33,6 +34,9 @@ addUser: "Додати користувача"
favorite: "Обране"
favorites: "Обране"
unfavorite: "Видалити з обраного"
favorited: "Додано до вподобаних."
alreadyFavorited: "Вже додано до вподобаних."
cantFavorite: "Неможливо вподобати."
pin: "Закріпити"
unpin: "Відкріпити"
copyContent: "Скопіювати контент"
@@ -46,6 +50,7 @@ copyUsername: "Скопіювати ім’я користувача"
searchUser: "Пошук користувачів"
reply: "Відповісти"
loadMore: "Показати більше"
showMore: "Показати більше"
youGotNewFollower: "Новий підписник"
receiveFollowRequest: "Отримано запит на підписку"
followRequestAccepted: "Підписка прийнята"
@@ -59,12 +64,12 @@ files: "Файли"
download: "Завантажити"
driveFileDeleteConfirm: "Ви впевнені, що хочете видалити файл {name}? Нотатки із цим файлом також буде видалено."
unfollowConfirm: "Ви впевнені, що хочете відписатися від {name}?"
exportRequested: "Ви запросили експорт. Це може зайняти деякий час. Після завершення експорту отриманий файл буде додано на диск."
importRequested: "Ви запросили імпорт. Це може зайняти деякий час."
exportRequested: "Експортування розпочато. Це може зайняти деякий час. Після завершення експорту отриманий файл буде додано на диск."
importRequested: "Імпортування розпочато. Це може зайняти деякий час."
lists: "Списки"
noLists: "Немає списків"
note: "Нотатки"
notes: "Нотатки"
note: "Запис"
notes: "Записи"
following: "Підписки"
followers: "Підписники"
followsYou: "Підписаний(-а) на вас"
@@ -76,10 +81,10 @@ retry: "Спробувати знову"
pageLoadError: "Помилка при завантаженні сторінки"
pageLoadErrorDescription: "Зазвичай це пов’язано з помилками мережі або кешем браузера. Очистіть кеш або почекайте трохи й спробуйте ще раз."
enterListName: "Введіть назву списку"
privacy: "Приватність"
privacy: "Конфіденційність"
makeFollowManuallyApprove: "Підтверджувати підписників уручну"
defaultNoteVisibility: "Видимість за замовчуванням"
follow: "Підписка"
follow: "Підписатись"
followRequest: "Запит на підписку"
followRequests: "Запити на підписку"
unfollow: "Відписатись"
@@ -87,14 +92,18 @@ followRequestPending: "Очікуючі запити на підписку"
enterEmoji: "Введіть емодзі"
renote: "Поширити"
unrenote: "Відміна поширення"
renoted: "Поширити запис."
cantRenote: "Неможливо поширити."
cantReRenote: "Поширення не можливо поширити."
quote: "Цитата"
pinnedNote: "Закріплена нотатка"
pinnedNote: "Закріплений запис"
you: "Ви"
clickToShow: "Натисніть для перегляду"
sensitive: "NSFW"
add: "Додати"
reaction: "Реакції"
reactionSettingDescription: "Виберіть свої улюблені реакції, які хочете закріпити в селекторі реакцій."
reactionSettingDescription2: "Перемістити щоб змінити порядок, Клацнути мишою щоб видалити, Натиснути \"+\" щоб додати."
rememberNoteVisibility: "Пам’ятати параметри видимісті"
attachCancel: "Видалити вкладення"
markAsSensitive: "Позначити як NSFW"
@@ -124,7 +133,9 @@ settingGuide: "Рекомендована конфігурація"
cacheRemoteFiles: "Кешувати дані з інших інстансів"
cacheRemoteFilesDescription: "Якщо кешування вимкнено, віддалені файли завантажуються безпосередньо з віддаленого інстансу. Це зменшує використання сховища, але збільшує трафік, оскільки не генеруются ескізи."
flagAsBot: "Акаунт бота"
flagAsBotDescription: "Ввімкніть якщо цей обліковий запис використовується ботом. Ця опція позначить обліковий запис як бота. Це потрібно щоб виключити безкінечну інтеракцію між ботами а також відповідного підлаштування Misskey."
flagAsCat: "Акаунт кота"
flagAsCatDescription: "Ввімкніть, щоб позначити, що обліковий запис є котиком."
autoAcceptFollowed: "Автоматично приймати запити на підписку від користувачів, на яких ви підписані"
addAcount: "Додати акаунт"
loginFailed: "Не вдалося увійти"
@@ -139,15 +150,15 @@ followConfirm: "Підписатися на {name}?"
proxyAccount: "Проксі-акаунт"
host: "Хост"
selectUser: "Виберіть користувача"
recipient: "Кому"
annotation: "Коментар"
recipient: "Отримувач"
annotation: "Коментарі"
federation: "Федіверс"
instances: "Інстанс"
registeredAt: "Приєднався(лась)"
latestRequestSentAt: "Останній запит надіслано"
latestRequestReceivedAt: "Останній запит прийнято"
latestStatus: "Останній статус"
storageUsage: "Використання простіру"
storageUsage: "Використання простору"
charts: "Графіки"
perHour: "Щогодинно"
perDay: "Щоденно"
@@ -172,19 +183,19 @@ clearCachedFiles: "Очистити кеш"
clearCachedFilesConfirm: "Ви впевнені, що хочете видалити всі кешовані файли?"
blockedInstances: "Заблоковані інстанси"
blockedInstancesDescription: "Вкажіть інстанси, які потрібно заблокувати. Перелічені інстанси більше не зможуть спілкуватися з цим інстансом."
muteAndBlock: "Ігнор і блокування"
mutedUsers: "Ігноровані користувачі"
muteAndBlock: "Заглушення і блокування"
mutedUsers: "Заглушені користувачі"
blockedUsers: "Заблоковані користувачі"
noUsers: "Немає користувачів"
editProfile: "Редагувати профіль"
noteDeleteConfirm: "Ви дійсно хочете видалити цю нотатку?"
pinLimitExceeded: "Більше нотаток не можна закріпити"
intro: "Встановлення Misskey завершено! Будь ласка, створіть акаунт адміністратора."
editProfile: "Редагувати обліковий запис"
noteDeleteConfirm: "Ви дійсно хочете видалити цей запис?"
pinLimitExceeded: "Більше записів не можна закріпити"
intro: "Встановлення Misskey завершено! Будь ласка, створіть обліковий запис адміністратора."
done: "Готово"
processing: "Обробка"
preview: "Передогляд"
preview: опередній перегляд"
default: "За умовчанням"
noCustomEmojis: "Немає кастомних емоджі"
noCustomEmojis: "Немає нетипових емоджі"
noJobs: "Немає завдань"
federating: "Федерується"
blocked: "Заблоковано"
@@ -202,27 +213,27 @@ retypedNotMatch: "Введені дані не збігаються."
currentPassword: "Поточний пароль"
newPassword: "Новий пароль"
newPasswordRetype: "Новий пароль (повторно)"
attachFile: "Вкласти файл"
attachFile: "Прикріпити файл"
more: "Бiльше!"
featured: "Популярні"
usernameOrUserId: "Ім'я або ID користувача"
noSuchUser: "Користувача не знайдено"
lookup: "Пошук"
announcements: "Оголошення"
imageUrl: "URL зображення"
imageUrl: "Посилання на зображення"
remove: "Видалити"
removed: "Видалено"
removeAreYouSure: "Ви впевнені, що хочете видалити \"{x}\"?"
deleteAreYouSure: "Ви впевнені, що хочете видалити \"{x}\"?"
resetAreYouSure: "Дійсно ресет?"
resetAreYouSure: "Справді скинути?"
saved: "Збережено"
messaging: "Чати"
upload: "Завантажити"
fromDrive: "З диска"
fromUrl: "З URL"
uploadFromUrl: "Завантажити з URL"
uploadFromUrlDescription: "URL-адреса файлу для завантаження"
uploadFromUrlRequested: "Обрано завантаження"
fromUrl: "З посилання"
uploadFromUrl: "Завантажити з посилання"
uploadFromUrlDescription: "Посилання на файл для завантаження"
uploadFromUrlRequested: "Завантаження розпочалось"
uploadFromUrlMayTakeTime: "Завантаження може зайняти деякий час."
explore: "Огляд"
games: "Ігри Misskey"
@@ -282,6 +293,7 @@ watch: "Стежити"
unwatch: "Не стежити"
accept: "Прийняти"
reject: "Відхилити"
normal: "Нормальний"
instanceName: "Назва інстансу"
instanceDescription: "Описання інстансу"
maintainerName: "Ім'я адміністратора"
@@ -304,6 +316,7 @@ registration: "Реєстрація"
enableRegistration: "Дозволити реєстрацію"
invite: "Запросити"
proxyRemoteFiles: "Проксувати файли з інших інстансів"
proxyRemoteFilesDescription: "При увімкненні віддалені файли, які не зберігаються локально або були видалені через недостатнії обсяг пам'яті, будуть локально проксовані включаючи обкладинки. Це налаштування не впливає на пам'ять серверу. "
driveCapacityPerLocalAccount: "Об'єм диска на одного локального користувача"
driveCapacityPerRemoteAccount: "Об'єм диска на одного віддаленого користувача"
inMb: "В мегабайтах"
@@ -311,8 +324,10 @@ iconUrl: "URL аватара"
bannerUrl: "URL банера"
basicInfo: "Основна інформація"
pinnedUsers: "Закріплені користувачі"
pinnedUsersDescription: "Впишіть в список користувачів, яких хочете закріпити на сторінці \"Знайти\", ім'я в стовпчик."
pinnedPages: "Закріплені сторінки"
pinnedPagesDescription: "Введіть шляхи сторінок, які ви бажаєте закріпити на головній сторінці цього інстанса, розділені новими рядками."
pinnedClipId: "Ідентифікатор закріпленої замітки."
pinnedNotes: "Закріплена нотатка"
hcaptcha: "hCaptcha"
enableHcaptcha: "Увімкнути hCaptcha"
@@ -328,10 +343,12 @@ name: "Ім'я"
antennaSource: "Джерело антени"
antennaKeywords: "Ключові слова антени"
antennaExcludeKeywords: "Винятки"
antennaKeywordsDescription: "Розділення ключових слів пробілами для \"І\" або з нової лінійки для \"АБО\""
notifyAntenna: "Сповіщати про нові нотатки"
withFileAntenna: "Тільки нотатки з вкладеними файлами"
serviceworker: "ServiceWorker"
enableServiceworker: "Ввімкнути ServiceWorker"
antennaUsersDescription: "Список імя користувачів в стопчик"
caseSensitive: "З урахуванням регістру"
withReplies: "Включаючи відповіді"
connectedTo: "Наступні акаунти під'єднані"
@@ -455,13 +472,21 @@ objectStorage: "Object Storage"
useObjectStorage: "Використовувати object storage"
objectStorageBaseUrl: "Base URL"
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Будь ласка вкажіть назву відра в налаштованому сервісі."
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "Файли будуть зберігатись у розташуванні з цим префіксом."
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Залиште пустим при використанні AWS S3. Інакше введіть кінцевий пункт як '<host>' або '<host>:<port>' слідуючи інструкціям сервісу, який використовується."
objectStorageRegion: "Region"
objectStorageRegionDesc: "Введіть регіон у формі 'xx-east-1'. Залиште пустим, якщо ваш сервіс не різниться відповідно до регіонів, або введіть 'us-east-1'."
objectStorageUseSSL: "Використовувати SSL"
objectStorageUseSSLDesc: "Вимкніть коли не використовується HTTPS для з'єднання API"
objectStorageUseProxy: "Використовувати Proxy"
objectStorageUseProxyDesc: "Вимкніть коли проксі не використовується для з'єднання ObjectStorage"
objectStorageSetPublicRead: "Встановіть 'публічне читання' при завантаженні"
serverLogs: "Журнал сервера"
deleteAll: "Видалити все"
showFixedPostForm: "Показати форму запису над стрічкою новин."
newNoteRecived: "Є нові нотатки"
sounds: "Звуки"
listen: "Слухати"
@@ -494,6 +519,8 @@ deleteAllFiles: "Видалити всі файли"
deleteAllFilesConfirm: "Ви дійсно хочете видалити всі файли?"
removeAllFollowing: "Скасувати всі підписки"
removeAllFollowingDescription: "Скасувати підписку на всі акаунти з {host}. Будь ласка, робіть це, якщо інстанс більше не існує."
userSuspended: "Обліковий запис заблокований."
userSilenced: "Обліковий запис приглушений."
sidebar: "Бокова панель"
divider: "Розділювач"
addItem: "Додати елемент"
@@ -504,6 +531,7 @@ inboxUrl: "Inbox URL"
addedRelays: "Додані ретранслятори"
serviceworkerInfo: "Повинен бути ввімкнений для push-сповіщень."
deletedNote: "Видалена нотатка"
invisibleNote: "Приховані записи"
enableInfiniteScroll: "Увімкнути нескінченну прокрутку"
visibility: "Видимість"
poll: "Опитування"
@@ -514,8 +542,10 @@ expandTweet: "Розгорнути твіт"
themeEditor: "Редактор тем"
description: "Опис"
author: "Автор"
leaveConfirm: "Зміни не збережені. Ви дійсно хочете скасувати зміни?"
manage: "Управління"
plugins: "Плагіни"
pluginInstallWarn: "Будь ласка не встановлюйте плагінів яким ви не довіряєте."
deck: "Дек"
undeck: "Залишити Дек"
useBlurEffectForModal: "Ефект розмиття під модальними діалогами"
@@ -530,18 +560,26 @@ permission: "Права"
enableAll: "Увімкнути все"
disableAll: "Вимкнути все"
tokenRequested: "Надати доступ до акаунту"
pluginTokenRequestedDescription: "Цей плагін зможе використовувати дозволи які тут вказані."
notificationType: "Тип сповіщення"
edit: "Редагувати"
useStarForReactionFallback: "Використовувати ★ як запасний варіант, якщо емодзі реакції невідомий"
emailConfig: "Налаштування email сервера"
enableEmail: "Увімкнути функцію доставки пошти"
emailConfigInfo: "Використовується для підтвердження електронної пошти підчас реєстрації, а також для відновлення паролю."
email: "E-mail"
emailAddress: "E-mail адреса"
smtpConfig: "Налаштування сервера SMTP"
smtpHost: "Хост"
smtpPort: "Порт"
smtpUser: "Ім'я користувача"
smtpPass: "Пароль"
emptyToDisableSmtpAuth: "Залиште назву користувача і пароль пустими для вимкнення підтвердження SMTP"
smtpSecure: "Використовувати безумовне шифрування SSL/TLS для з'єднань SMTP"
smtpSecureInfo: "Вимкніть при використанні STARTTLS "
testEmail: "Тестовий email"
wordMute: "Блокування слів"
userSaysSomething: "{name} щось сказав(ла)"
makeActive: "Активувати"
display: "Відображення"
copy: "Скопіювати"
@@ -555,16 +593,24 @@ create: "Створити"
notificationSetting: "Параметри сповіщень"
notificationSettingDesc: "Виберіть типи сповіщень для відображення"
useGlobalSetting: "Застосувати глобальнi параметри"
useGlobalSettingDesc: "Якщо увімкнено, то будуть використовуватись налаштування повідомлень облікового запису, інакше можливо налаштувати індивідуально."
other: "Інше"
regenerateLoginToken: "Оновити Login Token"
regenerateLoginTokenDescription: "Регенерувати внутрішній ключ використовуваний під час входу. Зазвичай цього не потрібно робити. При регенерації всі пристрої вийдуть з системи."
setMultipleBySeparatingWithSpace: "Можна вказати кілька значень, відділивши їх пробілом."
fileIdOrUrl: "Ідентифікатор файлу або посилання"
chatOpenBehavior: "Поводження вікна переписки під час відкриття"
behavior: "Поведінка"
sample: "Приклад"
abuseReports: "Скарги"
reportAbuse: "Поскаржитись"
reportAbuseOf: "Поскаржитись на {name}"
fillAbuseReportDescription: "Будь ласка вкажіть подробиці скарги. Якщо скарга стосується запису, вкажіть посилання на нього."
abuseReported: "Дякуємо, вашу скаргу було відправлено. "
send: "Відправити"
abuseMarkAsResolved: "Позначити скаргу як вирішену"
openInNewTab: "Відкрити в новій вкладці"
openInSideView: "Відкрити збоку"
defaultNavigationBehaviour: "Поведінка навігації за замовчуванням"
editTheseSettingsMayBreakAccount: "Зміна цих параметрів може призвести до пошкодження вашого акаунта."
instanceTicker: "Мітка з назвою інстанса в нотатках"
@@ -576,6 +622,7 @@ desktop: "Десктоп"
clip: "Добірка"
createNew: "Створити новий"
optional: "Необов'язково"
createNewClip: "Створити нотатку"
public: "Публічний"
i18nInfo: "Misskey перекладається на різні мови волонтерами. Ви можете допомогти: {link}"
manageAccessTokens: "Керування токенами доступу"
@@ -597,9 +644,11 @@ driveFilesCount: "Кількість файлів на диску"
driveUsage: "Використання місця на диску"
noCrawle: "Заборонити індексацію"
noCrawleDescription: "Просити пошукові системи не індексувати ваш профіль, нотатки, сторінки тощо."
lockedAccountInfo: "Якщо видимість вашого запису не встановлена як \"Тільки підписники\", то кожен зможе побачити ваш запис, навіть якщо ви вимагаєте підтвердження підписок вручну."
alwaysMarkSensitive: "Позначати NSFW за замовчуванням"
loadRawImages: "Відображати вкладені зображення повністю замість ескізів"
disableShowingAnimatedImages: "Не програвати анімовані зображення"
verificationEmailSent: "Електронний лист з підтвердженням відісланий. Будь ласка перейдіть по посиланню в листі для підтвердження."
notSet: "Не налаштовано"
emailVerified: "Електронну пошту підтверджено."
noteFavoritesCount: "Кількість улюблених нотаток"
@@ -611,6 +660,40 @@ useSystemFont: "Використовувати стандартний шрифт
clips: "Добірка"
experimentalFeatures: "Експериментальні функції"
developer: "Розробник"
makeExplorable: "Зробіть обліковий запис видимим у розділі \"Огляд\""
makeExplorableDescription: "Вимкніть, щоб обліковий запис не показувався у розділі \"Огляд\"."
showGapBetweenNotesInTimeline: "Показувати розрив між записами у стрічці новин"
duplicate: "Дублікат"
left: "Лівий"
center: "Центр"
wide: "Широкий"
narrow: "Вузький"
reloadToApplySetting: "Налаштування ввійде в дію при перезавантаженні. Перезавантажити?"
showTitlebar: "Показати титульний рядок"
clearCache: "Очистити кеш"
onlineUsersCount: "{n} користувачів онлайн"
nUsers: "{n} Користувачів"
nNotes: "{n} Записів"
sendErrorReports: "Надіслати звіт про помилки"
sendErrorReportsDescription: "При увімкненні детальна інформація про помилки буде надана Misskey у разі виникнення проблем, що дасть можливість покращити Misskey."
myTheme: "Моя тема"
backgroundColor: "Фон"
accentColor: "Акцент"
textColor: "Текст"
saveAs: "Зберегти як…"
advanced: "Розширені"
value: "Значення"
updatedAt: "Останнє оновлення"
saveConfirm: "Зберегти зміни?"
deleteConfirm: "Ви дійсно бажаєте це видалити?"
invalidValue: "Некоректне значення."
registry: "Реєстр"
closeAccount: "Закрити обліковий запис"
_registry:
key: "Ключ"
keys: "Ключі"
domain: "Домен"
createKey: "Створити ключ"
_aboutMisskey:
about: "Misskey - це програмне забезпечення з відкритим кодом, яке розробляє syuilo з 2014 року."
contributors: "Головні помічники"
@@ -649,10 +732,14 @@ _mfm:
inlineMath: "Формула (у рядку)"
inlineMathDescription: "Відображення математичних формул (KaTeX) у рядку"
blockMath: "Формули (блок)"
blockMathDescription: "Відображати багаторядкові формули (KaTeX) блоками"
quote: "Цитата"
quoteDescription: "Відображає зміст як цитату."
emoji: "Кастомні емоджі"
emojiDescription: "Щоб показати нетиповий емоджі, потрібно ввести його назву в двокрапках."
search: "Пошук"
searchDescription: "Відображає вікно пошуку з попередньо введеним текстом"
flip: "Перевернути"
flipDescription: "Віддзеркалює вміст по горизонталі або вертикалі"
jelly: "Анімація (желе)"
jellyDescription: "Створює желеподібну анімацію"
@@ -664,6 +751,16 @@ _mfm:
shake: "Анімація (Shake)"
twitch: "Анімація (Twitch)"
spin: "Анімація (Spin)"
x2: "Великий"
x2Description: "Показує контент збільшеним."
x3: "Дуже великий"
x3Description: "Показує контент ще більшим."
x4: "Надзвичайно великий"
x4Description: "Показує контент надзвичайно великим."
blur: "Розмиття"
blurDescription: "Цей ефект зробить контент розмитим. Контент можна зробити чітким, якщо навести на нього вказівник миші."
font: "Шрифт"
fontDescription: "Встановлює шрифт для контенту."
_reversi:
reversi: "Реверсі"
gameSettings: "Налаштування гри"
@@ -692,6 +789,9 @@ _reversi:
myGames: "Мої ігри"
allGames: "Усі ігри"
ended: "Завершено"
playing: "В даний момент у процесі гри"
isLlotheo: "Гравець з найменшою кількістю фігур виграє (Llotheo)"
canPutEverywhere: "Фігури можна ставити в будь якії позиції"
_instanceTicker:
none: "Не відображати"
remote: "Відображати для віддалених користувачів"
@@ -706,6 +806,7 @@ _channel:
setBanner: "Встановити банер"
removeBanner: "Видалити банер"
featured: "Тренди"
following: "Підписки"
usersCount: "{n} учасників"
notesCount: "{n} дописів"
_sidebar:
@@ -713,12 +814,32 @@ _sidebar:
icon: "Аватар"
hide: "Сховати"
_wordMute:
muteWords: "Заглушені слова"
muteWordsDescription: "Розділення ключових слів пробілами для \"І\" або з нової лінійки для \"АБО\""
muteWordsDescription2: "Для використання RegEx, ключові слова потрібно вписати поміж слешів \"/\"."
softDescription: "Приховати записи які відповідають критеріям зі стрічки подій."
hardDescription: "Приховати записи які відповідають критеріям зі стрічки подій. Також приховані записи не будуть додані до стрічки подій навіть якщо критерії буде змінено."
soft: "М'яко"
hard: "Жорстко"
mutedNotes: "Заблоковані нотатки"
_theme:
explore: "Оглянути теми"
install: "Встановити тему"
manage: "Керування темами"
code: "Код теми"
installed: "Тему {name} встановлено"
installedThemes: "Встановлені теми"
builtinThemes: "Вбудоваі теми"
alreadyInstalled: "Тему вже встановлено"
invalid: "Неправильний формат теми"
make: "Створити тему"
base: "Основа"
defaultValue: "Значення за замовчуванням"
func: "Функції"
lighten: "Яскравість"
inputConstantName: "Введіть назву константи"
importInfo: "Вставляючи сюди код теми, ви можете добавити її до редактору тем"
deleteConstantConfirm: "Ви дійсно бажаєте видалити константу \"{const}\"?"
keys:
accent: "Акцент"
bg: "Фон"
@@ -738,13 +859,41 @@ _theme:
mention: "Згадка"
mentionMe: "Згадки (мене)"
renote: "Поширити"
modalBg: "Модальний фон"
divider: "Розділювач"
scrollbarHandle: "Ручка смуги прокрутки"
scrollbarHandleHover: "Ручка смуги прокрутки (при наведенні)"
dateLabelFg: "Текст позначок дати"
infoBg: "Фон інформації"
infoFg: "Текст інформації"
infoWarnBg: "Фон попередження"
infoWarnFg: "Текст попередження"
cwBg: "Фон чутливого змісту"
cwFg: "Текст чутливого змісту"
cwHoverBg: "Фон чутливого змісту (при наведенні)"
toastBg: "Фон повідомлення"
toastFg: "Текст повідомлення"
buttonBg: "Фон кнопки"
buttonHoverBg: "Фон кнопки (при наведенні)"
inputBorder: "Край поля вводу"
listItemHoverBg: "Фон елементу в списку (при наведенні)"
driveFolderBg: "Фон папки на диску"
wallpaperOverlay: "Накладання шпалер"
badge: "Значок"
messageBg: "Фон переписки"
accentDarken: "Акцент (Затемлений)"
accentLighten: "Акцент (Освітлений)"
fgHighlighted: "Виділений текст"
_sfx:
note: "Нотатки"
noteMy: "Мої нотатки"
notification: "Сповіщення"
chat: "Чати"
chatBg: "Чати (фон)"
antenna: "Прийом антени"
channel: "Повідомлення каналу"
reversiPutBlack: "Реверсі: хід Чорного"
reversiPutWhite: "Реверсі: хід Білого"
_ago:
unknown: "Невідомо"
future: "Майбутнє"
@@ -764,8 +913,26 @@ _time:
_tutorial:
title: "Як користуватись Misskey"
step1_1: "Ласкаво просимо!"
step1_2: "Ця сторінка має назву \"стрічка подій\". На ній з'являються записи користувачів на яких ви підписані."
step1_3: "Наразі ваша стрічка порожня, оскільки ви ще не написали жодної нотатки і не підписані на інших."
step2_1: "Перш ніж зробити запис або підписатись на когось, спочатку заповніть свій обліковий запис."
step2_2: "Надання деякої інформації про себе дозволить іншим користувачам підписатись на вас."
step3_1: "Ви успішно налаштували свій обліковий запис?"
step3_2: "Наступним кроком є написання нотатки. Це можна зробити, натиснувши зображення олівця на екрані."
step3_3: "Після написання вмісту ви можете опублікувати його, натиснувши кнопку у верхньому правому куті форми."
step3_4: "Не знаєте що написати? Спробуйте \"налаштовую свій msky\"!"
step4_1: "Ви розмістили свій перший запис?"
step4_2: "Ура! Ваш перший запис відображається на вашій стрічці подій."
step5_1: "Настав час оживити вашу стрічку подій підписавшись на інших користувачів."
step5_2: "{featured} показує популярні записи , а {explore} популярних користувачів з цього інстансу. Спробуйте підписатись на користувача, який вам сподобався!"
step5_3: "Щоб підписатись на інших користувачів, нажміть на їхнє зображення, а потім на кнопку \"підписатись\"."
step5_4: "Якщо користувач має замок при імені, то йому потрібно буде вручну підтвердити вашу заявку на підписку."
step6_1: "Тепер ви повинні бачити записи інших користувачів на вашій стрічці подій."
step6_2: "Також ви можете швидко відповісти, або \"відреагувати\" на записи інших користувачів."
step6_3: "Щоб \"відреагувати\", нажміть на знак плюс \"+\" на записі і виберіть емоджі яким ви хочете \"відреагувати\"."
step7_1: "Вітаю! Ви пройшли ознайомлення з Misskey."
step7_2: "Якщо ви хочете більше дізнатись про Misskey, зайдіть в розділ {help}."
step7_3: "Насолоджуйтесь Misskey! 🚀"
_2fa:
registerKey: "Зареєструвати новий ключ безпеки"
_permissions:
@@ -825,6 +992,7 @@ _widgets:
postForm: "Створення нотатки"
slideshow: "Слайд-шоу"
button: "Кнопка"
jobQueue: "Черга завдань"
_cw:
hide: "Сховати"
show: "Показати більше"
@@ -899,8 +1067,12 @@ _charts:
_instanceCharts:
requests: "Запити"
usersTotal: "Сумарна кількість користувачів"
notes: "Різниця кількості зроблених записів"
notesTotal: "Сумарна кількість нотаток"
ff: "Різниця кількості підписників"
ffTotal: "Кількість підписників"
cacheSizeTotal: "Сумарний розмір кешу"
files: "Різниця в кількості файлів"
filesTotal: "Сумарна кількість файлів"
_timelines:
home: "Домівка"
@@ -915,6 +1087,7 @@ _rooms:
exit: "Назад"
remove: "Видалити"
clear: "Видалити все"
clearConfirm: "Ви дійсно хочете позбутись усіх речей у вашій кімнаті?"
leaveConfirm: "Є незбережені зміни. Ви дійсно хочете вийти?"
chooseImage: "Виберіть зображення"
roomType: "Тип кімнати"
@@ -963,6 +1136,7 @@ _rooms:
sofa: "Диван"
spiral: "Гвинтові сходи"
bin: "Смітник"
cup-noodle: "Локшина в чашці"
holo-display: "Голографічний дисплей"
energy-drink: "Енергетичний напій"
doll-ai: "Лялька Аі-тян"
@@ -988,6 +1162,7 @@ _pages:
featured: "Популярні"
inspector: "Інспектор"
contents: "Вміст"
content: "Блок сторінки"
variables: "Змінні"
title: "Заголовок"
url: "URL сторінки"
@@ -1002,6 +1177,7 @@ _pages:
chooseBlock: "Додати блок"
selectType: "Виберіть тип"
enterVariableName: "Введіть назву для змінної"
variableNameIsAlreadyUsed: "Ця назва вже використовується іншою змінною"
contentBlocks: "Контент"
inputBlocks: "Ввід"
specialBlocks: "Особливе"
@@ -1017,6 +1193,7 @@ _pages:
post: "Створення нотатки"
_post:
text: "Вміст"
canvasId: "Ідентифікатор полотна"
textInput: "Введення тексту"
_textInput:
name: "Ім'я змінної"
@@ -1032,7 +1209,9 @@ _pages:
name: "Ім'я змінної"
text: "Назва"
default: "Значення за замовчуванням"
canvas: "Полотно"
_canvas:
id: "Ідентифікатор полотна"
width: "Ширина"
height: "Висота"
note: "Вбудована нотатка"
@@ -1052,7 +1231,10 @@ _pages:
inc: "Збільшити на"
_button:
text: "Напис"
colored: "Кольоровий"
action: "Дія кнопки"
_action:
dialog: "Показати повідомлення"
_dialog:
content: "Вміст"
resetRandom: "Скидання генератора випадковості"
@@ -1087,11 +1269,15 @@ _pages:
text: "Текст"
multiLineText: "Текст (багаторядковий)"
textList: "Текстовий список"
_textList:
info: "Використовувати новий рядок як роздільник для вводу"
strLen: "Довжина тексту"
_strLen:
arg1: "Текст"
strPick: "Вибрати символ"
_strPick:
arg1: "Текст"
arg2: "Розташування символу"
strReplace: "Заміна тексту"
_strReplace:
arg1: "Текст"
@@ -1224,17 +1410,25 @@ _pages:
aiScriptVar: "Змінна AiScript"
fn: "Функції"
_fn:
slots: "Паз"
slots-info: "Використовувати нову лінію як роздільник пазів"
arg1: "Вивід"
for: "Повторення"
_for:
arg1: "Кількість повторень"
arg2: "Дія"
typeError: "Паз {slot} приймає \"{expect}\" тип, але надана змінна має тип \"{actual}\"!"
thereIsEmptySlot: "Паз {slot} пустий!"
types:
string: "Текст"
number: "Число"
boolean: "Прапорець"
array: "Списки"
stringArray: "Текстовий список"
emptySlot: "Пустий паз"
enviromentVariables: "Змінні середовища"
pageVariables: "Елемент сторінки"
argVariables: "Стрічка вводу"
_relayStatus:
requesting: "Очікує затвердження"
accepted: "Затверджено"
@@ -1268,6 +1462,8 @@ _notification:
_deck:
alwaysShowMainColumn: "Завжди показувати головну колонку"
columnAlign: "Вирівняти стовпці"
columnMargin: "Відступ між стовпцями"
columnHeaderHeight: "Висота заголовку колони"
addColumn: "Додати стовпець"
swapLeft: "Пересунути ліворуч"
swapRight: "Пересунути праворуч"
@@ -1275,7 +1471,9 @@ _deck:
swapDown: "Пересунути вниз"
stackLeft: "У стовпчик вліво"
popRight: "Витягнути вправо"
profile: "Обліковий запис"
_columns:
main: "Головна"
widgets: "Віджети"
notifications: "Сповіщення"
tl: "Стрічка"

View File

@@ -1,5 +1,6 @@
---
_lang_: "中文(简体)"
headlineMisskey: "通过帖子连接在一起的网络"
introMisskey: "欢迎Misskey是一个开源的、去中心化的“微博客”服务。\n通过编写「帖文」来和大家分享你的以及你周围的事情吧📡\n通过「回应」功能可以让你快速地对大家的帖文表达反馈👍\n来探索新的世界吧🚀"
monthAndDay: "{month}月 {day}日"
search: "搜索"
@@ -49,6 +50,7 @@ copyUsername: "复制用户名"
searchUser: "搜索用户"
reply: "回复"
loadMore: "查看更多"
showMore: "查看更多"
youGotNewFollower: "你有新的关注者"
receiveFollowRequest: "您收到了关注请求"
followRequestAccepted: "您的关注请求被通过了"
@@ -109,12 +111,12 @@ unmarkAsSensitive: "取消标记为敏感内容"
enterFileName: "请输入文件名"
mute: "屏蔽"
unmute: "解除屏蔽"
block: "屏蔽"
unblock: "取消屏蔽"
block: "拉黑"
unblock: "取消拉黑"
suspend: "冻结"
unsuspend: "解除冻结"
blockConfirm: "确定要屏蔽吗?"
unblockConfirm: "确定要解除屏蔽吗?"
blockConfirm: "确定要拉黑吗?"
unblockConfirm: "确定要解除拉黑吗?"
suspendConfirm: "要冻结吗?"
unsuspendConfirm: "要解除冻结吗?"
selectList: "选择列表"
@@ -183,8 +185,8 @@ clearCachedFilesConfirm: "确定要清除缓存文件?"
blockedInstances: "被阻拦的实例"
blockedInstancesDescription: "设定要阻拦的实例,以换行来进行分割。被阻拦的实例将无法与本实例进行交换通讯。"
muteAndBlock: "屏蔽/拉黑"
mutedUsers: "禁言用户"
blockedUsers: "已屏蔽用户"
mutedUsers: "已屏蔽用户"
blockedUsers: "被拉黑的用户"
noUsers: "无用户"
editProfile: "编辑资料"
noteDeleteConfirm: "要删除该帖子吗?"
@@ -197,7 +199,7 @@ default: "默认"
noCustomEmojis: "没有自定义表情符号"
noJobs: "没有任务"
federating: "联合中"
blocked: "已拦截"
blocked: "已拉黑"
suspended: "停止推流"
all: "全部"
subscribing: "已订阅"
@@ -637,10 +639,10 @@ followingCount: "正在关注数量"
followersCount: "关注者数量"
sentReactionsCount: "发送反应数"
receivedReactionsCount: "收到反应数"
pollVotesCount: "发起投票数"
pollVotedCount: "投票数"
yes: "确定"
no: "取消"
pollVotesCount: "问卷调查的投票数"
pollVotedCount: "问卷调查的被投票数"
yes: ""
no: ""
driveFilesCount: "磁盘文件数"
driveUsage: "磁盘空间用量"
noCrawle: "拒绝搜索器的索引"
@@ -670,6 +672,32 @@ center: "中央"
wide: "宽"
narrow: "窄"
reloadToApplySetting: "页面刷新后设置才会生效。是否现在刷新页面?"
showTitlebar: "显示标题栏"
clearCache: "清除缓存"
onlineUsersCount: "{n}人在线"
nUsers: "{n}用户"
nNotes: "{n}帖子"
sendErrorReports: "发送错误报告"
sendErrorReportsDescription: "启用后如果出现问题可以与Misskey共享详细的错误信息从而帮助提高软件的质量。"
myTheme: "我的主题"
backgroundColor: "背景"
accentColor: "强调色"
textColor: "文本"
saveAs: "另存为"
advanced: "高级"
value: "值"
updatedAt: "更新时间"
saveConfirm: "确定保存?"
deleteConfirm: "确定删除?"
invalidValue: "无效值。"
registry: "注册表"
closeAccount: "关闭账户"
_registry:
scope: "范围"
key: "主要"
keys: "主要"
domain: "域"
createKey: "创建键"
_aboutMisskey:
about: "Misskey是由syuilo于2014年开发的开源软件。"
contributors: "主要贡献者"
@@ -731,6 +759,16 @@ _mfm:
twitchDescription: "显示强烈颤抖的动画效果。"
spin: "动画(回转)"
spinDescription: "显示回转的动画效果。"
x2: "大"
x2Description: "以大尺寸显示内容。"
x3: "非常大"
x3Description: "以更大尺寸显示内容。"
x4: "最大"
x4Description: "以最大尺寸显示内容。"
blur: "模糊"
blurDescription: "产生模糊效果。将鼠标指针放在上面即可将内容显示出来。"
font: "字体"
fontDescription: "可以设置内容所使用的字体。"
_reversi:
reversi: "黑白棋"
gameSettings: "对局设置"
@@ -991,6 +1029,9 @@ _widgets:
postForm: "投稿窗口"
slideshow: "幻灯片展示"
button: "按钮"
onlineUsers: "在线用户"
jobQueue: "作业队列"
serverMetric: "服务器指标"
_cw:
hide: "隐藏"
show: "查看更多"
@@ -1056,7 +1097,7 @@ _exportOrImport:
allNotes: "所有帖子"
followingList: "关注中"
muteList: "屏蔽"
blockingList: "屏蔽"
blockingList: "拉黑"
userLists: "列表"
_charts:
federationInstancesIncDec: "联合:增加/减少"
@@ -1465,7 +1506,7 @@ _notification:
renote: "转发"
quote: "引用"
reaction: "回应"
pollVote: "问卷调查投票"
pollVote: "问卷调查投票"
receiveFollowRequest: "收到关注请求"
followRequestAccepted: "关注请求已接受"
groupInvited: "加入群组邀请"
@@ -1482,6 +1523,7 @@ _deck:
swapDown: "向下移动"
stackLeft: "向左折叠"
popRight: "向右弹出"
profile: "个人资料"
_columns:
main: "主列"
widgets: "小工具"

View File

@@ -1,12 +1,13 @@
---
_lang_: "繁體中文"
introMisskey: "歡迎! Misskey是一個開源的去中心化的社群網站。\n通過「貼文」來分享現在發生的事情吧 📡\n「反應」功能可以讓你快速的對大家的「帖子」來表達感情👍\n一起來探索新的世界吧 🚀"
headlineMisskey: "箋文連繫網絡"
introMisskey: "歡迎! Misskey是一個開源且去中心化的社群網絡。\n通過「箋文」分享周邊新鮮事並告訴其他人您的想法📡\n透過「情感」功能對大家的箋文表達情感👍\n一起來探索這個新的世界吧🚀"
monthAndDay: "{month}月 {day}日"
search: "搜尋"
notifications: "通知"
username: "使用者名稱"
password: "密碼"
fetchingAsApObject: "從 Fediverse 查詢中..."
fetchingAsApObject: "從聯邦宇宙取得中..."
ok: "OK"
gotIt: "知道了"
cancel: "取消"
@@ -31,38 +32,42 @@ save: "儲存"
users: "使用者"
addUser: "新增使用者"
favorite: "收藏"
favorites: "已加星號"
favorites: "已收藏"
unfavorite: "取消收藏"
favorited: "已添加至收藏夾"
alreadyFavorited: "已經有添加入收藏夾過了"
cantFavorite: "無法添加至收藏夾"
pin: "置頂"
unpin: "取消置頂"
copyContent: "複製內容"
copyLink: "複製連結"
delete: "刪除"
deleteAndEdit: "刪除並編輯"
deleteAndEditConfirm: "要刪除並再次編輯嗎?此文的所有反應,轉發和回覆也將會消失。"
addToList: "添加至清單"
deleteAndEditConfirm: "要刪除並再次編輯嗎?此文的所有情感、轉發和回覆也將會消失。"
addToList: "新增至清單"
sendMessage: "發送訊息"
copyUsername: "複製用戶名"
searchUser: "搜尋用戶"
reply: "回覆"
loadMore: "瀏覽更多"
loadMore: "載入更多"
showMore: "載入更多"
youGotNewFollower: "您有新的追隨者"
receiveFollowRequest: "收到追隨請求"
receiveFollowRequest: "您有新的追隨請求"
followRequestAccepted: "追隨請求已接受"
mention: "提及"
mentions: "提及"
directNotes: "私信"
importAndExport: "匯入 / 匯出"
directNotes: "指定使用者發佈"
importAndExport: "匯入匯出"
import: "匯入"
export: "匯出"
files: "檔案"
download: "下載"
driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?使用此附件的文也會跟著消失。"
unfollowConfirm: "確定要取消{name}的追隨嗎?"
driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?使用此附件的文也會跟著消失。\n"
unfollowConfirm: "確定要取消追隨{name}嗎?"
exportRequested: "已請求匯出。這可能會花一點時間。結束後檔案將會被放到雲端裡。"
importRequested: "已請求匯入。這可能會花一點時間"
lists: "清單"
noLists: "沒有清單"
noLists: "沒有任何清單"
note: "箋文"
notes: "箋文"
following: "追隨中"
@@ -74,28 +79,32 @@ error: "錯誤"
somethingHappened: "發生錯誤"
retry: "重試"
pageLoadError: "載入頁面失敗"
pageLoadErrorDescription: "這通常是因為網路錯誤或是瀏覽器快取殘留的原因。請先清除瀏覽器快取,稍後再重試"
enterListName: "輸入清單名稱"
privacy: "隱私"
makeFollowManuallyApprove: "手動審核追隨請求"
defaultNoteVisibility: "預設的筆記隱私權"
defaultNoteVisibility: "預設可見性"
follow: "追隨"
followRequest: "追隨請求"
followRequests: "追隨請求"
unfollow: "取消追隨"
followRequestPending: "追隨許可批准中"
enterEmoji: "輸入表情符號"
renote: "轉發貼文"
unrenote: "取消轉發貼文"
renote: "轉"
unrenote: "取消轉"
renoted: "轉發成功"
cantRenote: "無法轉發此箋文。"
cantReRenote: "無法轉發之前已經轉發過的內容"
quote: "引用"
pinnedNote: "已置頂的文"
pinnedNote: "已置頂的文"
you: "您"
clickToShow: "按一下以顯示"
sensitive: "敏感內容"
add: "新增"
reaction: "反應"
reaction: "情感"
reactionSettingDescription: "置頂「反應」表情符號\n"
rememberNoteVisibility: "記住筆記隱私設定"
reactionSettingDescription2: "拖動以重新列序,點擊以刪除,按下 + 添加。"
rememberNoteVisibility: "記住箋文可見性"
attachCancel: "移除附件"
markAsSensitive: "標記為敏感內容"
unmarkAsSensitive: "取消標記為敏感內容"
@@ -105,7 +114,7 @@ unmute: "解除靜音"
block: "封鎖"
unblock: "解除封鎖"
suspend: "凍結"
unsuspend: "解"
unsuspend: "解除凍結"
blockConfirm: "確定要封鎖此用戶?"
unblockConfirm: "確定解除封鎖此用戶?"
suspendConfirm: "確定凍結此帳號?"
@@ -114,7 +123,7 @@ selectList: "選擇清單"
selectAntenna: "選擇天線"
selectWidget: "選擇小工具"
editWidgets: "編輯小工具"
editWidgetsExit: "停止編輯"
editWidgetsExit: "完成"
customEmojis: "自訂表情符號"
emoji: "表情符號"
emojiName: "表情符號名稱"
@@ -122,11 +131,13 @@ emojiUrl: "表情符號URL"
addEmoji: "新增表情符號"
settingGuide: "推薦設定"
cacheRemoteFiles: "緩存非遠程檔案"
cacheRemoteFilesDescription: "禁用此設定會停止遠端檔案的緩存,從而節省儲存空間但資料會因直接連線從而產生額外連接數據。"
cacheRemoteFilesDescription: "禁用此設定會停止遠端檔案的緩存,從而節省儲存空間但資料會因直接連線從而產生額外連接數據。"
flagAsBot: "此使用者是機器人"
flagAsBotDescription: "如果本帳戶是由程式控制請啟用此選項。啟用後會作為標示幫助其他開發者防止機器人之間產生無限互動的行為並會調整Misskey內部系統將本帳戶識別為機器人"
flagAsCat: "此使用者是貓"
autoAcceptFollowed: "自動許可追隨"
addAcount: "新增帳號"
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
autoAcceptFollowed: "自動追隨中使用者的追隨請求"
addAcount: "新增帳戶"
loginFailed: "登入失敗"
showOnRemote: "轉到所在實例顯示"
general: "一般"
@@ -134,15 +145,15 @@ wallpaper: "桌布"
setWallpaper: "設定桌布"
removeWallpaper: "移除桌布"
searchWith: "搜尋: {q}"
youHaveNoLists: "沒有任何清單"
followConfirm: "你真的要關注{name}嗎?"
youHaveNoLists: "沒有任何清單"
followConfirm: "你真的要追隨{name}嗎?"
proxyAccount: "代理帳號"
proxyAccountDescription: "代理帳號是在某些情況下充當其他服務器用戶的帳號。例如,當用戶將一個來自其他服務器的帳號放在列表中時,由於沒有其他用戶關注該帳號,該指令不會傳送到該服務器上,因此會由代理帳戶關注。"
host: "主機"
selectUser: "選取使用者"
recipient: "發送至"
recipient: "收件人"
annotation: "註解"
federation: "聯邦宇宙"
federation: "站台聯邦"
instances: "實例"
registeredAt: "初次觀測"
latestRequestSentAt: "上次發送的請求"
@@ -156,7 +167,7 @@ blockThisInstance: "封鎖此實例"
operations: "操作"
software: "軟體"
version: "版本"
metadata: "元資Metadata"
metadata: "元資"
withNFiles: "{n}個檔案"
monitor: "監視器"
jobQueue: "佇列"
@@ -172,13 +183,13 @@ clearCachedFiles: "清除快取資料"
clearCachedFilesConfirm: "確定要清除緩存資料嗎?"
blockedInstances: "已封鎖的實例"
blockedInstancesDescription: "請逐行輸入需要封鎖的實例。已封鎖的實例將無法與本實例進行通訊。"
muteAndBlock: "靜音/封鎖"
muteAndBlock: "靜音封鎖"
mutedUsers: "已靜音用戶"
blockedUsers: "已封鎖用戶"
noUsers: "無用戶"
noUsers: "沒有任何使用者"
editProfile: "編輯個人檔案"
noteDeleteConfirm: "確定刪除此文嗎?"
pinLimitExceeded: "不能置頂更多的貼文了"
noteDeleteConfirm: "確定刪除此文嗎?"
pinLimitExceeded: "不能置頂更多文了"
intro: "Misskey 部署完成!請開設管理員帳號!"
done: "完成"
processing: "處理中"
@@ -191,30 +202,31 @@ blocked: "已封鎖"
suspended: "已凍結"
all: "全部"
subscribing: "訂閱中"
publishing: "現正直播"
publishing: "直播"
notResponding: "沒有回應"
instanceFollowing: "追蹤實例"
instanceFollowers: "追蹤實例"
instanceUsers: "用戶"
changePassword: "修改密碼"
security: "安全性"
retypedNotMatch: "不相符的輸入內容"
currentPassword: "現在的密碼"
newPassword: "新密碼"
newPasswordRetype: "新的密碼(再輸入一次)"
retypedNotMatch: "兩次輸入不一致。"
currentPassword: "目前密碼"
newPassword: "新密碼"
newPasswordRetype: "確認密碼"
attachFile: "添加附件"
more: "更多!"
featured: "精選"
usernameOrUserId: "使用者名稱或使用者 ID"
usernameOrUserId: "使用者名稱或用戶ID"
noSuchUser: "使用者不存在"
lookup: "查詢"
announcements: "公告"
imageUrl: "圖片URL"
remove: "刪除"
removed: "成功移除"
removed: "已刪除"
removeAreYouSure: "確定要刪掉「{x}」嗎?"
deleteAreYouSure: "確定要刪掉「{x}」嗎?"
saved: "已保存"
resetAreYouSure: "確定要重設嗎?"
saved: "已儲存"
messaging: "傳送訊息"
upload: "上傳"
fromDrive: "從雲端"
@@ -259,9 +271,9 @@ folderName: "資料夾名稱"
createFolder: "新增資料夾"
renameFolder: "重新命名資料夾"
deleteFolder: "刪除資料夾"
addFile: "添加檔案"
addFile: "添加附件"
emptyDrive: "雲端硬碟為空"
emptyFolder: "空的資料夾"
emptyFolder: "資料夾為空"
unableToDelete: "無法刪除"
inputNewFileName: "輸入檔案名稱"
inputNewFolderName: "輸入新資料夾的名稱"
@@ -269,7 +281,7 @@ circularReferenceFolder: "目標文件夾是您要移動的文件夾的子文件
hasChildFilesOrFolders: "此文件夾不是空的,無法刪除。"
copyUrl: "複製URL"
rename: "重新命名"
avatar: "頭像"
avatar: "大頭貼"
banner: "橫幅"
nsfw: "敏感內容"
whenServerDisconnected: "與服務器的連接中斷時"
@@ -283,11 +295,11 @@ accept: "接受"
reject: "拒絕"
normal: "正常"
instanceName: "實例名稱"
instanceDescription: "實例描述"
instanceDescription: "實例說明"
maintainerName: "管理員名稱"
maintainerEmail: "管理員箱"
maintainerEmail: "管理員箱"
tosUrl: "服務條款URL"
thisYear: "年"
thisYear: "年"
thisMonth: "本月"
today: "本日"
dayX: "{day}日"
@@ -298,7 +310,7 @@ integration: "整合"
connectSerice: "連線"
disconnectSerice: "中斷連線"
enableLocalTimeline: "開啟本地時間軸"
enableGlobalTimeline: "開啟全球時間軸"
enableGlobalTimeline: "啟用公開時間軸"
disablingTimelinesInfo: "即使您禁用了時間線功能,管理員和協調人仍可以繼續使用,以方便您。"
registration: "註冊"
enableRegistration: "開啟新用戶註冊"
@@ -313,7 +325,9 @@ bannerUrl: "橫幅圖片URL"
basicInfo: "基本資訊"
pinnedUsers: "置頂用戶"
pinnedUsersDescription: "在「發現」頁面中使用換行標記想要置頂的用戶。"
pinnedNotes: "已置頂的貼文"
pinnedPages: "釘選頁面"
pinnedPagesDescription: "輸入要固定至實例首頁的頁面路徑,以換行符分隔。"
pinnedNotes: "已置頂的箋文"
hcaptcha: "hCaptcha"
enableHcaptcha: "啟用 hCaptcha"
hcaptchaSiteKey: "網站金鑰"
@@ -330,7 +344,7 @@ antennaSource: "接收來源"
antennaKeywords: "包含關鍵字"
antennaExcludeKeywords: "排除關鍵字"
antennaKeywordsDescription: "用空格分隔指定AND、用換行符分隔指定OR"
notifyAntenna: "通知有新的貼文"
notifyAntenna: "通知有新文"
withFileAntenna: "僅帶有附件的箋文"
serviceworker: "ServiceWorker"
enableServiceworker: "開啟 ServiceWorker"
@@ -338,7 +352,7 @@ antennaUsersDescription: "指定用換行符分隔的用戶名"
caseSensitive: "區分大小寫"
withReplies: "包含回覆"
connectedTo: "您的帳號已連接到以下社交帳號"
notesAndReplies: "文與回覆"
notesAndReplies: "文與回覆"
withFiles: "附件"
silence: "禁言"
silenceConfirm: "確定要禁言此用戶嗎?"
@@ -355,7 +369,7 @@ userList: "清單"
about: "資訊"
aboutMisskey: "關於 Misskey"
administrator: "管理員"
token: "令牌"
token: "權杖"
twoStepAuthentication: "雙重身份驗證"
moderator: "板主"
nUsersMentioned: "提到了{n}"
@@ -374,7 +388,7 @@ notFoundDescription: "找不到與指定URL回應的頁面"
uploadFolder: "預設上傳資料夾"
cacheClear: "清除快取"
markAsReadAllNotifications: "標記所有通知為已讀"
markAsReadAllUnreadNotes: "標記所有文為已讀"
markAsReadAllUnreadNotes: "標記所有文為已讀"
markAsReadAllTalkMessages: "標記所有訊息為已讀"
help: "幫助"
inputMessageHere: "在此輸入訊息"
@@ -397,7 +411,7 @@ next: "下一步"
retype: "重新輸入"
noteOf: "{user}的箋文"
inviteToGroup: "邀請至群組"
maxNoteTextLength: "文的字數限制"
maxNoteTextLength: "文的字數限制"
quoteAttached: "引用"
quoteQuestion: "是否要引用?"
noMessagesYet: "沒有訊息"
@@ -428,6 +442,7 @@ useOsNativeEmojis: "使用OS原生表情符號"
youHaveNoGroups: "找不到群組"
joinOrCreateGroup: "請加入現有群組,或創建新群組。"
noHistory: "沒有歷史紀錄"
signinHistory: "登錄歷史"
disableAnimatedMfm: "禁用MFM動畫"
doing: "正在進行"
category: "類別"
@@ -437,7 +452,9 @@ createAccount: "建立帳戶"
existingAcount: "現有帳戶"
regenerate: "再生"
fontSize: "字體大小"
noFollowRequests: "沒有要求跟隨您的申請"
openImageInNewTab: "於新分頁中開啟圖片"
dashboard: "儀表板"
local: "本地"
remote: "遠端"
total: "合計"
@@ -446,10 +463,10 @@ dayOverDayChanges: "與前一日相比"
appearance: "外觀"
clientSettings: "用戶端設定"
accountSettings: "帳號設定"
promotion: "推廣貼文"
promotion: "推廣"
promote: "推廣"
numberOfDays: "有效天數"
hideThisNote: "隱藏此文"
hideThisNote: "隱藏此文"
showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦"
objectStorageBaseUrl: "Base URL"
objectStorageBucket: "儲存空間Bucket"
@@ -459,14 +476,18 @@ objectStorageEndpointDesc: "如要使用AWS S3請留空。否則請根據伺
objectStorageRegion: "地域Region"
objectStorageUseSSL: "使用SSL"
objectStorageUseProxy: "使用網路代理"
objectStorageSetPublicRead: "上載時設定為\"public-read\""
serverLogs: "伺服器日誌"
deleteAll: "刪除所有記錄"
newNoteRecived: "有新的箋文"
showFixedPostForm: "於時間軸頁頂顯示「發送箋文」方框"
newNoteRecived: "發現新的箋文"
sounds: "音效"
listen: "聆聽"
none: "無"
showInPage: "在頁面中顯示"
popout: "彈出型窗口"
volume: "音量"
masterVolume: "主音量"
details: "詳細資訊"
chooseEmoji: "選擇您的表情符號\n"
unableToProcess: "操作無法完成"
@@ -484,10 +505,12 @@ descendingOrder: "降冪"
scratchpad: "暫存記憶體"
output: "輸出"
script: "腳本"
disablePagesScript: "停用頁面的AiScript腳本"
updateRemoteUser: "更新非本地用戶資料"
deleteAllFiles: "刪除所有檔案"
deleteAllFilesConfirm: "要删除所有檔案吗?"
removeAllFollowing: "解除所有追隨"
removeAllFollowingDescription: "解除{host}所有的跟隨。在實例不再存在時執行。"
userSuspended: "該用戶已被凍結"
userSilenced: "該用戶已被禁言。"
sidebar: "側邊列"
@@ -499,10 +522,10 @@ addRelay: "添加中繼"
inboxUrl: "私信URL"
addedRelays: "已添加的中繼"
serviceworkerInfo: "您需要啟用推送通知"
deletedNote: "已删除的文"
deletedNote: "已删除的文"
invisibleNote: "隱藏的帖子"
enableInfiniteScroll: "啟用自動滾動頁面模式"
visibility: "公開範圍"
visibility: "可見性"
poll: "投票"
useCw: "隱藏內容"
enablePlayer: "打開播放器"
@@ -517,6 +540,8 @@ plugins: "插件"
pluginInstallWarn: "請不要安裝來源不明的插件。"
deck: "多欄模式"
undeck: "取消多欄模式"
useBlurEffectForModal: "在模態框使用模糊效果"
useFullReactionPicker: "使用大尺寸的情感選擇欄"
width: "寬度"
height: "高度"
large: "大"
@@ -542,6 +567,8 @@ smtpPass: "密碼"
emptyToDisableSmtpAuth: "留空使用者名稱和密碼以禁用SMTP驗證。"
testEmail: "郵件測試發送"
wordMute: "靜音文字"
userSaysSomething: "{name}說了什麼"
makeActive: "啟用"
display: "檢視"
copy: "複製"
metrics: "指標"
@@ -552,84 +579,182 @@ database: "資料庫"
channel: "頻道"
create: "新增"
notificationSetting: "通知設定"
notificationSettingDesc: "選擇顯示通知的類型"
other: "其他"
regenerateLoginTokenDescription: "再生用於登入的內部權杖。一般情況下是不需要這樣做的。一旦再生,所有裝置將會被登出。"
fileIdOrUrl: "文檔ID或者URL"
chatOpenBehavior: "開啟聊天窗口時的行為"
behavior: "行為"
sample: "範例 "
abuseReports: "檢舉"
reportAbuse: "檢舉"
reportAbuseOf: "檢舉{name}"
fillAbuseReportDescription: "請填寫檢舉的詳細理由。可以的話請附上針對的URL網址。"
abuseReported: "內容已經發送。感謝您的報告。"
send: "發送"
abuseMarkAsResolved: "處理完畢"
openInNewTab: "在新分頁中開啟"
instanceTicker: "箋文的實例資訊"
openInSideView: "在側欄中開啟"
editTheseSettingsMayBreakAccount: "修改這些設定可能會毀壞您的帳戶"
instanceTicker: "箋文的實例來源"
waitingFor: "等待{x}"
random: "隨機"
system: "系統"
switchUi: "切換界面"
desktop: "桌面"
clip: "片段"
createNew: "新建"
optional: "可選"
public: "公開"
i18nInfo: "Misskey已經被志願者們翻譯成各種語言版本如果想要幫忙的話可以進入{link}幫助翻譯。"
manageAccessTokens: "管理存取權杖"
accountInfo: "帳戶資訊"
notesCount: "箋文數量"
repliesCount: "回覆數量\n"
renotesCount: "轉發數量"
repliedCount: "回覆數量"
renotedCount: "轉發次數"
followingCount: "正在跟隨的用戶數量"
followersCount: "跟隨者數量"
sentReactionsCount: "情感發送次數"
receivedReactionsCount: "情感收到次數"
yes: "確定"
no: "取消"
driveFilesCount: "雲端硬碟檔案數量"
driveUsage: "雲端硬碟使用量"
noCrawleDescription: "請求網路搜尋引擎不要索引你的個人資料頁、箋文及頁面等。"
lockedAccountInfo: "即使你通過了追隨者請求,除非你將筆記的公開範圍設定為 「追隨者」,否則任何人都能看見你的箋文。"
lockedAccountInfo: "即使你通過了追隨者請求,除非你將箋文的可見性設定為 「追隨者」,否則任何人都能看見你的箋文。"
loadRawImages: "以原始圖像質量顯示附件圖像的縮略圖"
disableShowingAnimatedImages: "不播放動態圖像"
notSet: "未設定"
emailVerified: "已成功驗證您的電郵"
noteFavoritesCount: "收藏箋文的數目"
pageLikesCount: "頁面被喜歡次數"
pageLikedCount: "頁面被喜歡次數"
contact: "聯絡人"
useSystemFont: "使用系統默認的字型"
clips: "標籤"
experimentalFeatures: "測試中的功能"
developer: "開發者"
makeExplorable: "讓自己的帳戶能夠在“探索”版面顯示"
makeExplorableDescription: "如果關閉,帳戶將不會被顯示在\"探索\"版面中。"
showGapBetweenNotesInTimeline: "分開顯示時間線上的箋文。"
duplicate: "複製"
left: "左"
center: "向中央"
wide: "寬"
narrow: "窄"
reloadToApplySetting: "設定將會在頁面重新載入之後生效。要現在就重載頁面嗎?"
showTitlebar: "顯示標題列"
clearCache: "清除快取資料"
onlineUsersCount: "{n}人正在線上"
nUsers: "{n}用戶"
nNotes: "{n}箋文"
backgroundColor: "背景"
textColor: "文本"
advanced: "進階"
value: "數值 "
updatedAt: "最後更新"
_registry:
scope: "範圍"
key: "主要"
keys: "主要"
_aboutMisskey:
about: "Misskey是由syuilo於2014年開發的開源軟件。"
contributors: "主要貢獻者"
allContributors: "全體貢獻人員"
source: "原始碼"
translation: "翻譯Misskey"
donate: "捐贈給Misskey"
morePatrons: "感謝你們的支持、 幫助。 🥰"
patrons: "贊助者"
_nsfw:
respect: "隱藏敏感內容"
ignore: "不隱藏敏感內容"
force: "隱藏所有內容"
_mfm:
cheatSheet: "MFM代碼小抄"
intro: "MFM是Misskey專用的標記語言可以在Misskey中的各個位置使用。 您可以這裏看到MFM可用語法列表。"
mention: "提及"
hashtag: "#tag"
url: "URL"
link: "鏈接"
bold: "粗體"
small: "縮小"
center: "置中"
inlineMath: "數學公式(內嵌)"
inlineMathDescription: "顯示內嵌的KaTex數學公式。"
blockMath: "數學公式(方塊)"
quote: "引用"
emoji: "自訂表情符號"
search: "搜尋"
flip: "翻轉"
flipDescription: "將內容上下或左右翻轉。"
jelly: "動畫(果凍)"
jellyDescription: "顯示果凍一樣的動畫效果。"
bounce: "動畫(反彈)"
shake: "動畫(搖晃)"
twitch: "動畫(顫抖)"
spin: "動畫(旋轉)"
spinDescription: "顯示旋轉的動畫效果。"
x2: "大"
x3: "較大"
x3Description: "放大顯示內容。"
x4: "最大"
x4Description: "將顯示內容放至最大。"
blur: "模糊"
font: "字型"
fontDescription: "可設置顯示內容所使用的字型"
_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: "正在對弈"
_instanceTicker:
none: "隱藏"
remote: "向遠端使用者顯示"
always: "總是顯示"
_serverDisconnectedBehavior:
reload: "自動重載"
dialog: "以對話框警告"
quiet: "適當地警告"
dialog: "彈出式警告"
quiet: "非侵入式警告"
_channel:
create: "建立頻道"
edit: "編輯頻道"
setBanner: "設定橫幅"
removeBanner: "移除封面圖"
featured: "流行"
featured: "發燒內容"
owned: "管理中"
following: "關注中"
usersCount: "有{n}人參與"
notesCount: "有{n}個帖子"
_sidebar:
full: "全部"
icon: "頭像"
hide: "隱藏"
_wordMute:
@@ -637,11 +762,23 @@ _wordMute:
softDescription: "隱藏時間軸中指定條件的箋文。"
mutedNotes: "已靜音的箋文"
_theme:
explore: "取得佈景主題"
install: "安裝佈景主題"
manage: "佈景主題管理員"
code: "主題代碼"
installed: "{name}已安裝"
installedThemes: "已經安裝的主題"
builtinThemes: "標準主題"
alreadyInstalled: "此主題已經安裝"
invalid: "主題格式錯誤"
make: "製作主題"
base: "基於"
constant: "常數"
defaultValue: "預設值"
color: "顏色"
func: "函数"
argument: "引數"
funcKind: "功能類型"
argument: "參數"
alpha: "透明度"
darken: "暗度"
lighten: "亮度"
@@ -649,21 +786,38 @@ _theme:
bg: "背景"
fg: "文本"
shadow: "陰影"
navIndicator: "側邊欄指示符"
link: "鏈接"
hashtag: "#tag"
mention: "提及"
mentionMe: "提及我"
renote: "轉發文"
renote: "轉發文"
divider: "分割線"
scrollbarHandle: "滾動條"
scrollbarHandleHover: "滾動條 (漂浮)"
dateLabelFg: "日期標籤文字"
infoBg: "資訊背景"
infoFg: "資訊內容"
infoWarnBg: "警告背景"
infoWarnFg: "警告字元"
cwBg: "CW 按鈕背景"
cwFg: "CW 按鈕文本"
cwHoverBg: "CW 按鈕背景 (漂浮)"
buttonBg: "按鈕背景"
buttonHoverBg: "按鈕背景 (漂浮)"
inputBorder: "輸入框邊框"
listItemHoverBg: "列表物品背景 (漂浮)"
driveFolderBg: "雲端硬碟文件夾背景"
messageBg: "私信背景"
accentDarken: "強調色(偏暗)"
accentLighten: "強調色(明亮)"
fgHighlighted: "高亮顯示文本"
_sfx:
note: "箋文"
noteMy: "我的箋文"
notification: "通知"
chat: "傳送訊息"
antenna: "天線接收"
channel: "頻道通知"
_ago:
unknown: "未知"
@@ -684,28 +838,33 @@ _time:
_tutorial:
title: "Misskey使用方法"
step1_1: "歡迎!"
step1_2: "此為「時間軸」頁面,它會按照時間順序顯示你「追隨」的人的「文」"
step1_3: "由於你沒有發任何筆記,也沒有追隨任何人,所以你的時間軸目前是空的。"
step1_2: "此為「時間軸」頁面,它會按照時間順序顯示你「追隨」的人發出的「文」"
step1_3: "由於你沒有發任何箋文,也沒有追隨任何人,所以你的時間軸目前是空的。"
step2_1: "在發文或追隨其他人之前先讓我們設定一下個人資料吧。"
step2_2: "提供一些關於自己的資訊來讓其他人更有追隨你的意願。"
step3_1: "個人資料都打理好了嗎?"
step3_2: "下一步讓我們來試試看發個文,按一下畫面上的鉛筆圖示來開始"
step3_3: "輸入完內容後,按視窗右上角的按鈕來發文"
step3_4: "不知道該寫什麼內容嗎試試看「開始使用Misskey了」如何。"
step4_1: "筆記發出去了嗎?"
step4_1: "箋文發出去了嗎?"
step4_2: "如果你的箋文出現在時間軸上,就代表發文成功。"
step5_1: "現在試試看追隨其他人來讓你的時間軸變得更生動吧。"
step5_2: "你可以在{featured}上看到受歡迎的文,你也可以選擇從列表中追隨你喜歡的人,或者在{explore}上找到熱門使用者。"
step5_2: "你在{featured}上看到受歡迎的文,你也可以從列表中追隨你喜歡的人,或者在{explore}上找到熱門使用者。"
step5_3: "想要追隨其他人,只要點擊他們的頭像並按「追隨」即可。"
step5_4: "如果使用者的名字旁有鎖頭的圖示,代表他們需要手動核准你的追隨請求。"
step6_1: "現在你可以在時間軸上看到其他用戶的貼文"
step6_2: "你也可以在其他人的貼文上進行「反應」來表達簡單的回覆。"
step6_3: "在他人的貼文按下「+」的圖示即可選擇想要的表情符號來進行「反應」。"
step6_1: "現在你可以在時間軸上看到其他用戶的箋文。"
step6_2: "你也可以對別人的箋文作出「情感」,作出簡單的回覆。"
step7_1: "以上為Misskey的基本操作說明教學在此告一段落。辛苦了。"
step7_2: "歡迎到{help}來瞭解更多Misskey相關介紹。"
step7_3: "那麼祝您在Misskey玩的開心~ 🚀"
_2fa:
alreadyRegistered: "此設備已經被註冊過了"
registerDevice: "註冊裝置"
step1: "首先,在您的設備上安裝二步驗證程式,例如{a}或{b}。"
step2: "然後掃描熒幕上的QR code。"
_permissions:
"read:account": "查看帳戶信息"
"write:account": "更改帳戶信息"
"read:blocks": "已封鎖用戶名單"
"write:blocks": "編輯已封鎖用戶名單"
"read:drive": "存取雲端硬碟\n"
@@ -720,8 +879,8 @@ _permissions:
"write:notes": "撰寫或刪除箋文"
"read:notifications": "查看通知"
"write:notifications": "編輯通知"
"read:reactions": "查看反應"
"write:reactions": "編輯反應"
"read:reactions": "查看情感"
"write:reactions": "編輯情感"
"write:votes": "投票"
"read:pages": "顯示頁面"
"write:pages": "編輯頁面"
@@ -733,6 +892,11 @@ _permissions:
"write:channels": "編輯頻道"
_auth:
shareAccess: "要授權「“{name}”」存取您的帳戶嗎?"
shareAccessAsk: "您確定要授權這個應用程式使用您的帳戶嗎?"
permissionAsk: "此應用程式需要以下權限"
pleaseGoBack: "請返回至應用程式"
callback: "回到應用程式"
denied: "拒絕訪問"
_antennaSources:
all: "全部箋文"
homeTimeline: "來自已追隨使用者的箋文"
@@ -752,14 +916,18 @@ _widgets:
notifications: "通知"
timeline: "時間軸"
calendar: "行事曆"
trends: "發燒文"
trends: "發燒文"
clock: "時鐘"
rss: "RSS閱讀器"
activity: "動態"
photos: "照片"
digitalClock: "電子時鐘"
federation: "聯邦宇宙"
postForm: "發佈窗口"
slideshow: "幻燈片"
button: "按鈕"
onlineUsers: "在線上的用戶"
jobQueue: "佇列"
_cw:
hide: "隱藏"
show: "瀏覽更多"
@@ -767,6 +935,8 @@ _cw:
files: "{count} 個檔案"
_poll:
noOnlyOneChoice: "至少需要兩個選項。"
choiceN: "選擇{n}"
noMore: "沒辦法再添加選項了"
expiration: "期限"
infinite: "無期限"
at: "結束時間"
@@ -780,6 +950,7 @@ _poll:
voted: "已投票"
closed: "已結束"
remainingDays: "{d}天{h}小時後結束"
remainingHours: "{h}小時{m}分後結束"
_visibility:
public: "公開"
home: "首頁"
@@ -790,7 +961,7 @@ _visibility:
localOnlyDescription: "對遠端使用者隱藏"
_postForm:
replyPlaceholder: "回覆此箋文..."
quotePlaceholder: "引用此文..."
quotePlaceholder: "引用此文..."
channelPlaceholder: "發佈到頻道"
_placeholders:
a: "今天過得如何?"
@@ -804,10 +975,12 @@ _profile:
username: "使用者名稱"
description: "關於我"
youCanIncludeHashtags: "你也可以在「關於我」中加上 #tag"
metadata: "進階資訊"
metadataEdit: "編輯進階資訊"
metadataLabel: "標籤"
metadataContent: "内容"
_exportOrImport:
allNotes: "全部箋文"
allNotes: "所有箋文"
followingList: "追隨中"
muteList: "靜音"
blockingList: "封鎖"
@@ -840,7 +1013,7 @@ _timelines:
home: "首頁"
local: "本地"
social: "社群"
global: "全域"
global: "公開"
_rooms:
roomOf: "{user}的房間"
addFurniture: "擺放家具"
@@ -877,12 +1050,23 @@ _rooms:
book: "讀物"
book2: "讀物2"
piano: "鋼琴"
server: "伺服器"
moon: "月亮"
corkboard: "木栓板"
mousepad: "滑鼠墊"
monitor: "監視器"
keyboard: "鍵盤"
carpet-stripe: "條紋地毯"
mat: "地毯"
wall-clock: "壁鐘"
photoframe: "相框"
cube: "立方體"
tv: "電視"
pinguin: "企鵝蠟像"
poster-h: "海報(橫向)"
poster-v: "海報(直向)"
sofa: " 沙發"
spiral: "螺旋式樓梯"
bin: "垃圾箱"
cup-noodle: "杯面"
holo-display: "投影機"
@@ -892,9 +1076,14 @@ _rooms:
_pages:
newPage: "建立頁面"
editPage: "編輯頁面"
readPage: "正檢視原始碼"
created: "頁面已建立"
updated: "頁面已更新"
deleted: "頁面已被刪除"
pageSetting: "頁面設定"
nameAlreadyExists: "指定的頁面URL已經存在"
invalidNameTitle: "指定的頁面URL無效"
invalidNameText: "請確定是否為非空白"
editThisPage: "編輯此頁面"
viewSource: "檢視原始碼"
viewPage: "顯示頁面"
@@ -902,14 +1091,28 @@ _pages:
unlike: "收回喜歡"
my: "我的頁面"
liked: "已喜歡的頁面"
featured: "人氣"
inspector: "面板檢查"
contents: "內容"
content: "頁面方塊"
variables: "變數"
title: "標題"
url: "頁面網址"
summary: "頁面摘要"
alignCenter: "置中"
hideTitleWhenPinned: "被置頂於個人資料時隱藏頁面標題"
font: "字型"
fontSerif: "襯線體"
fontSansSerif: "無襯線體"
eyeCatchingImageSet: "設定封面影像"
eyeCatchingImageRemove: "刪除封面影像"
chooseBlock: "新增方塊"
selectType: "選擇類型"
enterVariableName: "請輸入變數名稱"
variableNameIsAlreadyUsed: "變數名稱已被佔用"
contentBlocks: "內容"
inputBlocks: "輸入"
specialBlocks: "特殊"
blocks:
text: "文本"
textarea: "文字區域"
@@ -919,6 +1122,7 @@ _pages:
if: "如果"
_if:
variable: "變數"
post: "發佈窗口"
_post:
text: "内容"
canvasId: "畫布ID"
@@ -978,6 +1182,7 @@ _pages:
default: "預設值"
script:
categories:
flow: "控制"
logical: "邏輯運算"
operation: "計算"
comparison: "對比"
@@ -993,6 +1198,7 @@ _pages:
textList: "文本列表"
_strLen:
arg1: "文本"
strPick: "提取字元"
_strPick:
arg1: "文本"
arg2: "字元位置"
@@ -1002,6 +1208,7 @@ _pages:
arg1: "本文"
_join:
arg1: "清單"
arg2: "分隔字元"
add: "加"
_add:
arg1: "A"
@@ -1018,6 +1225,7 @@ _pages:
_divide:
arg1: "A"
arg2: "B"
mod: "餘數"
_mod:
arg1: "A"
arg2: "B"
@@ -1112,7 +1320,6 @@ _pages:
aiScriptVar: "AiScript的變數"
fn: "函数"
_fn:
slots: "欄位"
arg1: "輸出"
_for:
arg1: "重複次數"
@@ -1129,9 +1336,14 @@ _relayStatus:
accepted: "已通過核准"
rejected: "已拒絕"
_notification:
youRenoted: "{name} 轉發了你的貼文"
youGotMention: "{name}提及到您"
youGotReply: "{name}回覆了您"
youGotQuote: "{name}引用了您"
youRenoted: "{name} 轉發了你的箋文"
youGotPoll: "{name}已投票"
youGotMessagingMessageFromUser: "{name}發送給您的訊息"
youWereFollowed: "您有新的追隨者"
youReceivedFollowRequest: "您有新的追隨請求"
yourFollowRequestAccepted: "您的追隨請求已通過"
youWereInvitedToGroup: "您有新的群組邀請"
_types:
@@ -1139,15 +1351,19 @@ _notification:
follow: "追隨中"
mention: "提及"
reply: "回覆"
renote: "轉發文"
renote: "轉發文"
quote: "引用"
reaction: "反應"
reaction: "情感"
pollVote: "統計已投票數"
receiveFollowRequest: "已收到追隨請求"
followRequestAccepted: "追隨請求已接受"
groupInvited: "加入社群邀請"
app: "應用程式通知"
_deck:
alwaysShowMainColumn: "總是顯示主欄"
columnAlign: "對齊欄位"
columnMargin: "列之間的邊距"
columnHeaderHeight: "欄位標題高度"
addColumn: "新增欄位"
swapLeft: "向左移動"
swapRight: "向右移動"
@@ -1155,7 +1371,9 @@ _deck:
swapDown: "往下移動"
stackLeft: "向左折疊"
popRight: "向右彈出"
profile: "個人檔案"
_columns:
main: "主列"
widgets: "小工具"
notifications: "通知"
tl: "時間軸"

View File

@@ -0,0 +1,22 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class registry1610277136869 implements MigrationInterface {
name = 'registry1610277136869'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "registry_item" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "key" character varying(1024) NOT NULL, "scope" character varying(1024) array NOT NULL DEFAULT '{}'::varchar[], "domain" character varying(512), CONSTRAINT "PK_64b3f7e6008b4d89b826cd3af95" PRIMARY KEY ("id")); COMMENT ON COLUMN "registry_item"."createdAt" IS 'The created date of the RegistryItem.'; COMMENT ON COLUMN "registry_item"."updatedAt" IS 'The updated date of the RegistryItem.'; COMMENT ON COLUMN "registry_item"."userId" IS 'The owner ID.'; COMMENT ON COLUMN "registry_item"."key" IS 'The key of the RegistryItem.'`);
await queryRunner.query(`CREATE INDEX "IDX_fb9d21ba0abb83223263df6bcb" ON "registry_item" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_22baca135bb8a3ea1a83d13df3" ON "registry_item" ("scope") `);
await queryRunner.query(`CREATE INDEX "IDX_0a72bdfcdb97c0eca11fe7ecad" ON "registry_item" ("domain") `);
await queryRunner.query(`ALTER TABLE "registry_item" ADD CONSTRAINT "FK_fb9d21ba0abb83223263df6bcb3" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "registry_item" DROP CONSTRAINT "FK_fb9d21ba0abb83223263df6bcb3"`);
await queryRunner.query(`DROP INDEX "IDX_0a72bdfcdb97c0eca11fe7ecad"`);
await queryRunner.query(`DROP INDEX "IDX_22baca135bb8a3ea1a83d13df3"`);
await queryRunner.query(`DROP INDEX "IDX_fb9d21ba0abb83223263df6bcb"`);
await queryRunner.query(`DROP TABLE "registry_item"`);
}
}

View File

@@ -0,0 +1,16 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class registry21610277585759 implements MigrationInterface {
name = 'registry21610277585759'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "registry_item" ADD "value" jsonb NOT NULL DEFAULT '{}'`);
await queryRunner.query(`COMMENT ON COLUMN "registry_item"."value" IS 'The value of the RegistryItem.'`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`COMMENT ON COLUMN "registry_item"."value" IS 'The value of the RegistryItem.'`);
await queryRunner.query(`ALTER TABLE "registry_item" DROP COLUMN "value"`);
}
}

View File

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

View File

@@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.64.1",
"version": "12.68.0",
"codename": "indigo",
"repository": {
"type": "git",
@@ -45,6 +45,8 @@
"@koa/cors": "3.1.0",
"@koa/multer": "3.0.0",
"@koa/router": "9.0.1",
"@sentry/browser": "5.29.2",
"@sentry/tracing": "5.29.2",
"@sinonjs/fake-timers": "6.0.1",
"@syuilo/aiscript": "0.11.1",
"@types/bcryptjs": "2.4.2",
@@ -87,7 +89,7 @@
"@types/pug": "2.0.4",
"@types/qrcode": "1.3.5",
"@types/random-seed": "0.3.3",
"@types/ratelimiter": "3.4.0",
"@types/ratelimiter": "3.4.1",
"@types/redis": "2.8.28",
"@types/rename": "1.0.2",
"@types/request-stats": "3.0.0",
@@ -96,6 +98,7 @@
"@types/sharp": "0.26.1",
"@types/sinonjs__fake-timers": "6.0.1",
"@types/speakeasy": "2.0.5",
"@types/throttle-debounce": "2.1.0",
"@types/tinycolor2": "1.4.2",
"@types/tmp": "0.2.0",
"@types/uuid": "8.3.0",
@@ -105,9 +108,9 @@
"@types/websocket": "1.0.1",
"@types/ws": "7.4.0",
"@typescript-eslint/parser": "4.10.0",
"@vue/compiler-sfc": "3.0.3",
"@vue/compiler-sfc": "3.0.5",
"abort-controller": "3.0.0",
"apexcharts": "3.22.3",
"apexcharts": "3.23.1",
"autobind-decorator": "2.4.0",
"autosize": "4.0.2",
"autwh": "0.1.0",
@@ -131,12 +134,12 @@
"diskusage": "1.1.3",
"double-ended-queue": "2.1.0-0",
"escape-regexp": "0.0.1",
"eslint": "7.16.0",
"eslint-plugin-vue": "7.3.0",
"eslint": "7.17.0",
"eslint-plugin-vue": "7.4.1",
"eventemitter3": "4.0.7",
"feed": "4.2.1",
"fibers": "5.0.0",
"file-type": "16.0.1",
"file-type": "16.1.0",
"fluent-ffmpeg": "2.1.2",
"glob": "7.1.6",
"got": "11.8.1",
@@ -145,7 +148,7 @@
"gulp-rename": "2.0.0",
"gulp-replace": "1.0.0",
"gulp-sourcemaps": "2.6.5",
"gulp-terser": "2.0.0",
"gulp-terser": "2.0.1",
"gulp-tslint": "8.1.4",
"gulp-typescript": "6.0.0-alpha.1",
"hard-source-webpack-plugin": "0.13.1",
@@ -164,7 +167,7 @@
"jsonld": "3.2.0",
"jsrsasign": "8.0.20",
"katex": "0.12.0",
"koa": "2.13.0",
"koa": "2.13.1",
"koa-bodyparser": "4.3.0",
"koa-favicon": "2.1.0",
"koa-json-body": "5.3.0",
@@ -192,9 +195,9 @@
"parsimmon": "1.16.0",
"pg": "8.5.1",
"portscanner": "2.2.0",
"postcss": "8.2.1",
"postcss": "8.2.4",
"postcss-loader": "4.1.0",
"prismjs": "1.22.0",
"prismjs": "1.23.0",
"probe-image-size": "6.0.0",
"promise-limit": "2.7.0",
"promise-sequential": "1.1.1",
@@ -204,7 +207,7 @@
"qrcode": "1.4.4",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.15.8",
"re2": "1.15.9",
"recaptcha-promise": "1.0.0",
"reconnecting-websocket": "4.4.0",
"redis": "3.0.2",
@@ -217,34 +220,35 @@
"rimraf": "3.0.2",
"rndstr": "1.0.0",
"s-age": "1.1.2",
"sass": "1.29.0",
"sass-loader": "10.1.0",
"sass": "1.32.4",
"sass-loader": "10.1.1",
"seedrandom": "3.0.5",
"sharp": "0.26.2",
"sharp": "0.27.0",
"speakeasy": "2.0.0",
"stringz": "2.1.0",
"style-loader": "2.0.0",
"summaly": "2.4.0",
"syslog-pro": "1.0.0",
"systeminformation": "4.31.1",
"systeminformation": "4.34.6",
"syuilo-password-strength": "0.0.1",
"textarea-caret": "3.1.0",
"three": "0.117.1",
"throttle-debounce": "3.0.1",
"tinycolor2": "1.4.2",
"tmp": "0.2.1",
"ts-loader": "8.0.11",
"ts-node": "9.1.0",
"ts-loader": "8.0.14",
"ts-node": "9.1.1",
"tslint": "6.1.3",
"tslint-sonarts": "1.9.0",
"typeorm": "0.2.29",
"typescript": "4.1.2",
"typeorm": "0.2.30",
"typescript": "4.1.3",
"ulid": "2.3.0",
"url-loader": "4.1.1",
"uuid": "8.3.2",
"v-debounce": "0.1.2",
"vanilla-tilt": "1.7.0",
"vue": "3.0.3",
"vue-color": "2.7.1",
"vue": "3.0.5",
"vue-color": "2.8.1",
"vue-json-pretty": "1.7.1",
"vue-loader": "16.0.0",
"vue-prism-editor": "2.0.0-alpha.2",
@@ -252,10 +256,10 @@
"vue-style-loader": "4.1.2",
"vuedraggable": "4.0.1",
"web-push": "3.4.4",
"webpack": "5.10.1",
"webpack-cli": "4.2.0",
"webpack": "5.13.0",
"webpack-cli": "4.3.1",
"websocket": "1.0.33",
"ws": "7.4.1",
"ws": "7.4.2",
"xev": "2.0.1"
},
"devDependencies": {

View File

@@ -7,7 +7,6 @@ import { waiting } from '@/os';
type Account = {
id: string;
token: string;
clientData: Record<string, any>;
};
const data = localStorage.getItem('account');

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -49,7 +49,7 @@ export default defineComponent({
const el = this.$slots.default({
item: item
})[0];
el.key = item.id;
if (el.key == null && item.id) el.key = item.id;
if (
i != this.items.length - 1 &&

View File

@@ -36,7 +36,7 @@ import { faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { faDownload, faLink, faICursor, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import MkDriveFileThumbnail from './drive-file-thumbnail.vue';
import bytes from '../filters/bytes';
import bytes from '@/filters/bytes';
import * as os from '@/os';
export default defineComponent({

View File

@@ -17,7 +17,7 @@
@dragenter="onDragenter"
@dragleave="onDragleave"
@drop.prevent.stop="onDrop"
@contextmenu="onContextmenu"
@contextmenu.stop="onContextmenu"
>
<div class="contents" ref="contents">
<div class="folders" ref="foldersContainer" v-show="folders.length > 0">

View File

@@ -2,7 +2,7 @@
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
<div class="omfetrab _popup" :class="['w' + width, 'h' + height, { big }]">
<input ref="search" class="search" :class="{ filled: q != null && q != '' }" v-model.trim="q" :placeholder="$ts.search" @paste.stop="paste" @keyup.enter="done()">
<div class="emojis">
<div class="emojis" ref="emojis">
<section class="result">
<div v-if="searchResultCustom.length > 0">
<button v-for="emoji in searchResultCustom"
@@ -180,6 +180,8 @@ export default defineComponent({
watch: {
q() {
this.$refs.emojis.scrollTop = 0;
if (this.q == null || this.q === '') {
this.searchResultCustom = [];
this.searchResultUnicode = [];

View File

@@ -0,0 +1,34 @@
<template>
<div class="xfbouadm" v-if="meta" :style="{ backgroundImage: `url(${ meta.backgroundImageUrl })` }">
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import * as os from '@/os';
export default defineComponent({
components: {
},
data() {
return {
meta: null,
};
},
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
},
});
</script>
<style lang="scss" scoped>
.xfbouadm {
background-position: center;
background-size: cover;
}
</style>

View File

@@ -11,6 +11,11 @@ export default defineComponent({
required: false,
default: 'span',
},
textTag: {
type: String,
required: false,
default: null,
},
},
render() {
let str = this.src;
@@ -32,6 +37,6 @@ export default defineComponent({
str = str.substr(nextBracketClose + 1);
}
return h(this.tag, parsed.map(x => typeof x === 'string' ? x : this.$slots[x.arg]()));
return h(this.tag, parsed.map(x => typeof x === 'string' ? (this.textTag ? h(this.textTag, x) : x) : this.$slots[x.arg]()));
}
});

View File

@@ -14,6 +14,15 @@ export default defineComponent({
</script>
<style lang="scss">
._mfm_blur_ {
filter: blur(6px);
transition: filter 0.3s;
&:hover {
filter: blur(0px);
}
}
@keyframes mfm-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }

View File

@@ -48,6 +48,11 @@ export default defineComponent({
const ast = (this.plain ? parsePlain : parse)(this.text);
const validTime = (t: string | null | undefined) => {
if (t == null) return null;
return t.match(/^[0-9.]+s$/) ? t : null;
};
const genEl = (ast: MfmForest) => concat(ast.map((token): VNode[] => {
switch (token.node.type) {
case 'text': {
@@ -86,17 +91,17 @@ export default defineComponent({
break;
}
case 'jelly': {
const speed = token.node.props.args.speed || '1s';
const speed = validTime(token.node.props.args.speed) || '1s';
style = (this.$store.state.animatedMfm ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
break;
}
case 'twitch': {
const speed = token.node.props.args.speed || '0.5s';
const speed = validTime(token.node.props.args.speed) || '0.5s';
style = this.$store.state.animatedMfm ? `animation: mfm-twitch ${speed} ease infinite;` : '';
break;
}
case 'shake': {
const speed = token.node.props.args.speed || '0.5s';
const speed = validTime(token.node.props.args.speed) || '0.5s';
style = this.$store.state.animatedMfm ? `animation: mfm-shake ${speed} ease infinite;` : '';
break;
}
@@ -109,7 +114,7 @@ export default defineComponent({
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';
const speed = validTime(token.node.props.args.speed) || '1.5s';
style = this.$store.state.animatedMfm ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : '';
break;
}
@@ -129,6 +134,35 @@ export default defineComponent({
style = `transform: ${transform};`;
break;
}
case 'x2': {
style = `font-size: 200%;`;
break;
}
case 'x3': {
style = `font-size: 400%;`;
break;
}
case 'x4': {
style = `font-size: 600%;`;
break;
}
case 'font': {
const family =
token.node.props.args.serif ? 'serif' :
token.node.props.args.monospace ? 'monospace' :
token.node.props.args.cursive ? 'cursive' :
token.node.props.args.fantasy ? 'fantasy' :
token.node.props.args.emoji ? 'emoji' :
token.node.props.args.math ? 'math' :
null;
if (family) style = `font-family: ${family};`;
break;
}
case 'blur': {
return h('span', {
class: '_mfm_blur_',
}, genEl(token.children));
}
}
if (style == null) {
return h('span', {}, ['[', token.node.props.name, ...genEl(token.children), ']']);

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,6 @@
v-hotkey="keymap"
v-size="{ max: [500, 450, 350, 300] }"
>
<XSub v-for="note in conversation" class="reply-to-more" :key="note.id" :note="note"/>
<XSub :note="appearNote.reply" class="reply-to" v-if="appearNote.reply"/>
<div class="info" v-if="pinned"><Fa :icon="faThumbtack"/> {{ $ts.pinnedNote }}</div>
<div class="info" v-if="appearNote._prId_"><Fa :icon="faBullhorn"/> {{ $ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ $ts.hideThisNote }} <Fa :icon="faTimes"/></button></div>
@@ -36,7 +35,7 @@
<span class="localOnly" v-if="note.localOnly"><Fa :icon="faBiohazard"/></span>
</div>
</div>
<article class="article" @contextmenu="onContextmenu">
<article class="article" @contextmenu.stop="onContextmenu">
<MkAvatar class="avatar" :user="appearNote.user"/>
<div class="main">
<XNoteHeader class="header" :note="appearNote" :mini="true"/>
@@ -46,7 +45,7 @@
<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
<XCwButton v-model:value="showContent" :note="appearNote"/>
</p>
<div class="content" v-show="appearNote.cw == null || showContent">
<div class="content" :class="{ collapsed }" v-show="appearNote.cw == null || showContent">
<div class="text">
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $ts.private }})</span>
<MkA class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><Fa :icon="faReply"/></MkA>
@@ -57,8 +56,11 @@
<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"/>
<MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="false" class="url-preview"/>
<div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div>
<button v-if="collapsed" class="fade _button" @click="collapsed = false">
<span>{{ $ts.showMore }}</span>
</button>
</div>
<MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><Fa :icon="faSatelliteDish"/> {{ appearNote.channel.name }}</MkA>
</div>
@@ -87,7 +89,6 @@
</footer>
</div>
</article>
<XSub v-for="note in replies" :key="note.id" :note="note" class="reply" :detail="true"/>
</div>
<div v-else class="_panel muted" @click="muted = false">
<I18n :src="$ts.userSaysSomething" tag="small">
@@ -154,11 +155,6 @@ export default defineComponent({
type: Object,
required: true
},
detail: {
type: Boolean,
required: false,
default: false
},
pinned: {
type: Boolean,
required: false,
@@ -171,9 +167,9 @@ export default defineComponent({
data() {
return {
connection: null,
conversation: [],
replies: [],
showContent: false,
collapsed: false,
isDeleted: false,
muted: false,
faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faBiohazard, faPlug, faSatelliteDish
@@ -273,6 +269,12 @@ export default defineComponent({
this.connection = os.stream;
}
this.collapsed = this.appearNote.cw == null && this.appearNote.text && (
(this.appearNote.text.split('\n').length > 9) ||
(this.appearNote.text.length > 500)
);
this.muted = await checkWordMute(this.appearNote, this.$i, this.$store.state.mutedWords);
// plugin
if (noteViewInterruptors.length > 0) {
let result = this.note;
@@ -281,25 +283,6 @@ export default defineComponent({
}
this.$emit('update:note', Object.freeze(result));
}
this.muted = await checkWordMute(this.appearNote, this.$i, this.$store.state.mutedWords);
if (this.detail) {
os.api('notes/children', {
noteId: this.appearNote.id,
limit: 30
}).then(replies => {
this.replies = replies;
});
if (this.appearNote.replyId) {
os.api('notes/conversation', {
noteId: this.appearNote.replyId
}).then(conversation => {
this.conversation = conversation.reverse();
});
}
}
},
mounted() {
@@ -943,10 +926,6 @@ export default defineComponent({
padding-bottom: 0;
}
> .reply-to-more {
opacity: 0.7;
}
> .renote {
display: flex;
align-items: center;
@@ -1038,6 +1017,37 @@ export default defineComponent({
}
> .content {
&.collapsed {
position: relative;
max-height: 9em;
overflow: hidden;
> .fade {
display: block;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 64px;
background: linear-gradient(0deg, var(--panel), var(--X15));
> span {
display: inline-block;
background: var(--panel);
padding: 6px 10px;
font-size: 0.8em;
border-radius: 999px;
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
}
&:hover {
> span {
background: var(--panelHighlight);
}
}
}
}
> .text {
overflow-wrap: break-word;

View File

@@ -15,7 +15,7 @@
</div>
<XList ref="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed">
<XNote :note="note" @update:note="updated(note, $event)" :detail="detail" :key="note._featuredId_ || note._prId_ || note.id"/>
<XNote :note="note" @update:note="updated(note, $event)" :key="note._featuredId_ || note._prId_ || note.id"/>
</XList>
<div v-show="more && !reversed" style="margin-top: var(--margin);">
@@ -55,12 +55,6 @@ export default defineComponent({
required: true
},
detail: {
type: Boolean,
required: false,
default: false
},
prop: {
type: String,
required: false

View File

@@ -1,6 +1,6 @@
<template>
<XWindow ref="window"
:initial-width="700"
:initial-width="500"
:initial-height="500"
:can-resize="true"
:close-right="true"

View File

@@ -1,17 +1,20 @@
<template>
<div class="voxdxuby">
<XNote v-if="note" v-model:note="note" :key="note.id" :detail="value.detailed"/>
<XNote v-if="note && !value.detailed" v-model:note="note" :key="note.id + ':normal'"/>
<XNoteDetailed v-if="note && value.detailed" v-model:note="note" :key="note.id + ':detail'"/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XNote from '@/components/note.vue';
import XNoteDetailed from '@/components/note-detailed.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XNote
XNote,
XNoteDetailed,
},
props: {
value: {

View File

@@ -9,7 +9,7 @@
<header>
<button v-if="!fixed" class="cancel _button" @click="cancel"><Fa :icon="faTimes"/></button>
<div>
<span class="text-count" :class="{ over: trimmedLength(text) > max }">{{ max - trimmedLength(text) }}</span>
<span class="text-count" :class="{ over: textLength > max }">{{ max - textLength }}</span>
<span class="local-only" v-if="localOnly"><Fa :icon="faBiohazard"/></span>
<button class="_button visibility" @click="setVisibility" ref="visibilityButton" v-tooltip="$ts.visibility" :disabled="channel != null">
<span v-if="visibility === 'public'"><Fa :icon="faGlobe"/></span>
@@ -35,7 +35,7 @@
</div>
</div>
<input v-show="useCw" ref="cw" class="cw" v-model="cw" :placeholder="$ts.annotation" @keydown="onKeydown">
<textarea v-model="text" class="text" :class="{ withCw: useCw }" ref="text" :disabled="posting" :placeholder="placeholder" @keydown="onKeydown" @paste="onPaste"></textarea>
<textarea v-model="text" class="text" :class="{ withCw: useCw }" ref="text" :disabled="posting" :placeholder="placeholder" @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd" />
<XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/>
<XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/>
<footer>
@@ -142,6 +142,7 @@ export default defineComponent({
draghover: false,
quoteId: null,
recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
imeText: '',
postFormActions,
faReply, faQuoteRight, faPaperPlane, faTimes, faUpload, faPollH, faGlobe, faHome, faUnlock, faEnvelope, faEyeSlash, faLaughSquint, faPlus, faPhotoVideo, faAt, faBiohazard, faPlug
};
@@ -190,10 +191,14 @@ export default defineComponent({
: this.$ts.note;
},
textLength(): number {
return length((this.text + this.imeText).trim());
},
canPost(): boolean {
return !this.posting &&
(1 <= this.text.length || 1 <= this.files.length || this.poll || this.renote) &&
(length(this.text.trim()) <= this.max) &&
(1 <= this.textLength || 1 <= this.files.length || !!this.poll || !!this.renote) &&
(this.textLength <= this.max) &&
(!this.poll || this.poll.choices.length >= 2);
},
@@ -262,7 +267,7 @@ export default defineComponent({
}
// keep cw when reply
if (this.$store.keepCw && this.reply && this.reply.cw) {
if (this.$store.state.keepCw && this.reply && this.reply.cw) {
this.useCw = true;
this.cw = this.reply.cw;
}
@@ -339,10 +344,6 @@ export default defineComponent({
}
},
trimmedLength(text: string) {
return length(text.trim());
},
addTag(tag: string) {
insertTextAtCursor(this.$refs.text, ` #${tag} `);
},
@@ -429,11 +430,19 @@ export default defineComponent({
this.quoteId = null;
},
onKeydown(e) {
onKeydown(e: KeyboardEvent) {
if ((e.which === 10 || e.which === 13) && (e.ctrlKey || e.metaKey) && this.canPost) this.post();
if (e.which === 27) this.$emit('esc');
},
onCompositionUpdate(e: CompositionEvent) {
this.imeText = e.data;
},
onCompositionEnd(e: CompositionEvent) {
this.imeText = '';
},
async onPaste(e: ClipboardEvent) {
for (const { item, i } of Array.from(e.clipboardData.items).map((item, i) => ({item, i}))) {
if (item.kind == 'file') {

View File

@@ -15,7 +15,7 @@
<MkButton inline>This is</MkButton>
<MkButton inline primary>the button</MkButton>
</div>
<div class="_content">
<div class="_content" style="pointer-events: none;">
<Mfm :text="mfm"/>
</div>
<div class="_content">
@@ -49,7 +49,7 @@ export default defineComponent({
data() {
return {
text: '',
flag: false,
flag: true,
radio: 'misskey',
mfm: `Hello world! This is an @example mention. BTW you are @${this.$i.username}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.`
}

View File

@@ -59,7 +59,6 @@ export default defineComponent({
return {
host: host,
showing: false,
searching: false,
accounts: [],
connection: null,
menuDef: sidebarDef,
@@ -125,19 +124,7 @@ export default defineComponent({
},
search() {
if (this.searching) return;
os.dialog({
title: this.$ts.search,
input: true
}).then(async ({ canceled, result: query }) => {
if (canceled || query == null || query === '') return;
this.searching = true;
search(this, query).finally(() => {
this.searching = false;
});
});
search();
},
async openAccountMenu(ev) {
@@ -357,12 +344,6 @@ export default defineComponent({
top: 0;
left: 0;
z-index: 1001;
> div {
> .notifications {
display: none;
}
}
}
&:not(.hidden) {

View File

@@ -115,6 +115,22 @@ export default defineComponent({
endpoint = 'notes/global-timeline';
this.connection = os.stream.useSharedConnection('globalTimeline');
this.connection.on('note', prepend);
} else if (this.src == 'mentions') {
endpoint = 'notes/mentions';
this.connection = os.stream.useSharedConnection('main');
this.connection.on('mention', prepend);
} else if (this.src == 'directs') {
endpoint = 'notes/mentions';
this.query = {
visibility: 'specified'
};
const onNote = note => {
if (note.visibility == 'specified') {
prepend(note);
}
};
this.connection = os.stream.useSharedConnection('main');
this.connection.on('mention', onNote);
} else if (this.src == 'list') {
endpoint = 'notes/user-list-timeline';
this.query = {

View File

@@ -123,7 +123,7 @@ export default defineComponent({
box-shadow: none;
text-decoration: none;
background: var(--buttonBg);
border-radius: 8px;
border-radius: 999px;
overflow: hidden;
&:not(:disabled):hover {

View File

@@ -134,6 +134,8 @@ export default defineComponent({
> header {
position: relative;
color: var(--panelHeaderFg);
background: var(--panelHeaderBg);
box-shadow: 0 1px 0 0 var(--panelHeaderDivider);
z-index: 2;
line-height: 1.4em;
@@ -179,6 +181,7 @@ export default defineComponent({
> header {
> .title {
padding: 8px 10px;
font-size: 0.9em;
}
}

View File

@@ -34,7 +34,7 @@ export default defineComponent({
font-size: 90%;
background: var(--infoBg);
color: var(--infoFg);
border-radius: 5px;
border-radius: var(--radius);
&.warn {
background: var(--infoWarnBg);

View File

@@ -243,7 +243,7 @@ export default defineComponent({
display: block;
font-size: 14px;
box-shadow: 0 0 0 1px var(--divider);
border-radius: 6px;
border-radius: 8px;
overflow: hidden;
&:hover {

View File

@@ -31,17 +31,20 @@ if (localStorage.getItem('vuex') != null) {
localStorage.setItem('vuex-old', JSON.stringify(vuex));
localStorage.removeItem('vuex');
localStorage.removeItem('i');
localStorage.removeItem('locale');
location.reload();
}
import * as Sentry from '@sentry/browser';
import { Integrations } from '@sentry/tracing';
import { createApp, watch } from 'vue';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import widgets from '@/widgets';
import directives from '@/directives';
import components from '@/components';
import { version, ui, lang } from '@/config';
import { version, ui, lang, host } from '@/config';
import { router } from '@/router';
import { applyTheme } from '@/scripts/theme';
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
@@ -53,11 +56,10 @@ import { defaultStore, ColdDeviceStorage } from '@/store';
import { fetchInstance, instance } from '@/instance';
import { makeHotkey } from './scripts/hotkey';
import { search } from './scripts/search';
import { getThemes } from './theme-store';
console.info(`Misskey v${version}`);
window.clearTimeout((window as any).mkBootTimer);
if (_DEV_) {
console.warn('Development mode!!!');
@@ -87,13 +89,21 @@ if (_DEV_) {
});
}
if (defaultStore.state.reportError && !_DEV_) {
Sentry.init({
dsn: 'https://fd273254a07a4b61857607a9ea05d629@o501808.ingest.sentry.io/5583438',
tracesSampleRate: 1.0,
});
Sentry.setTag('misskey_version', version);
Sentry.setTag('ui', ui);
Sentry.setTag('lang', lang);
Sentry.setTag('host', host);
}
// タッチデバイスでCSSの:hoverを機能させる
document.addEventListener('touchend', () => {}, { passive: true });
if (localStorage.theme == null) {
applyTheme(require('@/themes/l-light.json5'));
}
//#region SEE: https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
// TODO: いつの日にか消したい
const vh = window.innerHeight * 0.01;
@@ -158,6 +168,8 @@ if ($i && $i.token) {
//#endregion
fetchInstance().then(() => {
localStorage.setItem('v', instance.version);
// Init service worker
//if (this.store.state.instance.meta.swPublickey) this.registerSw(this.store.state.instance.meta.swPublickey);
});
@@ -200,10 +212,10 @@ app.mount('body');
watch(defaultStore.reactiveState.darkMode, (darkMode) => {
import('@/scripts/theme').then(({ builtinThemes }) => {
const themes = builtinThemes.concat(ColdDeviceStorage.get('themes'));
const themes = builtinThemes.concat(getThemes());
applyTheme(themes.find(x => x.id === (darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme'))));
});
});
}, { immediate: localStorage.theme == null });
//#region Sync dark mode
if (ColdDeviceStorage.get('syncDeviceDarkMode')) {
@@ -336,14 +348,6 @@ if ($i) {
updateAccount({ hasUnreadAnnouncement: false });
});
main.on('clientSettingUpdated', x => {
updateAccount({
clientData: {
[x.key]: x.value
}
});
});
// トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる
main.on('myTokenRegenerated', () => {

View File

@@ -1,11 +1,13 @@
import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vue';
import { EventEmitter } from 'eventemitter3';
import * as Sentry from '@sentry/browser';
import Stream from '@/scripts/stream';
import { apiUrl, debug } from '@/config';
import MkPostFormDialog from '@/components/post-form-dialog.vue';
import MkWaitingDialog from '@/components/waiting-dialog.vue';
import { resolve } from '@/router';
import { $i } from './account';
import { $i } from '@/account';
import { defaultStore } from '@/store';
const ua = navigator.userAgent.toLowerCase();
export const isMobile = /mobile|iphone|ipad|android/.test(ua);
@@ -54,19 +56,32 @@ 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';
log!.res = markRaw(body);
log!.state = 'success';
}
} else if (res.status === 204) {
resolve();
if (debug) {
log.state = 'success';
log!.state = 'success';
}
} else {
reject(body.error);
if (debug) {
log.res = markRaw(body.error);
log.state = 'failed';
log!.res = markRaw(body.error);
log!.state = 'failed';
}
if (defaultStore.state.reportError && !_DEV_) {
Sentry.withScope((scope) => {
scope.setTag('api_endpoint', endpoint);
scope.setContext('api params', data);
scope.setContext('api error info', body.info);
scope.setTag('api_error_id', body.id);
scope.setTag('api_error_code', body.code);
scope.setTag('api_error_kind', body.kind);
scope.setLevel(Sentry.Severity.Error);
Sentry.captureMessage('API error');
});
}
}
}).catch(reject);
@@ -97,8 +112,8 @@ export function apiWithDialog(
export function promiseDialog<T extends Promise<any>>(
promise: T,
onSuccess?: (res: any) => void,
onFailure?: (e: Error) => void,
onSuccess?: ((res: any) => void) | null,
onFailure?: ((e: Error) => void) | null,
text?: string,
): T {
const showing = ref(true);

View File

@@ -4,7 +4,7 @@
<div id="debug"></div>
<section class="_formItem about">
<div class="_formPanel panel" :class="{ playing: easterEggEngine != null }" ref="about">
<img src="/assets/icons/512.png" alt="" class="icon" ref="icon" @load="iconLoaded" draggable="false"/>
<img src="/assets/about-icon.png" alt="" class="icon" ref="icon" @load="iconLoaded" draggable="false"/>
<div class="misskey">Misskey</div>
<div class="version">v{{ version }}</div>
<span class="emoji" v-for="emoji in easterEggEmojis" :key="emoji.id" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$instance.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span>

View File

@@ -23,6 +23,18 @@
</FormGroup>
<FormLink v-if="meta.tosUrl" :to="meta.tosUrl" external>{{ $ts.tos }}</FormLink>
<FormGroup v-if="stats">
<template #label>{{ $ts.statistics }}</template>
<FormKeyValueView>
<template #key>{{ $ts.users }}</template>
<template #value>{{ number(stats.originalUsersCount) }}</template>
</FormKeyValueView>
<FormKeyValueView>
<template #key>{{ $ts.notes }}</template>
<template #value>{{ number(stats.originalNotesCount) }}</template>
</FormKeyValueView>
</FormGroup>
</FormBase>
</template>
@@ -35,6 +47,7 @@ import FormBase from '@/components/form/base.vue';
import FormGroup from '@/components/form/group.vue';
import FormKeyValueView from '@/components/form/key-value-view.vue';
import * as os from '@/os';
import number from '@/filters/number';
export default defineComponent({
components: {
@@ -52,7 +65,7 @@ export default defineComponent({
},
version,
instanceName,
serverInfo: null,
stats: null,
faInfoCircle
}
},
@@ -62,6 +75,16 @@ export default defineComponent({
return this.$instance;
},
},
created() {
os.api('stats').then(stats => {
this.stats = stats;
});
},
methods: {
number
}
});
</script>

View File

@@ -0,0 +1,353 @@
<template>
<div class="t9makv94">
<section class="_section">
<div class="_content">
<details>
<summary>{{ $ts.import }}</summary>
<MkTextarea v-model:value="themeToImport">
{{ $ts._theme.importInfo }}
</MkTextarea>
<MkButton :disabled="!themeToImport.trim()" @click="importTheme">{{ $ts.import }}</MkButton>
</details>
</div>
</section>
<section class="_section">
<div class="_content _card _vMargin">
<div class="_content">
<MkInput v-model:value="name" required><span>{{ $ts.name }}</span></MkInput>
<MkInput v-model:value="author" required><span>{{ $ts.author }}</span></MkInput>
<MkTextarea v-model:value="description"><span>{{ $ts.description }}</span></MkTextarea>
<div class="_inputs">
<div v-text="$ts._theme.base" />
<MkRadio v-model="baseTheme" value="light">{{ $ts.light }}</MkRadio>
<MkRadio v-model="baseTheme" value="dark">{{ $ts.dark }}</MkRadio>
</div>
</div>
</div>
<div class="_content _card _vMargin">
<div class="list-view _content">
<div class="item" v-for="([ k, v ], i) in theme" :key="k">
<div class="_inputs">
<div>
{{ k.startsWith('$') ? `${k} (${$ts._theme.constant})` : $t('_theme.keys.' + k) }}
<button v-if="k.startsWith('$')" class="_button _link" @click="del(i)" v-text="$ts.delete" />
</div>
<div>
<div class="type" @click="chooseType($event, i)">
{{ getTypeOf(v) }} <Fa :icon="faChevronDown"/>
</div>
<!-- default -->
<div v-if="v === null" v-text="baseProps[k]" class="default-value" />
<!-- color -->
<div v-else-if="typeof v === 'string'" class="color">
<input type="color" :value="v" @input="colorChanged($event.target.value, i)"/>
<MkInput class="select" :value="v" @update:value="colorChanged($event, i)"/>
</div>
<!-- ref const -->
<MkInput v-else-if="v.type === 'refConst'" v-model:value="v.key">
<template #prefix>$</template>
<span>{{ $ts.name }}</span>
</MkInput>
<!-- ref props -->
<MkSelect class="select" v-else-if="v.type === 'refProp'" v-model:value="v.key">
<option v-for="key in themeProps" :value="key" :key="key">{{ $t('_theme.keys.' + key) }}</option>
</MkSelect>
<!-- func -->
<template v-else-if="v.type === 'func'">
<MkSelect class="select" v-model:value="v.name">
<template #label>{{ $ts._theme.funcKind }}</template>
<option v-for="n in ['alpha', 'darken', 'lighten']" :value="n" :key="n">{{ $t('_theme.' + n) }}</option>
</MkSelect>
<MkInput type="number" v-model:value="v.arg"><span>{{ $ts._theme.argument }}</span></MkInput>
<MkSelect class="select" v-model:value="v.value">
<template #label>{{ $ts._theme.basedProp }}</template>
<option v-for="key in themeProps" :value="key" :key="key">{{ $t('_theme.keys.' + key) }}</option>
</MkSelect>
</template>
<!-- CSS -->
<MkInput v-else-if="v.type === 'css'" v-model:value="v.value">
<span>CSS</span>
</MkInput>
</div>
</div>
</div>
<MkButton primary @click="addConst">{{ $ts._theme.addConstant }}</MkButton>
</div>
</div>
</section>
<section class="_section">
<details class="_content">
<summary>{{ $ts.sample }}</summary>
<MkSample/>
</details>
</section>
<section class="_section">
<div class="_content">
<MkButton inline @click="preview">{{ $ts.preview }}</MkButton>
<MkButton inline primary :disabled="!name || !author" @click="save">{{ $ts.save }}</MkButton>
</div>
</section>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faPalette, faChevronDown, faKeyboard } from '@fortawesome/free-solid-svg-icons';
import * as JSON5 from 'json5';
import { toUnicode } from 'punycode';
import MkRadio from '@/components/ui/radio.vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/ui/input.vue';
import MkTextarea from '@/components/ui/textarea.vue';
import MkSelect from '@/components/ui/select.vue';
import MkSample from '@/components/sample.vue';
import { convertToMisskeyTheme, ThemeValue, convertToViewModel, ThemeViewModel } from '@/scripts/theme-editor';
import { Theme, applyTheme, lightTheme, darkTheme, themeProps, validateTheme } from '@/scripts/theme';
import { host } from '@/config';
import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
import { addTheme } from '@/theme-store';
export default defineComponent({
components: {
MkRadio,
MkButton,
MkInput,
MkTextarea,
MkSelect,
MkSample,
},
data() {
return {
INFO: {
title: this.$ts.themeEditor,
icon: faPalette,
},
theme: [] as ThemeViewModel,
name: '',
description: '',
baseTheme: 'light' as 'dark' | 'light',
author: `@${this.$i.username}@${toUnicode(host)}`,
themeToImport: '',
changed: false,
lightTheme, darkTheme, themeProps,
faPalette, faChevronDown, faKeyboard,
}
},
computed: {
baseProps() {
return this.baseTheme === 'light' ? this.lightTheme.props : this.darkTheme.props;
},
},
beforeUnmount() {
window.removeEventListener('beforeunload', this.beforeunload);
},
async beforeRouteLeave(to, from, next) {
if (this.changed && !(await this.confirm())) {
next(false);
} else {
next();
}
},
mounted() {
this.init();
window.addEventListener('beforeunload', this.beforeunload);
const changed = () => this.changed = true;
this.$watch('name', changed);
this.$watch('description', changed);
this.$watch('baseTheme', changed);
this.$watch('author', changed);
this.$watch('theme', changed);
},
methods: {
beforeunload(e: BeforeUnloadEvent) {
if (this.changed) {
e.preventDefault();
e.returnValue = '';
}
},
async confirm(): Promise<boolean> {
const { canceled } = await os.dialog({
type: 'warning',
text: this.$ts.leaveConfirm,
showCancelButton: true
});
return !canceled;
},
init() {
const t: ThemeViewModel = [];
for (const key of themeProps) {
t.push([ key, null ]);
}
this.theme = t;
},
async del(i: number) {
const { canceled } = await os.dialog({
type: 'warning',
showCancelButton: true,
text: this.$t('_theme.deleteConstantConfirm', { const: this.theme[i][0] }),
});
if (canceled) return;
Vue.delete(this.theme, i);
},
async addConst() {
const { canceled, result } = await os.dialog({
title: this.$ts._theme.inputConstantName,
input: true
});
if (canceled) return;
this.theme.push([ '$' + result, '#000000']);
},
save() {
const theme = convertToMisskeyTheme(this.theme, this.name, this.description, this.author, this.baseTheme);
addTheme(theme);
os.dialog({
type: 'success',
text: this.$t('_theme.installed', { name: theme.name })
});
this.changed = false;
},
preview() {
const theme = convertToMisskeyTheme(this.theme, this.name, this.description, this.author, this.baseTheme);
try {
applyTheme(theme, false);
} catch (e) {
os.dialog({
type: 'error',
text: e.message
});
}
},
async importTheme() {
if (this.changed && (!await this.confirm())) return;
try {
const theme = JSON5.parse(this.themeToImport) as Theme;
if (!validateTheme(theme)) throw new Error(this.$ts._theme.invalid);
this.name = theme.name;
this.description = theme.desc || '';
this.author = theme.author;
this.baseTheme = theme.base || 'light';
this.theme = convertToViewModel(theme);
this.themeToImport = '';
} catch (e) {
os.dialog({
type: 'error',
text: e.message
});
}
},
colorChanged(color: string, i: number) {
this.theme[i] = [this.theme[i][0], color];
},
getTypeOf(v: ThemeValue) {
return v === null
? this.$ts._theme.defaultValue
: typeof v === 'string'
? this.$ts._theme.color
: this.$t('_theme.' + v.type);
},
async chooseType(e: MouseEvent, i: number) {
const newValue = await this.showTypeMenu(e);
this.theme[i] = [ this.theme[i][0], newValue ];
},
showTypeMenu(e: MouseEvent) {
return new Promise<ThemeValue>((resolve) => {
os.modalMenu([{
text: this.$ts._theme.defaultValue,
action: () => resolve(null),
}, {
text: this.$ts._theme.color,
action: () => resolve('#000000'),
}, {
text: this.$ts._theme.func,
action: () => resolve({
type: 'func', name: 'alpha', arg: 1, value: 'accent'
}),
}, {
text: this.$ts._theme.refProp,
action: () => resolve({
type: 'refProp', key: 'accent',
}),
}, {
text: this.$ts._theme.refConst,
action: () => resolve({
type: 'refConst', key: '',
}),
}, {
text: 'CSS',
action: () => resolve({
type: 'css', value: '',
}),
}], e.currentTarget || e.target);
});
}
}
});
</script>
<style lang="scss" scoped>
.t9makv94 {
> ._section {
> ._content {
> .list-view {
> .item {
min-height: 48px;
word-break: break-all;
&:not(:last-child) {
margin-bottom: 8px;
}
.select {
margin: 24px 0;
}
.type {
cursor: pointer;
}
.default-value {
opacity: 0.6;
pointer-events: none;
user-select: none;
}
.color {
> input {
display: inline-block;
width: 1.5em;
height: 1.5em;
}
> div {
margin-left: 8px;
display: inline-block;
}
}
}
}
}
}
}
</style>

View File

@@ -22,7 +22,7 @@
<XPostForm :channel="channel" class="post-form _content _panel _vMargin" fixed v-if="$i"/>
<XTimeline class="_content _vMargin" src="channel" :channel="channelId" @before="before" @after="after"/>
<XTimeline class="_content _vMargin _noGap_" src="channel" :key="channelId" :channel="channelId" @before="before" @after="after"/>
</div>
</template>

View File

@@ -3,7 +3,7 @@
<div class="title">{{ title }}</div>
<div class="body" v-html="body"></div>
<div class="footer">
<MkLink :url="`https://github.com/syuilo/misskey/blob/master/src/docs/${doc}.ja-JP.md`" class="at">{{ $ts.docSource }}</MkLink>
<MkLink :url="`https://github.com/syuilo/misskey/blob/master/src/docs/${lang}/${doc}.md`" class="at">{{ $ts.docSource }}</MkLink>
</div>
</div>
</template>
@@ -45,6 +45,7 @@ export default defineComponent({
title: null,
body: null,
markdown: null,
lang,
}
},

View File

@@ -145,6 +145,56 @@
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.font }}</div>
<div class="_content">
<p>{{ $ts._mfm.fontDescription }}</p>
<div class="preview _panel">
<Mfm :text="preview_font"/>
<MkTextarea v-model:value="preview_font"><span>MFM</span></MkTextarea>
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.x2 }}</div>
<div class="_content">
<p>{{ $ts._mfm.x2Description }}</p>
<div class="preview _panel">
<Mfm :text="preview_x2"/>
<MkTextarea v-model:value="preview_x2"><span>MFM</span></MkTextarea>
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.x3 }}</div>
<div class="_content">
<p>{{ $ts._mfm.x3Description }}</p>
<div class="preview _panel">
<Mfm :text="preview_x3"/>
<MkTextarea v-model:value="preview_x3"><span>MFM</span></MkTextarea>
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.x4 }}</div>
<div class="_content">
<p>{{ $ts._mfm.x4Description }}</p>
<div class="preview _panel">
<Mfm :text="preview_x4"/>
<MkTextarea v-model:value="preview_x4"><span>MFM</span></MkTextarea>
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.blur }}</div>
<div class="_content">
<p>{{ $ts._mfm.blurDescription }}</p>
<div class="preview _panel">
<Mfm :text="preview_blur"/>
<MkTextarea v-model:value="preview_blur"><span>MFM</span></MkTextarea>
</div>
</div>
</div>
<div class="_section">
<div class="_title">{{ $ts._mfm.jelly }}</div>
<div class="_content">
@@ -255,6 +305,11 @@ export default defineComponent({
preview_twitch: `[twitch 🍮]`,
preview_spin: `[spin 🍮] [spin.left 🍮] [spin.alternate 🍮]\n[spin.x 🍮] [spin.x,left 🍮] [spin.x,alternate 🍮]\n[spin.y 🍮] [spin.y,left 🍮] [spin.y,alternate 🍮]`,
preview_flip: `[flip ${this.$ts._mfm.dummy}]\n[flip.v ${this.$ts._mfm.dummy}]\n[flip.h,v ${this.$ts._mfm.dummy}]`,
preview_font: `[font.serif ${this.$ts._mfm.dummy}]\n[font.monospace ${this.$ts._mfm.dummy}]\n[font.cursive ${this.$ts._mfm.dummy}]\n[font.fantasy ${this.$ts._mfm.dummy}]`,
preview_x2: `[x2 🍮]`,
preview_x3: `[x3 🍮]`,
preview_x4: `[x4 🍮]`,
preview_blur: `[blur ${this.$ts._mfm.dummy}]`,
}
},
});

View File

@@ -45,6 +45,13 @@ export default defineComponent({
MkButton
},
props: {
groupId: {
type: String,
required: true,
},
},
data() {
return {
INFO: computed(() => this.group ? {
@@ -58,7 +65,7 @@ export default defineComponent({
},
watch: {
$route: 'fetch'
groupId: 'fetch',
},
created() {
@@ -69,7 +76,7 @@ export default defineComponent({
fetch() {
Progress.start();
os.api('users/groups/show', {
groupId: this.$route.params.group
groupId: this.groupId
}).then(group => {
this.group = group;
os.api('users/show', {

View File

@@ -2,14 +2,14 @@
<div class="fcuexfpr">
<div v-if="note" class="note">
<div class="_section" v-if="showNext">
<XNotes class="_content" :pagination="next"/>
<XNotes class="_content _noGap_" :pagination="next"/>
</div>
<div class="_section main">
<MkButton v-if="!showNext && hasNext" class="load next _content" @click="showNext = true"><Fa :icon="faChevronUp"/></MkButton>
<div class="_content _vMargin">
<MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_vMargin"/>
<XNote v-model:note="note" :key="note.id" :detail="true" class="_vMargin"/>
<XNoteDetailed v-model:note="note" :key="note.id" class="_vMargin"/>
</div>
<div class="_content clips _vMargin" v-if="clips && clips.length > 0">
<div class="title">{{ $ts.clip }}</div>
@@ -25,7 +25,7 @@
</div>
<div class="_section" v-if="showPrev">
<XNotes class="_content" :pagination="prev"/>
<XNotes class="_content _noGap_" :pagination="prev"/>
</div>
</div>
@@ -39,6 +39,7 @@
import { computed, defineComponent } from 'vue';
import { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';
import XNote from '@/components/note.vue';
import XNoteDetailed from '@/components/note-detailed.vue';
import XNotes from '@/components/notes.vue';
import MkRemoteCaution from '@/components/remote-caution.vue';
import MkButton from '@/components/ui/button.vue';
@@ -47,6 +48,7 @@ import * as os from '@/os';
export default defineComponent({
components: {
XNote,
XNoteDetailed,
XNotes,
MkRemoteCaution,
MkButton,

View File

@@ -9,7 +9,8 @@
</MkInput>
<MkSwitch v-model:value="value.detailed"><span>{{ $ts._pages.blocks._note.detailed }}</span></MkSwitch>
<XNote v-if="note" v-model:note="note" :key="note.id + ':' + (value.detailed ? 'detailed' : 'normal')" :detail="value.detailed" style="margin-bottom: 16px;"/>
<XNote v-if="note && !value.detailed" v-model:note="note" :key="note.id + ':normal'" style="margin-bottom: 16px;"/>
<XNoteDetailed v-if="note && value.detailed" v-model:note="note" :key="note.id + ':detail'" style="margin-bottom: 16px;"/>
</section>
</XContainer>
</template>
@@ -21,11 +22,12 @@ import XContainer from '../page-editor.container.vue';
import MkInput from '@/components/ui/input.vue';
import MkSwitch from '@/components/ui/switch.vue';
import XNote from '@/components/note.vue';
import XNoteDetailed from '@/components/note-detailed.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XContainer, MkInput, MkSwitch, XNote
XContainer, MkInput, MkSwitch, XNote, XNoteDetailed,
},
props: {

View File

@@ -7,7 +7,7 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { computed, defineComponent } from 'vue';
import { faSearch } from '@fortawesome/free-solid-svg-icons';
import Progress from '@/scripts/loading';
import XNotes from '@/components/notes.vue';
@@ -20,7 +20,7 @@ export default defineComponent({
data() {
return {
INFO: {
title: this.$t('searchWith', { q: this.$route.query.q }),
title: computed(() => this.$t('searchWith', { q: this.$route.query.q })),
icon: faSearch
},
pagination: {

View File

@@ -24,6 +24,8 @@
<span>{{ $ts._deck.columnMargin }}</span>
<template #suffix>px</template>
</FormInput>
<FormLink @click="setProfile">{{ $ts._deck.profile }}<template #suffix>{{ profile }}</template></FormLink>
</FormBase>
</template>
@@ -31,7 +33,7 @@
import { defineComponent } from 'vue';
import { faImage, faCog, faColumns } from '@fortawesome/free-solid-svg-icons';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormLink from '@/components/form/link.vue';
import FormRadios from '@/components/form/radios.vue';
import FormInput from '@/components/form/input.vue';
import FormBase from '@/components/form/base.vue';
@@ -42,7 +44,7 @@ import * as os from '@/os';
export default defineComponent({
components: {
FormSwitch,
FormSelect,
FormLink,
FormInput,
FormRadios,
FormBase,
@@ -67,6 +69,7 @@ export default defineComponent({
columnAlign: deckStore.makeGetterSetter('columnAlign'),
columnMargin: deckStore.makeGetterSetter('columnMargin'),
columnHeaderHeight: deckStore.makeGetterSetter('columnHeaderHeight'),
profile: deckStore.makeGetterSetter('profile'),
},
watch: {
@@ -85,5 +88,19 @@ export default defineComponent({
mounted() {
this.$emit('info', this.INFO);
},
methods: {
async setProfile() {
const { canceled, result: name } = await os.dialog({
title: this.$ts._deck.profile,
input: {
allowEmpty: false
}
});
if (canceled) return;
this.profile = name;
location.reload();
}
}
});
</script>

View File

@@ -1,33 +1,97 @@
<template>
<section class="uawsfosz _section">
<div class="_title"><Fa :icon="faCloud"/> {{ $ts.drive }}</div>
<div class="_content">
<span>{{ $ts.uploadFolder }}: {{ uploadFolder ? uploadFolder.name : '-' }}</span>
<MkButton primary @click="chooseUploadFolder()"><Fa :icon="faFolderOpen"/> {{ $ts.selectFolder }}</MkButton>
<FormBase class="">
<FormGroup v-if="!fetching">
<template #label>{{ $ts.usageAmount }}</template>
<div class="_formItem uawsfosz">
<div class="_formPanel">
<div class="meter"><div :style="meterStyle"></div></div>
</div>
</div>
<FormKeyValueView>
<template #key>{{ $ts.capacity }}</template>
<template #value>{{ bytes(capacity, 1) }}</template>
</FormKeyValueView>
<FormKeyValueView>
<template #key>{{ $ts.inUse }}</template>
<template #value>{{ bytes(usage, 1) }}</template>
</FormKeyValueView>
</FormGroup>
<div class="_formItem">
<div class="_formLabel">{{ $ts.statistics }}</div>
<div class="_formPanel">
<div ref="chart"></div>
</div>
</div>
</section>
<FormButton :center="false" @click="chooseUploadFolder()" primary>
{{ $ts.uploadFolder }}
<template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template>
<template #suffixIcon><Fa :icon="faFolderOpen"/></template>
</FormButton>
</FormBase>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import * as tinycolor from 'tinycolor2';
import ApexCharts from 'apexcharts';
import { faCloud, faFolderOpen } from '@fortawesome/free-solid-svg-icons';
import { faClock, faEyeSlash, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import MkButton from '@/components/ui/button.vue';
import FormButton from '@/components/form/button.vue';
import FormGroup from '@/components/form/group.vue';
import FormKeyValueView from '@/components/form/key-value-view.vue';
import FormBase from '@/components/form/base.vue';
import * as os from '@/os';
import bytes from '@/filters/bytes';
export default defineComponent({
components: {
MkButton,
FormBase,
FormButton,
FormGroup,
FormKeyValueView,
},
emits: ['info'],
data() {
return {
INFO: {
title: this.$ts.drive,
icon: faCloud
},
fetching: true,
usage: null,
capacity: null,
uploadFolder: null,
faCloud, faClock, faEyeSlash, faFolderOpen, faTrashAlt
}
},
computed: {
meterStyle(): any {
return {
width: `${this.usage / this.capacity * 100}%`,
background: tinycolor({
h: 180 - (this.usage / this.capacity * 180),
s: 0.7,
l: 0.5
})
};
}
},
async created() {
os.api('drive').then(info => {
this.capacity = info.capacity;
this.usage = info.usage;
this.fetching = false;
this.$nextTick(() => {
this.renderChart();
});
});
if (this.$store.state.uploadFolder) {
this.uploadFolder = await os.api('drive/folders/show', {
folderId: this.$store.state.uploadFolder
@@ -35,6 +99,10 @@ export default defineComponent({
}
},
mounted() {
this.$emit('info', this.INFO);
},
methods: {
chooseUploadFolder() {
os.selectDriveFolder(false).then(async folder => {
@@ -48,13 +116,127 @@ export default defineComponent({
this.uploadFolder = null;
}
});
}
},
renderChart() {
os.api('charts/user/drive', {
userId: this.$i.id,
span: 'day',
limit: 21
}).then(stats => {
const addition = [];
const deletion = [];
const now = new Date();
const y = now.getFullYear();
const m = now.getMonth();
const d = now.getDate();
for (let i = 0; i < 21; i++) {
const x = new Date(y, m, d - i);
addition.push([
x,
stats.incSize[i]
]);
deletion.push([
x,
-stats.decSize[i]
]);
}
const chart = new ApexCharts(this.$refs.chart, {
chart: {
type: 'bar',
stacked: true,
height: 150,
toolbar: {
show: false
},
zoom: {
enabled: false
}
},
plotOptions: {
bar: {
columnWidth: '80%'
}
},
grid: {
clipMarkers: false,
borderColor: 'rgba(0, 0, 0, 0.1)',
xaxis: {
lines: {
show: true,
}
},
},
tooltip: {
shared: true,
intersect: false,
theme: this.$store.state.darkMode ? 'dark' : 'light',
},
dataLabels: {
enabled: false
},
legend: {
show: false
},
series: [{
name: 'Additions',
data: addition
}, {
name: 'Deletions',
data: deletion
}],
xaxis: {
type: 'datetime',
labels: {
style: {
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--fg')).toRgbString()
}
},
axisBorder: {
color: 'rgba(0, 0, 0, 0.1)'
},
axisTicks: {
color: 'rgba(0, 0, 0, 0.1)'
},
crosshairs: {
width: 1,
opacity: 1
}
},
yaxis: {
labels: {
formatter: v => bytes(v, 0),
style: {
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--fg')).toRgbString()
}
}
}
});
chart.render();
});
},
bytes
}
});
</script>
<style lang="scss" scoped>
.uawsfosz {
> div {
padding: 24px;
> .meter {
$size: 12px;
background: rgba(0, 0, 0, 0.1);
border-radius: ($size / 2);
overflow: hidden;
> div {
height: $size;
border-radius: ($size / 2);
}
}
}
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<FormBase>
<FormButton @click="error()">error test</FormButton>
</FormBase>
</template>
@@ -42,5 +42,11 @@ export default defineComponent({
mounted() {
this.$emit('info', this.INFO);
},
methods: {
error() {
throw new Error('Test error');
}
}
});
</script>

View File

@@ -1,5 +1,6 @@
<template>
<FormBase>
<FormSwitch v-model:value="titlebar">{{ $ts.showTitlebar }}</FormSwitch>
<FormSwitch v-model:value="showFixedPostForm">{{ $ts.showFixedPostForm }}</FormSwitch>
<FormSelect v-model:value="lang">
@@ -133,6 +134,7 @@ export default defineComponent({
useOsNativeEmojis: defaultStore.makeGetterSetter('useOsNativeEmojis'),
disableShowingAnimatedImages: defaultStore.makeGetterSetter('disableShowingAnimatedImages'),
loadRawImages: defaultStore.makeGetterSetter('loadRawImages'),
titlebar: defaultStore.makeGetterSetter('titlebar'),
imageNewTab: defaultStore.makeGetterSetter('imageNewTab'),
nsfw: defaultStore.makeGetterSetter('nsfw'),
disablePagesScript: defaultStore.makeGetterSetter('disablePagesScript'),
@@ -175,6 +177,14 @@ export default defineComponent({
showGapBetweenNotesInTimeline() {
this.reloadAsk();
},
titlebar() {
this.reloadAsk();
},
instanceTicker() {
this.reloadAsk();
},
},
mounted() {

View File

@@ -1,49 +1,71 @@
<template>
<section class="_section">
<div class="_title"><Fa :icon="faBoxes"/> {{ $ts.importAndExport }}</div>
<div class="_content">
<MkSelect v-model:value="exportTarget">
<option value="notes">{{ $ts._exportOrImport.allNotes }}</option>
<option value="following">{{ $ts._exportOrImport.followingList }}</option>
<option value="user-lists">{{ $ts._exportOrImport.userLists }}</option>
<option value="mute">{{ $ts._exportOrImport.muteList }}</option>
<option value="blocking">{{ $ts._exportOrImport.blockingList }}</option>
</MkSelect>
<MkButton inline primary @click="doExport"><Fa :icon="faDownload"/> {{ $ts.export }}</MkButton>
<MkButton inline primary @click="doImport" :disabled="!['following', 'user-lists'].includes(exportTarget)"><Fa :icon="faUpload"/> {{ $ts.import }}</MkButton>
</div>
</section>
<FormBase>
<FormGroup>
<template #label>{{ $ts._exportOrImport.allNotes }}</template>
<FormButton @click="doExport('notes')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton>
</FormGroup>
<FormGroup>
<template #label>{{ $ts._exportOrImport.followingList }}</template>
<FormButton @click="doExport('following')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton>
<FormButton @click="doImport('following', $event)"><Fa :icon="faUpload"/> {{ $ts.import }}</FormButton>
</FormGroup>
<FormGroup>
<template #label>{{ $ts._exportOrImport.userLists }}</template>
<FormButton @click="doExport('user-lists')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton>
<FormButton @click="doImport('user-lists', $event)"><Fa :icon="faUpload"/> {{ $ts.import }}</FormButton>
</FormGroup>
<FormGroup>
<template #label>{{ $ts._exportOrImport.muteList }}</template>
<FormButton @click="doExport('mute')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton>
</FormGroup>
<FormGroup>
<template #label>{{ $ts._exportOrImport.blockingList }}</template>
<FormButton @click="doExport('blocking')"><Fa :icon="faDownload"/> {{ $ts.export }}</FormButton>
</FormGroup>
</FormBase>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faDownload, faUpload, faBoxes } from '@fortawesome/free-solid-svg-icons';
import MkButton from '@/components/ui/button.vue';
import MkSelect from '@/components/ui/select.vue';
import FormSelect from '@/components/form/select.vue';
import FormButton from '@/components/form/button.vue';
import FormBase from '@/components/form/base.vue';
import FormGroup from '@/components/form/group.vue';
import * as os from '@/os';
import { selectFile } from '@/scripts/select-file';
export default defineComponent({
components: {
MkButton,
MkSelect,
FormBase,
FormGroup,
FormButton,
},
emits: ['info'],
data() {
return {
exportTarget: 'notes',
INFO: {
title: this.$ts.importAndExport,
icon: faBoxes
},
faDownload, faUpload, faBoxes
}
},
mounted() {
this.$emit('info', this.INFO);
},
methods: {
doExport() {
doExport(target) {
os.api(
this.exportTarget == 'notes' ? 'i/export-notes' :
this.exportTarget == 'following' ? 'i/export-following' :
this.exportTarget == 'blocking' ? 'i/export-blocking' :
this.exportTarget == 'user-lists' ? 'i/export-user-lists' :
this.exportTarget == 'mute' ? 'i/export-mute' :
target == 'notes' ? 'i/export-notes' :
target == 'following' ? 'i/export-following' :
target == 'blocking' ? 'i/export-blocking' :
target == 'user-lists' ? 'i/export-user-lists' :
target == 'mute' ? 'i/export-mute' :
null, {})
.then(() => {
os.dialog({
@@ -58,12 +80,12 @@ export default defineComponent({
});
},
async doImport(e) {
async doImport(target, e) {
const file = await selectFile(e.currentTarget || e.target);
os.api(
this.exportTarget == 'following' ? 'i/import-following' :
this.exportTarget == 'user-lists' ? 'i/import-user-lists' :
target == 'following' ? 'i/import-following' :
target == 'user-lists' ? 'i/import-user-lists' :
null, {
fileId: file.id
}).then(() => {

View File

@@ -6,6 +6,7 @@
<FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><Fa :icon="faUser"/></template>{{ $ts.profile }}</FormLink>
<FormLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><Fa :icon="faLockOpen"/></template>{{ $ts.privacy }}</FormLink>
<FormLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><Fa :icon="faLaugh"/></template>{{ $ts.reaction }}</FormLink>
<FormLink :active="page === 'drive'" replace to="/settings/drive"><template #icon><Fa :icon="faCloud"/></template>{{ $ts.drive }}</FormLink>
<FormLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><Fa :icon="faBell"/></template>{{ $ts.notifications }}</FormLink>
<FormLink :active="page === 'email'" replace to="/settings/email"><template #icon><Fa :icon="faEnvelope"/></template>{{ $ts.email }}</FormLink>
<FormLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><Fa :icon="faShareAlt"/></template>{{ $ts.integration }}</FormLink>
@@ -27,19 +28,22 @@
<FormLink :active="page === 'api'" replace to="/settings/api"><template #icon><Fa :icon="faKey"/></template>API</FormLink>
<FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><Fa :icon="faEllipsisH"/></template>{{ $ts.other }}</FormLink>
</FormGroup>
<FormGroup>
<FormButton @click="clear">{{ $ts.clearCache }}</FormButton>
</FormGroup>
<FormGroup>
<FormButton @click="logout" danger>{{ $ts.logout }}</FormButton>
</FormGroup>
</FormBase>
<div class="main">
<component :is="component" @info="onInfo"/>
<component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, ref, watch } from 'vue';
import { faCog, faPalette, faPlug, faUser, faListUl, faLock, faCommentSlash, faMusic, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes } from '@fortawesome/free-solid-svg-icons';
import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue';
import { faCog, faPalette, faPlug, faUser, faListUl, faLock, faCommentSlash, faMusic, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes, faCloud } from '@fortawesome/free-solid-svg-icons';
import { faLaugh, faBell, faEnvelope } from '@fortawesome/free-regular-svg-icons';
import { i18n } from '@/i18n';
import FormLink from '@/components/form/link.vue';
@@ -75,11 +79,14 @@ export default defineComponent({
const onInfo = (viewInfo) => {
INFO.value = viewInfo;
};
const pageProps = ref({});
const component = computed(() => {
if (props.page == null) return null;
switch (props.page) {
case 'profile': return defineAsyncComponent(() => import('./profile.vue'));
case 'privacy': return defineAsyncComponent(() => import('./privacy.vue'));
case 'reaction': return defineAsyncComponent(() => import('./reaction.vue'));
case 'drive': return defineAsyncComponent(() => import('./drive.vue'));
case 'notifications': return defineAsyncComponent(() => import('./notifications.vue'));
case 'mute-block': return defineAsyncComponent(() => import('./mute-block.vue'));
case 'word-mute': return defineAsyncComponent(() => import('./word-mute.vue'));
@@ -101,16 +108,36 @@ export default defineComponent({
case 'plugins': return defineAsyncComponent(() => import('./plugins.vue'));
case 'import-export': return defineAsyncComponent(() => import('./import-export.vue'));
case 'account-info': return defineAsyncComponent(() => import('./account-info.vue'));
case 'update': return defineAsyncComponent(() => import('./update.vue'));
case 'registry': return defineAsyncComponent(() => import('./registry.vue'));
case 'experimental-features': return defineAsyncComponent(() => import('./experimental-features.vue'));
default: return null;
}
if (props.page.startsWith('registry/keys/system/')) {
return defineAsyncComponent(() => import('./registry.keys.vue'));
}
if (props.page.startsWith('registry/value/system/')) {
return defineAsyncComponent(() => import('./registry.value.vue'));
}
});
watch(component, () => {
pageProps.value = {};
if (props.page) {
if (props.page.startsWith('registry/keys/system/')) {
pageProps.value.scope = props.page.replace('registry/keys/system/', '').split('/');
}
if (props.page.startsWith('registry/value/system/')) {
const path = props.page.replace('registry/value/system/', '').split('/');
pageProps.value.xKey = path.pop();
pageProps.value.scope = path;
}
}
nextTick(() => {
scroll(el.value, 0);
});
});
}, { immediate: true });
onMounted(() => {
narrow.value = el.value.offsetWidth < 1025;
@@ -122,11 +149,17 @@ export default defineComponent({
view,
el,
onInfo,
pageProps,
component,
logout: () => {
signout();
},
faPalette, faPlug, faUser, faListUl, faLock, faLaugh, faCommentSlash, faMusic, faBell, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes, faEnvelope,
clear: () => {
localStorage.removeItem('locale');
localStorage.removeItem('theme');
location.reload();
},
faPalette, faPlug, faUser, faListUl, faLock, faLaugh, faCommentSlash, faMusic, faBell, faCogs, faEllipsisH, faBan, faShareAlt, faLockOpen, faKey, faBoxes, faEnvelope, faCloud,
};
},
});

View File

@@ -1,34 +1,34 @@
<template>
<section class="rrfwjxfl _section">
<FormBase>
<MkTab v-model:value="tab" style="margin-bottom: var(--margin);">
<option value="mute">{{ $ts.mutedUsers }}</option>
<option value="block">{{ $ts.blockedUsers }}</option>
</MkTab>
<div class="_content" v-if="tab === 'mute'">
<div v-if="tab === 'mute'">
<MkPagination :pagination="mutingPagination" class="muting">
<template #empty><MkInfo>{{ $ts.noUsers }}</MkInfo></template>
<template #default="{items}">
<div class="user" v-for="mute in items" :key="mute.id">
<MkA class="name" :to="userPage(mute.mutee)">
<FormGroup>
<FormLink v-for="mute in items" :key="mute.id" :to="userPage(mute.mutee)">
<MkAcct :user="mute.mutee"/>
</MkA>
</div>
</FormLink>
</FormGroup>
</template>
</MkPagination>
</div>
<div class="_content" v-if="tab === 'block'">
<div v-if="tab === 'block'">
<MkPagination :pagination="blockingPagination" class="blocking">
<template #empty><MkInfo>{{ $ts.noUsers }}</MkInfo></template>
<template #default="{items}">
<div class="user" v-for="block in items" :key="block.id">
<MkA class="name" :to="userPage(block.blockee)">
<FormGroup>
<FormLink v-for="block in items" :key="block.id" :to="userPage(block.blockee)">
<MkAcct :user="block.blockee"/>
</MkA>
</div>
</FormLink>
</FormGroup>
</template>
</MkPagination>
</div>
</section>
</FormBase>
</template>
<script lang="ts">
@@ -37,6 +37,9 @@ import { faBan } from '@fortawesome/free-solid-svg-icons';
import MkPagination from '@/components/ui/pagination.vue';
import MkTab from '@/components/tab.vue';
import MkInfo from '@/components/ui/info.vue';
import FormLink from '@/components/form/link.vue';
import FormBase from '@/components/form/base.vue';
import FormGroup from '@/components/form/group.vue';
import { userPage } from '@/filters/user';
import * as os from '@/os';
@@ -45,6 +48,9 @@ export default defineComponent({
MkPagination,
MkTab,
MkInfo,
FormBase,
FormGroup,
FormLink,
},
emits: ['info'],
@@ -76,19 +82,3 @@ export default defineComponent({
}
});
</script>
<style lang="scss" scoped>
.rrfwjxfl {
> ._content {
max-height: 350px;
overflow: auto;
> .muting,
> .blocking {
> .empty {
opacity: 0.5 !important;
}
}
}
}
</style>

View File

@@ -1,9 +1,13 @@
<template>
<FormBase>
<FormLink to="/settings/update">Misskey Update</FormLink>
<FormSwitch :value="$i.injectFeaturedNote" @update:value="onChangeInjectFeaturedNote">
{{ $ts.showFeaturedNotesInTimeline }}
</FormSwitch>
<FormSwitch v-model:value="reportError">{{ $ts.sendErrorReports }}<template #desc>{{ $ts.sendErrorReportsDescription }}</template></FormSwitch>
<FormLink to="/settings/account-info">{{ $ts.accountInfo }}</FormLink>
<FormLink to="/settings/experimental-features">{{ $ts.experimentalFeatures }}</FormLink>
@@ -13,16 +17,19 @@
DEBUG MODE
</FormSwitch>
<template v-if="debug">
<FormLink to="/settings/regedit">RegEdit</FormLink>
<FormButton @click="taskmanager">Task Manager</FormButton>
</template>
</FormGroup>
<FormLink to="/settings/registry"><template #icon><Fa :icon="faCogs"/></template>{{ $ts.registry }}</FormLink>
<FormButton @click="closeAccount" danger>{{ $ts.closeAccount }}</FormButton>
</FormBase>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
import { faEllipsisH } from '@fortawesome/free-solid-svg-icons';
import { faEllipsisH, faCogs } from '@fortawesome/free-solid-svg-icons';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormLink from '@/components/form/link.vue';
@@ -31,6 +38,8 @@ import FormGroup from '@/components/form/group.vue';
import FormButton from '@/components/form/button.vue';
import * as os from '@/os';
import { debug } from '@/config';
import { defaultStore } from '@/store';
import { signout } from '@/account';
export default defineComponent({
components: {
@@ -50,10 +59,15 @@ export default defineComponent({
title: this.$ts.other,
icon: faEllipsisH
},
debug
debug,
faCogs
}
},
computed: {
reportError: defaultStore.makeGetterSetter('reportError'),
},
mounted() {
this.$emit('info', this.INFO);
},
@@ -74,6 +88,22 @@ export default defineComponent({
taskmanager() {
os.popup(import('@/components/taskmanager.vue'), {
}, {}, 'closed');
},
closeAccount() {
os.dialog({
title: this.$ts.password,
input: {
type: 'password'
}
}).then(({ canceled, result: password }) => {
if (canceled) return;
os.api('i/delete-account', {
password: password
}).then(() => {
signout();
});
});
}
}
});

View File

@@ -0,0 +1,115 @@
<template>
<FormBase>
<FormGroup>
<FormKeyValueView>
<template #key>{{ $ts._registry.domain }}</template>
<template #value>{{ $ts.system }}</template>
</FormKeyValueView>
<FormKeyValueView>
<template #key>{{ $ts._registry.scope }}</template>
<template #value>{{ scope.join('/') }}</template>
</FormKeyValueView>
</FormGroup>
<FormGroup v-if="keys">
<template #label>{{ $ts._registry.keys }}</template>
<FormLink v-for="key in keys" :to="`/settings/registry/value/system/${scope.join('/')}/${key[0]}`" class="_monospace">{{ key[0] }}<template #suffix>{{ key[1].toUpperCase() }}</template></FormLink>
</FormGroup>
<FormButton @click="createKey" primary>{{ $ts._registry.createKey }}</FormButton>
</FormBase>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
import { faCogs } from '@fortawesome/free-solid-svg-icons';
import * as JSON5 from 'json5';
import MkInfo from '@/components/ui/info.vue';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormLink from '@/components/form/link.vue';
import FormBase from '@/components/form/base.vue';
import FormGroup from '@/components/form/group.vue';
import FormButton from '@/components/form/button.vue';
import FormKeyValueView from '@/components/form/key-value-view.vue';
import * as os from '@/os';
export default defineComponent({
components: {
MkInfo,
FormBase,
FormSelect,
FormSwitch,
FormButton,
FormLink,
FormGroup,
FormKeyValueView,
},
props: {
scope: {
required: true
}
},
emits: ['info'],
data() {
return {
INFO: {
title: this.$ts.registry,
icon: faCogs
},
keys: null,
}
},
watch: {
scope() {
this.fetch();
}
},
mounted() {
this.$emit('info', this.INFO);
this.fetch();
},
methods: {
fetch() {
os.api('i/registry/keys-with-type', {
scope: this.scope
}).then(keys => {
this.keys = Object.entries(keys).sort((a, b) => a[0].localeCompare(b[0]));
});
},
async createKey() {
const { canceled, result } = await os.form(this.$ts._registry.createKey, {
key: {
type: 'string',
label: this.$ts._registry.key,
},
value: {
type: 'string',
multiline: true,
label: this.$ts.value,
},
scope: {
type: 'string',
label: this.$ts._registry.scope,
default: this.scope.join('/')
}
});
if (canceled) return;
os.apiWithDialog('i/registry/set', {
scope: result.scope.split('/'),
key: result.key,
value: JSON5.parse(result.value),
}).then(() => {
this.fetch();
});
}
}
});
</script>

View File

@@ -0,0 +1,149 @@
<template>
<FormBase>
<MkInfo warn>{{ $ts.editTheseSettingsMayBreakAccount }}</MkInfo>
<template v-if="value">
<FormGroup>
<FormKeyValueView>
<template #key>{{ $ts._registry.domain }}</template>
<template #value>{{ $ts.system }}</template>
</FormKeyValueView>
<FormKeyValueView>
<template #key>{{ $ts._registry.scope }}</template>
<template #value>{{ scope.join('/') }}</template>
</FormKeyValueView>
<FormKeyValueView>
<template #key>{{ $ts._registry.key }}</template>
<template #value>{{ xKey }}</template>
</FormKeyValueView>
</FormGroup>
<FormGroup>
<FormTextarea tall v-model:value="valueForEditor" class="_monospace" style="tab-size: 2;">
<span>{{ $ts.value }} (JSON)</span>
</FormTextarea>
<FormButton @click="save" primary><Fa :icon="faSave"/> {{ $ts.save }}</FormButton>
</FormGroup>
<FormKeyValueView>
<template #key>{{ $ts.updatedAt }}</template>
<template #value><MkTime :time="value.updatedAt" mode="detail"/></template>
</FormKeyValueView>
<FormButton danger @click="del"><Fa :icon="faTrash"/> {{ $ts.delete }}</FormButton>
</template>
</FormBase>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
import { faCogs, faSave, faTrash } from '@fortawesome/free-solid-svg-icons';
import * as JSON5 from 'json5';
import MkInfo from '@/components/ui/info.vue';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormTextarea from '@/components/form/textarea.vue';
import FormBase from '@/components/form/base.vue';
import FormGroup from '@/components/form/group.vue';
import FormButton from '@/components/form/button.vue';
import FormKeyValueView from '@/components/form/key-value-view.vue';
import * as os from '@/os';
export default defineComponent({
components: {
MkInfo,
FormBase,
FormSelect,
FormSwitch,
FormButton,
FormTextarea,
FormGroup,
FormKeyValueView,
},
props: {
scope: {
required: true
},
xKey: {
required: true
},
},
emits: ['info'],
data() {
return {
INFO: {
title: this.$ts.registry,
icon: faCogs
},
value: null,
valueForEditor: null,
faSave, faTrash,
}
},
watch: {
key() {
this.fetch();
},
},
mounted() {
this.$emit('info', this.INFO);
this.fetch();
},
methods: {
fetch() {
os.api('i/registry/get-detail', {
scope: this.scope,
key: this.xKey
}).then(value => {
this.value = value;
this.valueForEditor = JSON5.stringify(this.value.value, null, '\t');
});
},
save() {
try {
JSON5.parse(this.valueForEditor);
} catch (e) {
os.dialog({
type: 'error',
text: this.$ts.invalidValue
});
return;
}
os.dialog({
type: 'warning',
text: this.$ts.saveConfirm,
showCancelButton: true
}).then(({ canceled }) => {
if (canceled) return;
os.apiWithDialog('i/registry/set', {
scope: this.scope,
key: this.xKey,
value: JSON5.parse(this.valueForEditor)
});
});
},
del() {
os.dialog({
type: 'warning',
text: this.$ts.deleteConfirm,
showCancelButton: true
}).then(({ canceled }) => {
if (canceled) return;
os.apiWithDialog('i/registry/remove', {
scope: this.scope,
key: this.xKey
});
});
}
}
});
</script>

View File

@@ -0,0 +1,91 @@
<template>
<FormBase>
<FormGroup v-if="scopes">
<template #label>{{ $ts.system }}</template>
<FormLink v-for="scope in scopes" :to="`/settings/registry/keys/system/${scope.join('/')}`" class="_monospace">{{ scope.join('/') }}</FormLink>
</FormGroup>
<FormButton @click="createKey" primary>{{ $ts._registry.createKey }}</FormButton>
</FormBase>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
import { faCogs } from '@fortawesome/free-solid-svg-icons';
import * as JSON5 from 'json5';
import MkInfo from '@/components/ui/info.vue';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormLink from '@/components/form/link.vue';
import FormBase from '@/components/form/base.vue';
import FormGroup from '@/components/form/group.vue';
import FormButton from '@/components/form/button.vue';
import FormKeyValueView from '@/components/form/key-value-view.vue';
import * as os from '@/os';
export default defineComponent({
components: {
MkInfo,
FormBase,
FormSelect,
FormSwitch,
FormButton,
FormLink,
FormGroup,
FormKeyValueView,
},
emits: ['info'],
data() {
return {
INFO: {
title: this.$ts.registry,
icon: faCogs
},
scopes: null,
}
},
created() {
this.fetch();
},
mounted() {
this.$emit('info', this.INFO);
},
methods: {
fetch() {
os.api('i/registry/scopes').then(scopes => {
this.scopes = scopes.slice().sort((a, b) => a.join('/').localeCompare(b.join('/')));
});
},
async createKey() {
const { canceled, result } = await os.form(this.$ts._registry.createKey, {
key: {
type: 'string',
label: this.$ts._registry.key,
},
value: {
type: 'string',
multiline: true,
label: this.$ts.value,
},
scope: {
type: 'string',
label: this.$ts._registry.scope,
}
});
if (canceled) return;
os.apiWithDialog('i/registry/set', {
scope: result.scope.split('/'),
key: result.key,
value: JSON5.parse(result.value),
}).then(() => {
this.fetch();
});
}
}
});
</script>

View File

@@ -90,12 +90,25 @@ export default defineComponent({
save() {
this.$store.set('menu', this.splited);
this.reloadAsk();
},
reset() {
this.$store.reset('menu');
this.items = this.$store.state.menu.join('\n');
this.reloadAsk();
},
async reloadAsk() {
const { canceled } = await os.dialog({
type: 'info',
text: this.$ts.reloadToApplySetting,
showCancelButton: true
});
if (canceled) return;
location.reload();
}
},
});
</script>

View File

@@ -25,6 +25,7 @@ import FormButton from '@/components/form/button.vue';
import { applyTheme, validateTheme } from '@/scripts/theme';
import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
import { addTheme, getThemes } from '@/theme-store';
export default defineComponent({
components: {
@@ -74,7 +75,7 @@ export default defineComponent({
});
return false;
}
if (ColdDeviceStorage.get('themes').some(t => t.id === theme.id)) {
if (getThemes().some(t => t.id === theme.id)) {
os.dialog({
type: 'info',
text: this.$ts._theme.alreadyInstalled
@@ -90,11 +91,10 @@ export default defineComponent({
if (theme) applyTheme(theme, false);
},
install(code) {
async install(code) {
const theme = this.parseThemeCode(code);
if (!theme) return;
const themes = ColdDeviceStorage.get('themes').concat(theme);
ColdDeviceStorage.set('themes', themes);
await addTheme(theme);
os.dialog({
type: 'success',
text: this.$t('_theme.installed', { name: theme.name })

View File

@@ -1,9 +1,11 @@
<template>
<FormBase>
<FormSelect v-model:value="selectedThemeId">
<template #label>{{ $ts.installedThemes }}</template>
<option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
<optgroup :label="$ts.builtinThemes">
<template #label>{{ $ts.theme }}</template>
<optgroup :label="$ts._theme.installedThemes">
<option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
<optgroup :label="$ts._theme.builtinThemes">
<option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
</FormSelect>
@@ -35,6 +37,7 @@ import { Theme, builtinThemes } from '@/scripts/theme';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
import { getThemes, removeTheme } from '@/theme-store';
export default defineComponent({
components: {
@@ -55,7 +58,7 @@ export default defineComponent({
title: this.$ts._theme.manage,
icon: faFolderOpen
},
installedThemes: ColdDeviceStorage.ref('themes'),
installedThemes: getThemes(),
builtinThemes,
selectedThemeId: null,
faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye
@@ -89,10 +92,7 @@ export default defineComponent({
},
uninstall() {
const theme = this.selectedTheme;
const themes = ColdDeviceStorage.get('themes').filter(t => t.id != theme.id);
ColdDeviceStorage.set('themes', themes);
os.success();
removeTheme(this.selectedTheme);
},
}
});

View File

@@ -49,11 +49,14 @@
<FormButton primary v-else @click="wallpaper = null">{{ $ts.removeWallpaper }}</FormButton>
<FormGroup>
<FormLink to="https://assets.msky.cafe/theme/list" external>{{ $ts._theme.explore }}</FormLink>
<FormLink to="/theme-editor">{{ $ts._theme.make }}</FormLink>
<FormLink to="https://assets.msky.cafe/theme/list" external><template #icon><Fa :icon="faGlobe"/></template>{{ $ts._theme.explore }}</FormLink>
<FormLink to="/settings/theme/install"><template #icon><Fa :icon="faDownload"/></template>{{ $ts._theme.install }}</FormLink>
</FormGroup>
<FormLink to="/settings/theme/install"><template #icon><Fa :icon="faDownload"/></template>{{ $ts._theme.install }}</FormLink>
<FormGroup>
<FormLink to="/theme-editor"><template #icon><Fa :icon="faPaintRoller"/></template>{{ $ts._theme.make }}</FormLink>
<FormLink to="/advanced-theme-editor"><template #icon><Fa :icon="faPaintRoller"/></template>{{ $ts._theme.make }} ({{ $ts.advanced }})</FormLink>
</FormGroup>
<FormLink to="/settings/theme/manage"><template #icon><Fa :icon="faFolderOpen"/></template>{{ $ts._theme.manage }}</FormLink>
</FormBase>
@@ -61,7 +64,7 @@
<script lang="ts">
import { computed, defineComponent, onMounted, ref, watch } from 'vue';
import { faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye } from '@fortawesome/free-solid-svg-icons';
import { faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye, faGlobe, faPaintRoller } from '@fortawesome/free-solid-svg-icons';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormBase from '@/components/form/base.vue';
@@ -74,6 +77,7 @@ import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
import { ColdDeviceStorage } from '@/store';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
import { fetchThemes, getThemes } from '@/theme-store';
export default defineComponent({
components: {
@@ -93,7 +97,7 @@ export default defineComponent({
icon: faPalette
};
const installedThemes = ColdDeviceStorage.ref('themes');
const installedThemes = ref(getThemes());
const themes = computed(() => builtinThemes.concat(installedThemes.value));
const darkThemes = computed(() => themes.value.filter(t => t.base == 'dark' || t.kind == 'dark'));
const lightThemes = computed(() => themes.value.filter(t => t.base == 'light' || t.kind == 'light'));
@@ -134,6 +138,10 @@ export default defineComponent({
emit('info', INFO);
});
fetchThemes().then(() => {
installedThemes.value = getThemes();
});
return {
INFO,
darkThemes,
@@ -148,7 +156,7 @@ export default defineComponent({
wallpaper.value = file.url;
});
},
faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye
faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt, faEye, faGlobe, faPaintRoller,
};
}
});

View File

@@ -0,0 +1,94 @@
<template>
<FormBase>
<template v-if="meta">
<MkInfo v-if="version === meta.version">{{ $ts.youAreRunningUpToDateClient }}</MkInfo>
<MkInfo v-else warn>{{ $ts.newVersionOfClientAvailable }}</MkInfo>
</template>
<FormGroup>
<template #label>{{ instanceName }}</template>
<FormKeyValueView>
<template #key>{{ $ts.currentVersion }}</template>
<template #value>{{ version }}</template>
</FormKeyValueView>
<FormKeyValueView>
<template #key>{{ $ts.latestVersion }}</template>
<template #value v-if="meta">{{ meta.version }}</template>
<template #value v-else><MkEllipsis/></template>
</FormKeyValueView>
</FormGroup>
<FormGroup>
<template #label>Misskey</template>
<FormKeyValueView>
<template #key>{{ $ts.latestVersion }}</template>
<template #value v-if="releases">{{ releases[0].tag_name }}</template>
<template #value v-else><MkEllipsis/></template>
</FormKeyValueView>
<template #caption v-if="releases"><MkTime :time="releases[0].published_at" mode="detail"/></template>
</FormGroup>
</FormBase>
</template>
<script lang="ts">
import { defineAsyncComponent, defineComponent } from 'vue';
import { faInfoCircle, faSyncAlt } from '@fortawesome/free-solid-svg-icons';
import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue';
import FormLink from '@/components/form/link.vue';
import FormBase from '@/components/form/base.vue';
import FormGroup from '@/components/form/group.vue';
import FormButton from '@/components/form/button.vue';
import FormKeyValueView from '@/components/form/key-value-view.vue';
import MkInfo from '@/components/ui/info.vue';
import * as os from '@/os';
import { version, instanceName } from '@/config';
export default defineComponent({
components: {
FormBase,
FormSelect,
FormSwitch,
FormButton,
FormLink,
FormGroup,
FormKeyValueView,
MkInfo,
},
emits: ['info'],
data() {
return {
INFO: {
title: 'Misskey Update',
icon: faSyncAlt
},
version,
instanceName,
releases: null,
meta: null
}
},
mounted() {
this.$emit('info', this.INFO);
os.api('meta', {
detail: false
}).then(meta => {
this.meta = meta;
localStorage.setItem('v', meta.version);
});
fetch('https://api.github.com/repos/syuilo/misskey/releases', {
method: 'GET',
})
.then(res => res.json())
.then(res => {
this.releases = res;
});
},
methods: {
}
});
</script>

View File

@@ -1,121 +1,71 @@
<template>
<div class="t9makv94">
<section class="_section">
<div class="_content">
<details>
<summary>{{ $ts.import }}</summary>
<MkTextarea v-model:value="themeToImport">
{{ $ts._theme.importInfo }}
</MkTextarea>
<MkButton :disabled="!themeToImport.trim()" @click="importTheme">{{ $ts.import }}</MkButton>
</details>
</div>
</section>
<section class="_section">
<div class="_content _card _vMargin">
<div class="_content">
<MkInput v-model:value="name" required><span>{{ $ts.name }}</span></MkInput>
<MkInput v-model:value="author" required><span>{{ $ts.author }}</span></MkInput>
<MkTextarea v-model:value="description"><span>{{ $ts.description }}</span></MkTextarea>
<div class="_inputs">
<div v-text="$ts._theme.base" />
<MkRadio v-model="baseTheme" value="light">{{ $ts.light }}</MkRadio>
<MkRadio v-model="baseTheme" value="dark">{{ $ts.dark }}</MkRadio>
</div>
<FormBase class="cwepdizn">
<div class="_formItem colorPicker">
<div class="_formLabel">{{ $ts.backgroundColor }}</div>
<div class="_formPanel colors">
<div class="row">
<button v-for="color in bgColors.filter(x => x.kind === 'light')" :key="color.color" @click="bgColor = color" class="color _button" :class="{ active: bgColor?.color === color.color }">
<div class="preview" :style="{ background: color.forPreview }"></div>
</button>
</div>
<div class="row">
<button v-for="color in bgColors.filter(x => x.kind === 'dark')" :key="color.color" @click="bgColor = color" class="color _button" :class="{ active: bgColor?.color === color.color }">
<div class="preview" :style="{ background: color.forPreview }"></div>
</button>
</div>
</div>
<div class="_content _card _vMargin">
<div class="list-view _content">
<div class="item" v-for="([ k, v ], i) in theme" :key="k">
<div class="_inputs">
<div>
{{ k.startsWith('$') ? `${k} (${$ts._theme.constant})` : $t('_theme.keys.' + k) }}
<button v-if="k.startsWith('$')" class="_button _link" @click="del(i)" v-text="$ts.delete" />
</div>
<div>
<div class="type" @click="chooseType($event, i)">
{{ getTypeOf(v) }} <Fa :icon="faChevronDown"/>
</div>
<!-- default -->
<div v-if="v === null" v-text="baseProps[k]" class="default-value" />
<!-- color -->
<div v-else-if="typeof v === 'string'" class="color">
<input type="color" :value="v" @input="colorChanged($event.target.value, i)"/>
<MkInput class="select" :value="v" @update:value="colorChanged($event, i)"/>
</div>
<!-- ref const -->
<MkInput v-else-if="v.type === 'refConst'" v-model:value="v.key">
<template #prefix>$</template>
<span>{{ $ts.name }}</span>
</MkInput>
<!-- ref props -->
<MkSelect class="select" v-else-if="v.type === 'refProp'" v-model:value="v.key">
<option v-for="key in themeProps" :value="key" :key="key">{{ $t('_theme.keys.' + key) }}</option>
</MkSelect>
<!-- func -->
<template v-else-if="v.type === 'func'">
<MkSelect class="select" v-model:value="v.name">
<template #label>{{ $ts._theme.funcKind }}</template>
<option v-for="n in ['alpha', 'darken', 'lighten']" :value="n" :key="n">{{ $t('_theme.' + n) }}</option>
</MkSelect>
<MkInput type="number" v-model:value="v.arg"><span>{{ $ts._theme.argument }}</span></MkInput>
<MkSelect class="select" v-model:value="v.value">
<template #label>{{ $ts._theme.basedProp }}</template>
<option v-for="key in themeProps" :value="key" :key="key">{{ $t('_theme.keys.' + key) }}</option>
</MkSelect>
</template>
<!-- CSS -->
<MkInput v-else-if="v.type === 'css'" v-model:value="v.value">
<span>CSS</span>
</MkInput>
</div>
</div>
</div>
<MkButton primary @click="addConst">{{ $ts._theme.addConstant }}</MkButton>
</div>
<div class="_formItem colorPicker">
<div class="_formLabel">{{ $ts.accentColor }}</div>
<div class="_formPanel colors">
<div class="row">
<button v-for="color in accentColors" :key="color" @click="accentColor = color" class="color rounded _button" :class="{ active: accentColor === color }">
<div class="preview" :style="{ background: color }"></div>
</button>
</div>
</div>
</section>
<section class="_section">
<details class="_content">
<summary>{{ $ts.sample }}</summary>
<MkSample/>
</details>
</section>
<section class="_section">
<div class="_content">
<MkButton inline @click="preview">{{ $ts.preview }}</MkButton>
<MkButton inline primary :disabled="!name || !author" @click="save">{{ $ts.save }}</MkButton>
</div>
<div class="_formItem colorPicker">
<div class="_formLabel">{{ $ts.textColor }}</div>
<div class="_formPanel colors">
<div class="row">
<button v-for="color in fgColors" :key="color" @click="fgColor = color" class="color char _button" :class="{ active: fgColor === color }">
<div class="preview" :style="{ color: color.forPreview ? color.forPreview : bgColor?.kind === 'light' ? '#5f5f5f' : '#dadada' }">A</div>
</button>
</div>
</div>
</section>
</div>
</div>
<div class="_formItem preview">
<div class="_formLabel">{{ $ts.preview }}</div>
<div class="_formPanel preview">
<MkSample class="preview"/>
</div>
</div>
<FormButton @click="saveAs" primary>{{ $ts.saveAs }}</FormButton>
</FormBase>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faPalette, faChevronDown, faKeyboard } from '@fortawesome/free-solid-svg-icons';
import * as JSON5 from 'json5';
import { toUnicode } from 'punycode';
import * as tinycolor from 'tinycolor2';
import { v4 as uuid} from 'uuid';
import MkRadio from '@/components/ui/radio.vue';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/ui/input.vue';
import MkTextarea from '@/components/ui/textarea.vue';
import MkSelect from '@/components/ui/select.vue';
import FormBase from '@/components/form/base.vue';
import FormButton from '@/components/form/button.vue';
import MkSample from '@/components/sample.vue';
import { convertToMisskeyTheme, ThemeValue, convertToViewModel, ThemeViewModel } from '@/scripts/theme-editor';
import { Theme, applyTheme, lightTheme, darkTheme, themeProps, validateTheme } from '@/scripts/theme';
import { Theme, applyTheme, validateTheme } from '@/scripts/theme';
import { host } from '@/config';
import * as os from '@/os';
import { ColdDeviceStorage } from '@/store';
import { addTheme } from '@/theme-store';
export default defineComponent({
components: {
MkRadio,
MkButton,
MkInput,
MkTextarea,
MkSelect,
FormBase,
FormButton,
MkSample,
},
@@ -125,47 +75,70 @@ export default defineComponent({
title: this.$ts.themeEditor,
icon: faPalette,
},
theme: [] as ThemeViewModel,
name: '',
description: '',
baseTheme: 'light' as 'dark' | 'light',
author: `@${this.$i.username}@${toUnicode(host)}`,
themeToImport: '',
bgColors: [
{ color: '#f5f5f5', kind: 'light', forPreview: '#f5f5f5' },
{ color: '#f0eee9', kind: 'light', forPreview: '#f3e2b9' },
{ color: '#e9eff0', kind: 'light', forPreview: '#bfe3e8' },
{ color: '#f0e9ee', kind: 'light', forPreview: '#f1d1e8' },
{ color: '#dce2e0', kind: 'light', forPreview: '#a4dccc' },
{ color: '#e2e0dc', kind: 'light', forPreview: '#d8c7a5' },
{ color: '#d5dbe0', kind: 'light', forPreview: '#b0cae0' },
{ color: '#dad5d5', kind: 'light', forPreview: '#d6afaf' },
{ color: '#2b2b2b', kind: 'dark', forPreview: '#444444' },
{ color: '#362e29', kind: 'dark', forPreview: '#735c4d' },
{ color: '#303629', kind: 'dark', forPreview: '#506d2f' },
{ color: '#293436', kind: 'dark', forPreview: '#258192' },
{ color: '#2e2936', kind: 'dark', forPreview: '#504069' },
{ color: '#252722', kind: 'dark', forPreview: '#3c462f' },
{ color: '#212525', kind: 'dark', forPreview: '#303e3e' },
{ color: '#191919', kind: 'dark', forPreview: '#272727' },
],
bgColor: null,
accentColors: ['#e36749', '#f29924', '#98c934', '#34c9a9', '#34a1c9', '#606df7', '#8d34c9', '#e84d83'],
accentColor: null,
fgColors: [
{ color: 'none', forLight: '#5f5f5f', forDark: '#dadada', forPreview: null },
{ color: 'red', forLight: '#7f6666', forDark: '#e4d1d1', forPreview: '#ca4343' },
{ color: 'yellow', forLight: '#736955', forDark: '#e0d5c0', forPreview: '#d49923' },
{ color: 'green', forLight: '#586d5b', forDark: '#d1e4d4', forPreview: '#4cbd5c' },
{ color: 'cyan', forLight: '#5d7475', forDark: '#d1e3e4', forPreview: '#2abdc3' },
{ color: 'blue', forLight: '#676880', forDark: '#d1d2e4', forPreview: '#7275d8' },
{ color: 'pink', forLight: '#84667d', forDark: '#e4d1e0', forPreview: '#b12390' },
],
fgColor: null,
changed: false,
lightTheme, darkTheme, themeProps,
faPalette, faChevronDown, faKeyboard,
faPalette,
}
},
computed: {
baseProps() {
return this.baseTheme === 'light' ? this.lightTheme.props : this.darkTheme.props;
},
created() {
const currentBgColor = getComputedStyle(document.documentElement).getPropertyValue('--bg');
const matchedBgColor = this.bgColors.find(x => tinycolor(x.color).toRgbString() === tinycolor(currentBgColor).toRgbString());
if (matchedBgColor) this.bgColor = matchedBgColor;
const currentAccentColor = getComputedStyle(document.documentElement).getPropertyValue('--accent');
const matchedAccentColor = this.accentColors.find(x => tinycolor(x).toRgbString() === tinycolor(currentAccentColor).toRgbString());
if (matchedAccentColor) this.accentColor = matchedAccentColor;
const currentFgColor = getComputedStyle(document.documentElement).getPropertyValue('--fg');
const matchedFgColor = this.fgColors.find(x => [tinycolor(x.forLight).toRgbString(), tinycolor(x.forDark).toRgbString()].includes(tinycolor(currentFgColor).toRgbString()));
if (matchedFgColor) this.fgColor = matchedFgColor;
this.$watch('bgColor', this.apply);
this.$watch('accentColor', this.apply);
this.$watch('fgColor', this.apply);
window.addEventListener('beforeunload', this.beforeunload);
},
beforeUnmount() {
window.removeEventListener('beforeunload', this.beforeunload);
},
async beforeRouteLeave(to, from, next) {
if (this.changed && !(await this.confirm())) {
next(false);
} else {
next();
async beforeRouteLeave(to, from) {
if (this.changed && !(await this.leaveConfirm())) {
return false;
}
},
mounted() {
this.init();
window.addEventListener('beforeunload', this.beforeunload);
const changed = () => this.changed = true;
this.$watch('name', changed);
this.$watch('description', changed);
this.$watch('baseTheme', changed);
this.$watch('author', changed);
this.$watch('theme', changed);
},
methods: {
beforeunload(e: BeforeUnloadEvent) {
if (this.changed) {
@@ -174,7 +147,7 @@ export default defineComponent({
}
},
async confirm(): Promise<boolean> {
async leaveConfirm(): Promise<boolean> {
const { canceled } = await os.dialog({
type: 'warning',
text: this.$ts.leaveConfirm,
@@ -183,171 +156,119 @@ export default defineComponent({
return !canceled;
},
init() {
const t: ThemeViewModel = [];
for (const key of themeProps) {
t.push([ key, null ]);
convert(): Theme {
return {
name: this.$ts.myTheme,
base: this.bgColor.kind,
props: {
bg: this.bgColor.color,
fg: this.bgColor.kind === 'light' ? this.fgColor.forLight : this.fgColor.forDark,
accent: this.accentColor,
}
};
},
apply() {
if (this.bgColor == null) this.bgColor = this.bgColors[0];
if (this.accentColor == null) this.accentColor = this.accentColors[0];
if (this.fgColor == null) this.fgColor = this.fgColors[0];
const theme = this.convert();
applyTheme(theme, false);
this.changed = true;
},
async saveAs() {
const { canceled, result: name } = await os.dialog({
title: this.$ts.name,
input: {
allowEmpty: false
}
});
if (canceled) return;
const theme = this.convert();
theme.id = uuid();
theme.name = name;
theme.author = `@${this.$i.username}@${toUnicode(host)}`;
addTheme(theme);
applyTheme(theme);
if (this.$store.state.darkMode) {
ColdDeviceStorage.set('darkTheme', theme.id);
} else {
ColdDeviceStorage.set('lightTheme', theme.id);
}
this.theme = t;
},
async del(i: number) {
const { canceled } = await os.dialog({
type: 'warning',
showCancelButton: true,
text: this.$t('_theme.deleteConstantConfirm', { const: this.theme[i][0] }),
});
if (canceled) return;
Vue.delete(this.theme, i);
},
async addConst() {
const { canceled, result } = await os.dialog({
title: this.$ts._theme.inputConstantName,
input: true
});
if (canceled) return;
this.theme.push([ '$' + result, '#000000']);
},
save() {
const theme = convertToMisskeyTheme(this.theme, this.name, this.description, this.author, this.baseTheme);
const themes = ColdDeviceStorage.get('themes').concat(theme);
ColdDeviceStorage.set('themes', themes);
this.changed = false;
os.dialog({
type: 'success',
text: this.$t('_theme.installed', { name: theme.name })
});
this.changed = false;
},
preview() {
const theme = convertToMisskeyTheme(this.theme, this.name, this.description, this.author, this.baseTheme);
try {
applyTheme(theme, false);
} catch (e) {
os.dialog({
type: 'error',
text: e.message
});
}
},
async importTheme() {
if (this.changed && (!await this.confirm())) return;
try {
const theme = JSON5.parse(this.themeToImport) as Theme;
if (!validateTheme(theme)) throw new Error(this.$ts._theme.invalid);
this.name = theme.name;
this.description = theme.desc || '';
this.author = theme.author;
this.baseTheme = theme.base || 'light';
this.theme = convertToViewModel(theme);
this.themeToImport = '';
} catch (e) {
os.dialog({
type: 'error',
text: e.message
});
}
},
colorChanged(color: string, i: number) {
this.theme[i] = [this.theme[i][0], color];
},
getTypeOf(v: ThemeValue) {
return v === null
? this.$ts._theme.defaultValue
: typeof v === 'string'
? this.$ts._theme.color
: this.$t('_theme.' + v.type);
},
async chooseType(e: MouseEvent, i: number) {
const newValue = await this.showTypeMenu(e);
this.theme[i] = [ this.theme[i][0], newValue ];
},
showTypeMenu(e: MouseEvent) {
return new Promise<ThemeValue>((resolve) => {
os.modalMenu([{
text: this.$ts._theme.defaultValue,
action: () => resolve(null),
}, {
text: this.$ts._theme.color,
action: () => resolve('#000000'),
}, {
text: this.$ts._theme.func,
action: () => resolve({
type: 'func', name: 'alpha', arg: 1, value: 'accent'
}),
}, {
text: this.$ts._theme.refProp,
action: () => resolve({
type: 'refProp', key: 'accent',
}),
}, {
text: this.$ts._theme.refConst,
action: () => resolve({
type: 'refConst', key: '',
}),
}, {
text: 'CSS',
action: () => resolve({
type: 'css', value: '',
}),
}], e.currentTarget || e.target);
});
}
}
});
</script>
<style lang="scss" scoped>
.t9makv94 {
> ._section {
> ._content {
> .list-view {
> .item {
min-height: 48px;
word-break: break-all;
.cwepdizn {
max-width: 800px;
margin: 0 auto;
&:not(:last-child) {
margin-bottom: 8px;
> .colorPicker {
> .colors {
padding: 32px;
text-align: center;
> .row {
> .color {
display: inline-block;
position: relative;
width: 64px;
height: 64px;
border-radius: 8px;
> .preview {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
width: 42px;
height: 42px;
border-radius: 4px;
box-shadow: 0 2px 4px rgb(0 0 0 / 30%);
transition: transform 0.15s ease;
}
.select {
margin: 24px 0;
}
.type {
cursor: pointer;
}
.default-value {
opacity: 0.6;
pointer-events: none;
user-select: none;
}
.color {
> input {
display: inline-block;
width: 1.5em;
height: 1.5em;
&:hover {
> .preview {
transform: scale(1.1);
}
}
> div {
margin-left: 8px;
display: inline-block;
&.active {
box-shadow: 0 0 0 2px var(--divider) inset;
}
&.rounded {
border-radius: 999px;
> .preview {
border-radius: 999px;
}
}
&.char {
line-height: 42px;
}
}
}
}
}
> .preview > .preview > .preview {
box-shadow: none;
background: transparent;
}
}
</style>

View File

@@ -1,10 +1,26 @@
<template>
<div class="mk-home" v-hotkey.global="keymap">
<div class="cmuxhskf" v-hotkey.global="keymap">
<div class="new" v-if="queue > 0" :style="{ width: width + 'px' }"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
<div class="_section">
<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _content _vMargin"/>
<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _panel _content _vMargin" fixed/>
<div class="tabs _panel _vMargin">
<div class="left">
<button class="_button tab" @click="() => { src = 'home'; saveSrc(); }" :class="{ active: src === 'home' }" v-tooltip="$ts._timelines.home"><Fa :icon="faHome"/></button>
<button class="_button tab" @click="() => { src = 'local'; saveSrc(); }" :class="{ active: src === 'local' }" v-tooltip="$ts._timelines.local" v-if="isLocalTimelineAvailable"><Fa :icon="faComments"/></button>
<button class="_button tab" @click="() => { src = 'social'; saveSrc(); }" :class="{ active: src === 'social' }" v-tooltip="$ts._timelines.social" v-if="isLocalTimelineAvailable"><Fa :icon="faShareAlt"/></button>
<button class="_button tab" @click="() => { src = 'global'; saveSrc(); }" :class="{ active: src === 'global' }" v-tooltip="$ts._timelines.global" v-if="isGlobalTimelineAvailable"><Fa :icon="faGlobe"/></button>
<span class="divider"></span>
<button class="_button tab" @click="() => { src = 'mentions'; saveSrc(); }" :class="{ active: src === 'mentions' }" v-tooltip="$ts.mentions"><Fa :icon="faAt"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadMentions"/></button>
<button class="_button tab" @click="() => { src = 'directs'; saveSrc(); }" :class="{ active: src === 'directs' }" v-tooltip="$ts.directNotes"><Fa :icon="faEnvelope"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadSpecifiedNotes"/></button>
</div>
<div class="right">
<button class="_button tab" @click="chooseChannel" :class="{ active: src === 'channel' }" v-tooltip="$ts.channel"><Fa :icon="faSatelliteDish"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadChannel"/></button>
<button class="_button tab" @click="chooseAntenna" :class="{ active: src === 'antenna' }" v-tooltip="$ts.antennas"><Fa :icon="faSatellite"/><Fa :icon="faCircle" class="i" v-if="$i.hasUnreadAntenna"/></button>
<button class="_button tab" @click="chooseList" :class="{ active: src === 'list' }" v-tooltip="$ts.lists"><Fa :icon="faListUl"/></button>
</div>
</div>
<XTimeline ref="tl"
class="_content _vMargin"
:key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src === 'channel' ? `channel:${channel.id}` : src"
@@ -23,8 +39,8 @@
<script lang="ts">
import { defineComponent, defineAsyncComponent, computed } from 'vue';
import { faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { faComments } from '@fortawesome/free-regular-svg-icons';
import { faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faPencilAlt, faAt } from '@fortawesome/free-solid-svg-icons';
import { faComments, faEnvelope } from '@fortawesome/free-regular-svg-icons';
import Progress from '@/scripts/loading';
import XTimeline from '@/components/timeline.vue';
import XPostForm from '@/components/post-form.vue';
@@ -49,64 +65,15 @@ export default defineComponent({
menuOpened: false,
queue: 0,
width: 0,
INFO: computed(() => {
const tabs = [{
id: 'home',
title: null,
tooltip: this.$ts._timelines.home,
icon: faHome,
onClick: () => { this.src = 'home'; this.saveSrc(); },
selected: computed(() => this.src === 'home')
}];
if (!this.$instance.disableLocalTimeline || this.$i.isModerator || this.$i.isAdmin) {
tabs.push({
id: 'local',
title: null,
tooltip: this.$ts._timelines.local,
icon: faComments,
onClick: () => { this.src = 'local'; this.saveSrc(); },
selected: computed(() => this.src === 'local')
});
tabs.push({
id: 'social',
title: null,
tooltip: this.$ts._timelines.social,
icon: faShareAlt,
onClick: () => { this.src = 'social'; this.saveSrc(); },
selected: computed(() => this.src === 'social')
});
INFO: computed(() => ({
title: this.$ts.timeline,
icon: this.src === 'local' ? faComments : this.src === 'social' ? faShareAlt : this.src === 'global' ? faGlobe : faHome,
action: {
icon: faPencilAlt,
handler: () => os.post()
}
if (!this.$instance.disableGlobalTimeline || this.$i.isModerator || this.$i.isAdmin) {
tabs.push({
id: 'global',
title: null,
tooltip: this.$ts._timelines.global,
icon: faGlobe,
onClick: () => { this.src = 'global'; this.saveSrc(); },
selected: computed(() => this.src === 'global')
});
}
tabs.push({
id: 'other',
title: null,
icon: faEllipsisH,
onClick: this.choose,
indicate: computed(() => this.$i.hasUnreadAntenna || this.$i.hasUnreadChannel)
});
return {
tabs,
action: {
icon: faPencilAlt,
handler: () => os.post()
}
};
}),
faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faComments, faListUl, faSatellite, faSatelliteDish, faCircle
})),
faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faComments, faListUl, faSatellite, faSatelliteDish, faCircle, faEllipsisH, faAt, faEnvelope,
};
},
@@ -117,8 +84,12 @@ export default defineComponent({
};
},
meta() {
return this.$instance;
isLocalTimelineAvailable(): boolean {
return !this.$instance.disableLocalTimeline || this.$i.isModerator || this.$i.isAdmin;
},
isGlobalTimelineAvailable(): boolean {
return !this.$instance.disableGlobalTimeline || this.$i.isModerator || this.$i.isAdmin;
},
},
@@ -176,16 +147,23 @@ export default defineComponent({
scroll(this.$el, 0);
},
async choose(ev) {
if (this.meta == null) return;
const [antennas, lists, channels] = await Promise.all([
os.api('antennas/list'),
os.api('users/lists/list'),
os.api('channels/followed'),
]);
const antennaItems = antennas.map(antenna => ({
async chooseList(ev) {
const lists = await os.api('users/lists/list');
const items = lists.map(list => ({
text: list.name,
action: () => {
this.list = list;
this.src = 'list';
this.saveSrc();
}
}));
os.modalMenu(items, ev.currentTarget || ev.target);
},
async chooseAntenna(ev) {
const antennas = await os.api('antennas/list');
const items = antennas.map(antenna => ({
text: antenna.name,
icon: faSatellite,
indicate: antenna.hasUnreadNote,
action: () => {
this.antenna = antenna;
@@ -193,18 +171,13 @@ export default defineComponent({
this.saveSrc();
}
}));
const listItems = lists.map(list => ({
text: list.name,
icon: faListUl,
action: () => {
this.list = list;
this.src = 'list';
this.saveSrc();
}
}));
const channelItems = channels.map(channel => ({
os.modalMenu(items, ev.currentTarget || ev.target);
},
async chooseChannel(ev) {
const channels = await os.api('channels/followed');
const items = channels.map(channel => ({
text: channel.name,
icon: faSatelliteDish,
indicate: channel.hasUnreadNote,
action: () => {
// NOTE: チャンネルタイムラインをこのコンポーネントで表示するようにすると投稿フォームはどうするかなどの問題が生じるのでとりあえずページ遷移で
@@ -214,7 +187,7 @@ export default defineComponent({
this.$router.push(`/channels/${channel.id}`);
}
}));
os.modalMenu([...antennaItems, listItems.length > 0 ? null : undefined, ...listItems, channelItems.length > 0 ? null : undefined, ...channelItems], ev.currentTarget || ev.target);
os.modalMenu(items, ev.currentTarget || ev.target);
},
saveSrc() {
@@ -235,7 +208,7 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
.mk-home {
.cmuxhskf {
> .new {
position: fixed;
z-index: 1000;
@@ -249,7 +222,71 @@ export default defineComponent({
}
> ._section {
> .tabs {
display: flex;
box-sizing: border-box;
padding: 0 8px;
max-width: var(--baseContentWidth);
margin-left: auto;
margin-right: auto;
white-space: nowrap;
overflow: auto;
// 影の都合上
position: relative;
> .right {
margin-left: auto;
}
> .left, > .right {
> .tab {
position: relative;
height: 50px;
padding: 0 12px;
&:hover {
color: var(--fgHighlighted);
}
&.active {
color: var(--fgHighlighted);
&:after {
content: "";
display: block;
position: absolute;
bottom: 0;
left: 0;
right: 0;
margin: 0 auto;
width: calc(100% - 16px);
height: 4px;
background: var(--accent);
border-radius: 8px 8px 0 0;
}
}
> .i {
position: absolute;
top: 16px;
right: 8px;
color: var(--indicator);
font-size: 8px;
animation: blink 1s infinite;
}
}
> .divider {
display: inline-block;
width: 1px;
height: 28px;
vertical-align: middle;
margin: 0 8px;
background: var(--divider);
}
}
}
}
}
</style>

View File

@@ -97,7 +97,8 @@ export default defineComponent({
},
tooltip: {
shared: true,
intersect: false
intersect: false,
theme: this.$store.state.darkMode ? 'dark' : 'light',
},
series: [{
name: 'Normal',

View File

@@ -80,7 +80,7 @@
</div>
<template v-if="page === 'index'">
<div v-if="user.pinnedNotes.length > 0" class="_vMargin">
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :detail="true" :pinned="true"/>
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
</div>
<div class="_vMargin">
<XUserTimeline :user="user"/>
@@ -195,7 +195,7 @@
<template v-if="page === 'index'">
<div class="_content _vMargin">
<div v-if="user.pinnedNotes.length > 0" class="_vMargin">
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :detail="true" :pinned="true"/>
<XNote v-for="note in user.pinnedNotes" class="note _vMargin" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/>
</div>
<XPhotos :user="user" :key="user.id" class="_vMargin"/>
<XActivity :user="user" :key="user.id" class="_vMargin"/>

View File

@@ -0,0 +1,308 @@
<template>
<div class="rsqzvsbo" v-if="meta">
<div class="top">
<MkFeaturedPhotos class="bg"/>
<XTimeline class="tl"/>
<div class="shape"></div>
<img src="/assets/misskey.svg" class="misskey"/>
<div class="emojis">
<MkEmoji :normal="true" :no-style="true" emoji="👍"/>
<MkEmoji :normal="true" :no-style="true" emoji="❤"/>
<MkEmoji :normal="true" :no-style="true" emoji="😆"/>
<MkEmoji :normal="true" :no-style="true" emoji="🎉"/>
<MkEmoji :normal="true" :no-style="true" emoji="🍮"/>
</div>
<div class="main _panel">
<div class="bg">
<div class="fade"></div>
</div>
<div class="fg">
<h1>
<img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
</h1>
<div class="about">
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">
<MkButton @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
<MkButton @click="signin()" inline>{{ $ts.login }}</MkButton>
</div>
<div class="status" v-if="onlineUsersCount && stats">
<div>
<I18n :src="$ts.nUsers" text-tag="span" class="users">
<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
</I18n>
<I18n :src="$ts.nNotes" text-tag="span" class="notes">
<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
</I18n>
</div>
<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
<template #n><b>{{ onlineUsersCount }}</b></template>
</I18n>
</div>
<button class="_button _acrylic menu" @click="showMenu"><Fa :icon="faEllipsisH"/></button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { toUnicode } from 'punycode';
import XSigninDialog from '@/components/signin-dialog.vue';
import XSignupDialog from '@/components/signup-dialog.vue';
import MkButton from '@/components/ui/button.vue';
import XNote from '@/components/note.vue';
import MkFeaturedPhotos from '@/components/featured-photos.vue';
import XTimeline from './welcome.timeline.vue';
import { host, instanceName } from '@/config';
import * as os from '@/os';
import number from '@/filters/number';
export default defineComponent({
components: {
MkButton,
XNote,
MkFeaturedPhotos,
XTimeline,
},
data() {
return {
host: toUnicode(host),
instanceName,
meta: null,
stats: null,
tags: [],
onlineUsersCount: null,
faEllipsisH
};
},
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
os.api('stats').then(stats => {
this.stats = stats;
});
os.api('get-online-users-count').then(res => {
this.onlineUsersCount = res.count;
});
os.api('hashtags/list', {
sort: '+mentionedLocalUsers',
limit: 8
}).then(tags => {
this.tags = tags;
});
},
methods: {
signin() {
os.popup(XSigninDialog, {
autoSet: true
}, {}, 'closed');
},
signup() {
os.popup(XSignupDialog, {
autoSet: true
}, {}, 'closed');
},
showMenu(ev) {
os.modalMenu([{
text: this.$t('aboutX', { x: instanceName }),
icon: faInfoCircle,
action: () => {
os.pageWindow('/about');
}
}, {
text: this.$ts.aboutMisskey,
icon: faInfoCircle,
action: () => {
os.pageWindow('/about-misskey');
}
}, null, {
text: this.$ts.help,
icon: faQuestionCircle,
action: () => {
os.pageWindow('/docs');
}
}], ev.currentTarget || ev.target);
},
number
}
});
</script>
<style lang="scss" scoped>
.rsqzvsbo {
> .top {
display: flex;
text-align: center;
min-height: 100vh;
box-sizing: border-box;
padding: 16px;
> .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
> .tl {
position: absolute;
top: 0;
bottom: 0;
right: 64px;
margin: auto;
width: 500px;
height: calc(100% - 128px);
overflow: hidden;
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
@media (max-width: 1200px) {
display: none;
}
}
> .shape {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--accent);
clip-path: polygon(0% 0%, 50% 0%, 15% 100%, 0% 100%);
}
> .misskey {
position: absolute;
top: 42px;
left: 42px;
width: 160px;
@media (max-width: 450px) {
width: 130px;
}
}
> .emojis {
position: absolute;
bottom: 32px;
left: 35px;
> * {
margin-right: 8px;
}
@media (max-width: 1200px) {
display: none;
}
}
> .main {
position: relative;
width: min(480px, 100%);
margin: auto auto auto 128px;
box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
@media (max-width: 1200px) {
margin: auto;
}
> .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 128px;
background-position: center;
background-size: cover;
opacity: 0.75;
> .fade {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 128px;
background: linear-gradient(0deg, var(--panel), var(--X15));
}
}
> .fg {
position: relative;
z-index: 1;
> h1 {
display: block;
margin: 0;
padding: 32px 32px 24px 32px;
> .logo {
vertical-align: bottom;
max-height: 120px;
max-width: min(100%, 300px);
}
}
> .about {
padding: 0 32px;
}
> .action {
padding: 32px;
> * {
line-height: 28px;
}
}
> .status {
border-top: solid 1px var(--divider);
padding: 32px;
font-size: 90%;
> div {
> span:not(:last-child) {
padding-right: 1em;
margin-right: 1em;
border-right: solid 1px var(--divider);
}
}
> .online {
::v-deep(b) {
color: #41b781;
}
::v-deep(span) {
opacity: 0.7;
}
}
}
> .menu {
position: absolute;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
border-radius: 8px;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,238 @@
<template>
<div class="rsqzvsbo" v-if="meta">
<div class="top">
<MkFeaturedPhotos class="bg"/>
<XTimeline class="tl"/>
<div class="shape"></div>
<div class="main">
<h1>
<img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
</h1>
<div class="about">
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">
<MkButton class="signup" @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
<MkButton class="signin" @click="signin()" inline>{{ $ts.login }}</MkButton>
</div>
<div class="status" v-if="onlineUsersCount && stats">
<div>
<I18n :src="$ts.nUsers" text-tag="span" class="users">
<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
</I18n>
<I18n :src="$ts.nNotes" text-tag="span" class="notes">
<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
</I18n>
</div>
<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
<template #n><b>{{ onlineUsersCount }}</b></template>
</I18n>
</div>
</div>
<img src="/assets/misskey.svg" class="misskey"/>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { toUnicode } from 'punycode';
import XSigninDialog from '@/components/signin-dialog.vue';
import XSignupDialog from '@/components/signup-dialog.vue';
import MkButton from '@/components/ui/button.vue';
import XNote from '@/components/note.vue';
import MkFeaturedPhotos from '@/components/featured-photos.vue';
import XTimeline from './welcome.timeline.vue';
import { host, instanceName } from '@/config';
import * as os from '@/os';
import number from '@/filters/number';
export default defineComponent({
components: {
MkButton,
XNote,
XTimeline,
MkFeaturedPhotos,
},
data() {
return {
host: toUnicode(host),
instanceName,
meta: null,
stats: null,
tags: [],
onlineUsersCount: null,
faEllipsisH
};
},
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
os.api('stats').then(stats => {
this.stats = stats;
});
os.api('get-online-users-count').then(res => {
this.onlineUsersCount = res.count;
});
os.api('hashtags/list', {
sort: '+mentionedLocalUsers',
limit: 8
}).then(tags => {
this.tags = tags;
});
},
methods: {
signin() {
os.popup(XSigninDialog, {
autoSet: true
}, {}, 'closed');
},
signup() {
os.popup(XSignupDialog, {
autoSet: true
}, {}, 'closed');
},
showMenu(ev) {
os.modalMenu([{
text: this.$t('aboutX', { x: instanceName }),
icon: faInfoCircle,
action: () => {
os.pageWindow('/about');
}
}, {
text: this.$ts.aboutMisskey,
icon: faInfoCircle,
action: () => {
os.pageWindow('/about-misskey');
}
}, null, {
text: this.$ts.help,
icon: faQuestionCircle,
action: () => {
os.pageWindow('/docs');
}
}], ev.currentTarget || ev.target);
},
number
}
});
</script>
<style lang="scss" scoped>
.rsqzvsbo {
> .top {
min-height: 100vh;
box-sizing: border-box;
> .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
> .tl {
position: absolute;
top: 0;
bottom: 0;
right: 64px;
margin: auto;
width: 500px;
height: calc(100% - 128px);
overflow: hidden;
-webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
mask-image: linear-gradient(0deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 128px, rgba(0,0,0,1) calc(100% - 128px), rgba(0,0,0,0) 100%);
}
> .shape {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--accent);
clip-path: polygon(0% 0%, 40% 0%, 22% 100%, 0% 100%);
}
> .misskey {
position: absolute;
bottom: 64px;
left: 64px;
width: 160px;
}
> .main {
position: relative;
width: min(450px, 100%);
padding: 64px;
color: #fff;
font-size: 1.1em;
@media (max-width: 1200px) {
margin: auto;
}
> h1 {
display: block;
margin: 0 0 32px 0;
padding: 0;
> .logo {
vertical-align: bottom;
max-height: 100px;
}
}
> .about {
padding: 0;
}
> .action {
margin: 32px 0;
> * {
line-height: 32px;
}
> .signup {
background: var(--panel);
color: var(--fg);
}
> .signin {
background: var(--accent);
color: inherit;
}
}
> .status {
margin: 32px 0;
border-top: solid 1px rgba(255, 255, 255, 0.5);
font-size: 90%;
> div {
padding: 16px 0;
> span:not(:last-child) {
padding-right: 1em;
margin-right: 1em;
border-right: solid 1px rgba(255, 255, 255, 0.5);
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,307 @@
<template>
<div class="rsqzvsbo" v-if="meta">
<div class="top">
<MkFeaturedPhotos class="bg"/>
<div class="fade"></div>
<div class="emojis">
<MkEmoji :normal="true" :no-style="true" emoji="👍"/>
<MkEmoji :normal="true" :no-style="true" emoji="❤"/>
<MkEmoji :normal="true" :no-style="true" emoji="😆"/>
<MkEmoji :normal="true" :no-style="true" emoji="🎉"/>
<MkEmoji :normal="true" :no-style="true" emoji="🍮"/>
</div>
<div class="main">
<img src="/assets/misskey.svg" class="misskey"/>
<div class="form _panel">
<div class="bg">
<div class="fade"></div>
</div>
<div class="fg">
<h1>
<img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
</h1>
<div class="about">
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">
<MkButton @click="signup()" inline primary>{{ $ts.signup }}</MkButton>
<MkButton @click="signin()" inline>{{ $ts.login }}</MkButton>
</div>
<div class="status" v-if="onlineUsersCount && stats">
<div>
<I18n :src="$ts.nUsers" text-tag="span" class="users">
<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
</I18n>
<I18n :src="$ts.nNotes" text-tag="span" class="notes">
<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
</I18n>
</div>
<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
<template #n><b>{{ onlineUsersCount }}</b></template>
</I18n>
</div>
<button class="_button _acrylic menu" @click="showMenu"><Fa :icon="faEllipsisH"/></button>
</div>
</div>
<nav class="nav">
<MkA to="/announcements">{{ $ts.announcements }}</MkA>
<MkA to="/explore">{{ $ts.explore }}</MkA>
<MkA to="/channels">{{ $ts.channel }}</MkA>
<MkA to="/featured">{{ $ts.featured }}</MkA>
</nav>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { faEllipsisH, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { toUnicode } from 'punycode';
import XSigninDialog from '@/components/signin-dialog.vue';
import XSignupDialog from '@/components/signup-dialog.vue';
import MkButton from '@/components/ui/button.vue';
import XNote from '@/components/note.vue';
import MkFeaturedPhotos from '@/components/featured-photos.vue';
import XTimeline from './welcome.timeline.vue';
import { host, instanceName } from '@/config';
import * as os from '@/os';
import number from '@/filters/number';
export default defineComponent({
components: {
MkButton,
XNote,
MkFeaturedPhotos,
XTimeline,
},
data() {
return {
host: toUnicode(host),
instanceName,
meta: null,
stats: null,
tags: [],
onlineUsersCount: null,
faEllipsisH
};
},
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
os.api('stats').then(stats => {
this.stats = stats;
});
os.api('get-online-users-count').then(res => {
this.onlineUsersCount = res.count;
});
os.api('hashtags/list', {
sort: '+mentionedLocalUsers',
limit: 8
}).then(tags => {
this.tags = tags;
});
},
methods: {
signin() {
os.popup(XSigninDialog, {
autoSet: true
}, {}, 'closed');
},
signup() {
os.popup(XSignupDialog, {
autoSet: true
}, {}, 'closed');
},
showMenu(ev) {
os.modalMenu([{
text: this.$t('aboutX', { x: instanceName }),
icon: faInfoCircle,
action: () => {
os.pageWindow('/about');
}
}, {
text: this.$ts.aboutMisskey,
icon: faInfoCircle,
action: () => {
os.pageWindow('/about-misskey');
}
}, null, {
text: this.$ts.help,
icon: faQuestionCircle,
action: () => {
os.pageWindow('/docs');
}
}], ev.currentTarget || ev.target);
},
number
}
});
</script>
<style lang="scss" scoped>
.rsqzvsbo {
> .top {
display: flex;
text-align: center;
min-height: 100vh;
box-sizing: border-box;
padding: 16px;
> .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
> .fade {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.25);
}
> .emojis {
position: absolute;
bottom: 32px;
left: 35px;
> * {
margin-right: 8px;
}
@media (max-width: 1200px) {
display: none;
}
}
> .main {
position: relative;
width: min(460px, 100%);
margin: auto;
> .misskey {
width: 150px;
margin-bottom: 16px;
@media (max-width: 450px) {
width: 130px;
}
}
> .form {
position: relative;
box-shadow: 0 12px 32px rgb(0 0 0 / 25%);
> .bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 128px;
background-position: center;
background-size: cover;
opacity: 0.75;
> .fade {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 128px;
background: linear-gradient(0deg, var(--panel), var(--X15));
}
}
> .fg {
position: relative;
z-index: 1;
> h1 {
display: block;
margin: 0;
padding: 32px 32px 24px 32px;
> .logo {
vertical-align: bottom;
max-height: 120px;
}
}
> .about {
padding: 0 32px;
}
> .action {
padding: 32px;
> * {
line-height: 28px;
}
}
> .status {
border-top: solid 1px var(--divider);
padding: 32px;
font-size: 90%;
> div {
> span:not(:last-child) {
padding-right: 1em;
margin-right: 1em;
border-right: solid 1px var(--divider);
}
}
> .online {
::v-deep(b) {
color: #41b781;
}
::v-deep(span) {
opacity: 0.7;
}
}
}
> .menu {
position: absolute;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
border-radius: 8px;
}
}
}
> .nav {
position: relative;
z-index: 2;
margin-top: 20px;
color: #fff;
text-shadow: 0 0 8px black;
font-size: 0.9em;
> *:not(:last-child) {
margin-right: 1.5em;
}
}
}
}
}
</style>

View File

@@ -1,181 +0,0 @@
<template>
<div class="rsqzvsbo _section" v-if="meta">
<div class="overview _monospace" v-if="stats">
<div class="stats">
<div><span>Users</span><span>{{ number(stats.originalUsersCount) }}</span></div>
<div><span>Notes</span><span>{{ number(stats.originalNotesCount) }}</span></div>
<div><span>Reactions</span><span>{{ number(stats.reactionsCount) }}</span></div>
</div>
<div class="tags">
<MkA class="tag" v-for="tag in tags" :to="`/tags/${encodeURIComponent(tag.tag)}`">#{{ tag.tag }}</MkA>
</div>
</div>
<template v-if="meta.pinnedClipId">
<h2># {{ $ts.pinnedNotes }}</h2>
<MkPagination :pagination="clipPagination" #default="{items}">
<XNote class="kmkqjgkl" v-for="note in items" :note="note" :key="note.id"/>
</MkPagination>
</template>
<template v-else>
<h2># {{ $ts.featured }}</h2>
<MkPagination :pagination="featuredPagination" #default="{items}">
<XNote class="kmkqjgkl" v-for="note in items" :note="note" :key="note.id"/>
</MkPagination>
</template>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { toUnicode } from 'punycode';
import XSigninDialog from '@/components/signin-dialog.vue';
import XSignupDialog from '@/components/signup-dialog.vue';
import MkButton from '@/components/ui/button.vue';
import XNote from '@/components/note.vue';
import MkPagination from '@/components/ui/pagination.vue';
import { host, instanceName } from '@/config';
import * as os from '@/os';
import number from '@/filters/number';
export default defineComponent({
components: {
MkButton,
XNote,
MkPagination,
},
data() {
return {
host: toUnicode(host),
instanceName,
meta: null,
stats: null,
tags: [],
clipPagination: {
endpoint: 'clips/notes',
limit: 10,
params: () => ({
clipId: this.meta.pinnedClipId,
})
},
featuredPagination: {
endpoint: 'notes/featured',
limit: 10,
offsetMode: true
},
};
},
created() {
os.api('meta', { detail: true }).then(meta => {
this.meta = meta;
});
os.api('stats').then(stats => {
this.stats = stats;
});
os.api('hashtags/list', {
sort: '+mentionedLocalUsers',
limit: 8
}).then(tags => {
this.tags = tags;
});
},
methods: {
signin() {
os.popup(XSigninDialog, {
autoSet: true
}, {}, 'closed');
},
signup() {
os.popup(XSignupDialog, {
autoSet: true
}, {}, 'closed');
},
number
}
});
</script>
<style lang="scss" scoped>
.rsqzvsbo {
text-align: center;
> h2 {
display: inline-block;
color: #fff;
margin: 16px;
padding: 8px 12px;
background: rgba(0, 0, 0, 0.5);
}
> .overview {
> .stats, > .tags {
display: inline-block;
vertical-align: top;
width: 530px;
padding: 32px;
margin: 16px;
box-sizing: border-box;
@media (max-width: 800px) {
display: block;
width: 100%;
margin: 12px 0;
}
}
> .stats {
background: var(--accent);
border-radius: 12px;
color: #fff;
font-size: 1.5em;
> div {
display: flex;
> span:first-child {
opacity: 0.7;
font-weight: bold;
}
> span:last-child {
margin-left: auto;
}
}
}
> .tags {
background: var(--panel);
border-radius: 12px;
color: var(--fg);
font-size: 1.5em;
> .tag {
margin-right: 1em;
}
}
}
}
.kmkqjgkl {
display: inline-block;
vertical-align: middle;
width: 530px;
margin: 16px;
box-sizing: border-box;
text-align: left;
box-shadow: 0 6px 46px rgb(0 0 0 / 25%);
border-radius: 12px;
@media (max-width: 800px) {
display: block;
width: 100%;
margin: 12px 0;
}
}
</style>

View File

@@ -0,0 +1,51 @@
<template>
<div class="civpbkhh">
<div v-for="note in notes" class="note">
<div class="content _panel">
{{ note.text }}
</div>
<XReactionsViewer :note="note" ref="reactionsViewer"/>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XReactionsViewer from '@/components/reactions-viewer.vue';
import * as os from '@/os';
export default defineComponent({
components: {
XReactionsViewer
},
data() {
return {
notes: [],
}
},
created() {
os.api('notes/featured').then(notes => {
this.notes = notes;
});
}
});
</script>
<style lang="scss" scoped>
.civpbkhh {
text-align: right;
> .note {
margin: 16px 0 16px auto;
> .content {
padding: 16px;
margin: 0 0 0 auto;
max-width: max-content;
border-radius: 16px;
}
}
}
</style>

View File

@@ -8,7 +8,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import XSetup from './welcome.setup.vue';
import XEntrance from './welcome.entrance.vue';
import XEntrance from './welcome.entrance.a.vue';
import { instanceName } from '@/config';
import * as os from '@/os';

View File

@@ -11,6 +11,7 @@ type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
export class Storage<T extends StateDef> {
public readonly key: string;
public readonly keyForLocalStorage: string;
public readonly def: T;
@@ -19,20 +20,22 @@ export class Storage<T extends StateDef> {
public readonly reactiveState: { [K in keyof T]: Ref<T[K]['default']> };
constructor(key: string, def: T) {
this.key = 'pizzax::' + key;
this.key = key;
this.keyForLocalStorage = 'pizzax::' + key;
this.def = def;
// TODO: indexedDBにする
const deviceState = JSON.parse(localStorage.getItem(this.key) || '{}');
const deviceAccountState = $i ? JSON.parse(localStorage.getItem(this.key + '::' + $i.id) || '{}') : {};
const deviceState = JSON.parse(localStorage.getItem(this.keyForLocalStorage) || '{}');
const deviceAccountState = $i ? JSON.parse(localStorage.getItem(this.keyForLocalStorage + '::' + $i.id) || '{}') : {};
const registryCache = $i ? JSON.parse(localStorage.getItem(this.keyForLocalStorage + '::cache::' + $i.id) || '{}') : {};
const state = {};
const reactiveState = {};
for (const [k, v] of Object.entries(def)) {
if (v.where === 'device' && Object.prototype.hasOwnProperty.call(deviceState, k)) {
state[k] = deviceState[k];
} else if (v.where === 'account' && $i && Object.prototype.hasOwnProperty.call($i.clientData, k)) {
state[k] = $i.clientData[k];
} else if (v.where === 'account' && $i && Object.prototype.hasOwnProperty.call(registryCache, k)) {
state[k] = registryCache[k];
} else if (v.where === 'deviceAccount' && Object.prototype.hasOwnProperty.call(deviceAccountState, k)) {
state[k] = deviceAccountState[k];
} else {
@@ -47,16 +50,27 @@ export class Storage<T extends StateDef> {
this.reactiveState = reactiveState as any;
if ($i) {
watch($i, () => {
if (_DEV_) console.log('$i updated');
for (const [k, v] of Object.entries(def)) {
if (v.where === 'account' && Object.prototype.hasOwnProperty.call($i!.clientData, k)) {
state[k] = $i!.clientData[k];
reactiveState[k].value = $i!.clientData[k];
// なぜかsetTimeoutしないとapi関数内でエラーになる(おそらく循環参照してることに原因がありそう)
setTimeout(() => {
api('i/registry/get-all', { scope: ['client', this.key] }).then(kvs => {
const cache = {};
for (const [k, v] of Object.entries(def)) {
if (v.where === 'account') {
if (Object.prototype.hasOwnProperty.call(kvs, k)) {
state[k] = kvs[k];
reactiveState[k].value = kvs[k];
cache[k] = kvs[k];
} else {
state[k] = v.default;
reactiveState[k].value = v.default;
}
}
}
}
});
localStorage.setItem(this.keyForLocalStorage + '::cache::' + $i.id, JSON.stringify(cache));
});
}, 1);
// TODO: streamingのuser storage updateイベントを監視して更新
}
}
@@ -68,21 +82,26 @@ export class Storage<T extends StateDef> {
switch (this.def[key].where) {
case 'device': {
const deviceState = JSON.parse(localStorage.getItem(this.key) || '{}');
const deviceState = JSON.parse(localStorage.getItem(this.keyForLocalStorage) || '{}');
deviceState[key] = value;
localStorage.setItem(this.key, JSON.stringify(deviceState));
localStorage.setItem(this.keyForLocalStorage, JSON.stringify(deviceState));
break;
}
case 'deviceAccount': {
if ($i == null) break;
const deviceAccountState = JSON.parse(localStorage.getItem(this.key + '::' + $i.id) || '{}');
const deviceAccountState = JSON.parse(localStorage.getItem(this.keyForLocalStorage + '::' + $i.id) || '{}');
deviceAccountState[key] = value;
localStorage.setItem(this.key + '::' + $i.id, JSON.stringify(deviceAccountState));
localStorage.setItem(this.keyForLocalStorage + '::' + $i.id, JSON.stringify(deviceAccountState));
break;
}
case 'account': {
api('i/update-client-setting', {
name: key,
if ($i == null) break;
const cache = JSON.parse(localStorage.getItem(this.keyForLocalStorage + '::cache::' + $i.id) || '{}');
cache[key] = value;
localStorage.setItem(this.keyForLocalStorage + '::cache::' + $i.id, JSON.stringify(cache));
api('i/registry/set', {
scope: ['client', this.key],
key: key,
value: value
});
break;

View File

@@ -29,6 +29,7 @@ export const router = createRouter({
{ path: '/featured', component: page('featured') },
{ path: '/docs', component: page('docs') },
{ path: '/theme-editor', component: page('theme-editor') },
{ path: '/advanced-theme-editor', component: page('advanced-theme-editor') },
{ path: '/docs/:doc', component: page('doc'), props: route => ({ doc: route.params.doc }) },
{ path: '/explore', component: page('explore') },
{ path: '/explore/tags/:tag', props: true, component: page('explore') },
@@ -54,7 +55,7 @@ export const router = createRouter({
{ path: '/my/lists', component: page('my-lists/index') },
{ path: '/my/lists/:list', component: page('my-lists/list') },
{ path: '/my/groups', component: page('my-groups/index') },
{ path: '/my/groups/:group', component: page('my-groups/group') },
{ path: '/my/groups/:group', component: page('my-groups/group'), props: route => ({ groupId: route.params.group }) },
{ path: '/my/antennas', component: page('my-antennas/index') },
{ path: '/my/clips', component: page('my-clips/index') },
{ path: '/scratchpad', component: page('scratchpad') },
@@ -80,7 +81,6 @@ export const router = createRouter({
{ path: '/miauth/:session', component: page('miauth') },
{ path: '/authorize-follow', component: page('follow') },
{ path: '/share', component: page('share') },
{ path: '/test', component: page('test') },
{ path: '/:catchAll(.*)', component: page('not-found') }
],
// なんかHacky

View File

@@ -1,19 +1,13 @@
import autobind from 'autobind-decorator';
import * as seedrandom from 'seedrandom';
import { Variable, PageVar, envVarsDef, funcDefs, Block, isFnBlock } from '.';
import { Variable, PageVar, envVarsDef, Block, isFnBlock, Fn, HpmlScope, HpmlError } from '.';
import { version } from '@/config';
import { AiScript, utils, values } from '@syuilo/aiscript';
import { createAiScriptEnv } from '../aiscript/api';
import { collectPageVars } from '../collect-page-vars';
import { initLib } from './lib';
import { initHpmlLib, initAiLib } from './lib';
import * as os from '@/os';
import { markRaw, ref, Ref } from 'vue';
type Fn = {
slots: string[];
exec: (args: Record<string, any>) => ReturnType<Hpml['evaluate']>;
};
/**
* Hpml evaluator
*/
@@ -41,7 +35,7 @@ export class Hpml {
if (this.opts.enableAiScript) {
this.aiscript = markRaw(new AiScript({ ...createAiScriptEnv({
storageKey: 'pages:' + this.page.id
}), ...initLib(this)}, {
}), ...initAiLib(this)}, {
in: (q) => {
return new Promise(ok => {
os.dialog({
@@ -137,7 +131,7 @@ export class Hpml {
}
@autobind
private _interpolate(str: string, scope: Scope) {
private _interpolateScope(str: string, scope: HpmlScope) {
return str.replace(/{(.+?)}/g, match => {
const v = scope.getState(match.slice(1, -1).trim());
return v == null ? 'NULL' : v.toString();
@@ -157,14 +151,14 @@ export class Hpml {
}
for (const v of this.variables) {
values[v.name] = this.evaluate(v, new Scope([values]));
values[v.name] = this.evaluate(v, new HpmlScope([values]));
}
return values;
}
@autobind
private evaluate(block: Block, scope: Scope): any {
private evaluate(block: Block, scope: HpmlScope): any {
if (block.type === null) {
return null;
}
@@ -174,11 +168,11 @@ export class Hpml {
}
if (block.type === 'text' || block.type === 'multiLineText') {
return this._interpolate(block.value || '', scope);
return this._interpolateScope(block.value || '', scope);
}
if (block.type === 'textList') {
return this._interpolate(block.value || '', scope).trim().split('\n');
return this._interpolateScope(block.value || '', scope).trim().split('\n');
}
if (block.type === 'ref') {
@@ -197,7 +191,8 @@ export class Hpml {
}
}
if (isFnBlock(block)) { // ユーザー関数定義
// Define user function
if (isFnBlock(block)) {
return {
slots: block.value.slots.map(x => x.name),
exec: (slotArg: Record<string, any>) => {
@@ -206,7 +201,8 @@ export class Hpml {
} as Fn;
}
if (block.type.startsWith('fn:')) { // ユーザー関数呼び出し
// Call user function
if (block.type.startsWith('fn:')) {
const fnName = block.type.split(':')[1];
const fn = scope.getState(fnName);
const args = {} as Record<string, any>;
@@ -219,77 +215,9 @@ export class Hpml {
if (block.args === undefined) return null;
const date = new Date();
const day = `${this.opts.visitor ? this.opts.visitor.id : ''} ${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
const funcs: { [p in keyof typeof funcDefs]: Function } = {
not: (a: boolean) => !a,
or: (a: boolean, b: boolean) => a || b,
and: (a: boolean, b: boolean) => a && b,
eq: (a: any, b: any) => a === b,
notEq: (a: any, b: any) => a !== b,
gt: (a: number, b: number) => a > b,
lt: (a: number, b: number) => a < b,
gtEq: (a: number, b: number) => a >= b,
ltEq: (a: number, b: number) => a <= b,
if: (bool: boolean, a: any, b: any) => bool ? a : b,
for: (times: number, fn: Fn) => {
const result = [];
for (let i = 0; i < times; i++) {
result.push(fn.exec({
[fn.slots[0]]: i + 1
}));
}
return result;
},
add: (a: number, b: number) => a + b,
subtract: (a: number, b: number) => a - b,
multiply: (a: number, b: number) => a * b,
divide: (a: number, b: number) => a / b,
mod: (a: number, b: number) => a % b,
round: (a: number) => Math.round(a),
strLen: (a: string) => a.length,
strPick: (a: string, b: number) => a[b - 1],
strReplace: (a: string, b: string, c: string) => a.split(b).join(c),
strReverse: (a: string) => a.split('').reverse().join(''),
join: (texts: string[], separator: string) => texts.join(separator || ''),
stringToNumber: (a: string) => parseInt(a),
numberToString: (a: number) => a.toString(),
splitStrByLine: (a: string) => a.split('\n'),
pick: (list: any[], i: number) => list[i - 1],
listLen: (list: any[]) => list.length,
random: (probability: number) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability,
rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)),
randomPick: (list: any[]) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)],
dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${block.id}`)() * 100) < probability,
dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${block.id}`)() * (max - min + 1)),
dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${block.id}`)() * list.length)],
seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability,
seedRannum: (seed: any, min: number, max: number) => min + Math.floor(seedrandom(seed)() * (max - min + 1)),
seedRandomPick: (seed: any, list: any[]) => list[Math.floor(seedrandom(seed)() * list.length)],
DRPWPM: (list: string[]) => {
const xs = [];
let totalFactor = 0;
for (const x of list) {
const parts = x.split(' ');
const factor = parseInt(parts.pop()!, 10);
const text = parts.join(' ');
totalFactor += factor;
xs.push({ factor, text });
}
const r = seedrandom(`${day}:${block.id}`)() * totalFactor;
let stackedFactor = 0;
for (const x of xs) {
if (r >= stackedFactor && r <= stackedFactor + x.factor) {
return x.text;
} else {
stackedFactor += x.factor;
}
}
return xs[0].text;
},
};
const funcs = initHpmlLib(block, scope, this.opts.randomSeed, this.opts.visitor);
// Call function
const fnName = block.type;
const fn = (funcs as any)[fnName];
if (fn == null) {
@@ -299,53 +227,3 @@ export class Hpml {
}
}
}
class HpmlError extends Error {
public info?: any;
constructor(message: string, info?: any) {
super(message);
this.info = info;
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, HpmlError);
}
}
}
class Scope {
private layerdStates: Record<string, any>[];
public name: string;
constructor(layerdStates: Scope['layerdStates'], name?: Scope['name']) {
this.layerdStates = layerdStates;
this.name = name || 'anonymous';
}
@autobind
public createChildScope(states: Record<string, any>, name?: Scope['name']): Scope {
const layer = [states, ...this.layerdStates];
return new Scope(layer, name);
}
/**
* 指定した名前の変数の値を取得します
* @param name 変数名
*/
@autobind
public getState(name: string): any {
for (const later of this.layerdStates) {
const state = later[name];
if (state !== undefined) {
return state;
}
}
throw new HpmlError(
`No such variable '${name}' in scope '${this.name}'`, {
scope: this.layerdStates
});
}
}

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