Compare commits

..

187 Commits

Author SHA1 Message Date
syuilo
19e1abe110 12.36.0 2020-04-22 21:43:54 +09:00
syuilo
d19441f3ae fix(client): Fix lint 2020-04-22 19:51:09 +09:00
syuilo
6e3ee05cb6 refactor(client): 2020-04-22 19:36:28 +09:00
syuilo
51476ad06f New Crowdin translations (#6281)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-04-22 10:10:43 +09:00
syuilo
1f998168e2 feat(client): Implement default upload folder setting
Resolve #5985
2020-04-22 00:34:56 +09:00
syuilo
e72011f1da chore: Update deps 2020-04-22 00:08:29 +09:00
syuilo
0df3e22e51 Fix #6289 2020-04-21 20:26:54 +09:00
syuilo
5a9530ccd4 Fix #6229 2020-04-21 20:16:37 +09:00
syuilo
0a4d119d86 fix(lint): Use const 2020-04-20 21:36:58 +09:00
syuilo
2ee0e07bb6 refactor(client): 2020-04-20 21:35:27 +09:00
Acid Chicken (硫酸鶏)
533c9a4fe1 Merge pull request #6282 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-04-20 01:02:14 +09:00
Acid Chicken (硫酸鶏)
30c000116f Update README.md [AUTOGEN] 2020-04-19 20:51:06 +09:00
syuilo
dc649fe420 12.35.2 2020-04-19 18:43:24 +09:00
syuilo
4a8ec173ae Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-04-19 18:41:16 +09:00
syuilo
26d6fe9a4e chore: Update dep 2020-04-19 18:41:02 +09:00
syuilo
46f5175a0d New Crowdin translations (#6278)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

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

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

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-04-19 18:39:22 +09:00
syuilo
f704e7a602 enhance(pages): Improve hcart 2020-04-19 17:41:01 +09:00
syuilo
8cefcaa55f fix(client): Fix bug that cannot post when image only 2020-04-19 17:40:46 +09:00
syuilo
164c6505f2 enhance(client): Use icon instead of text 2020-04-19 17:39:54 +09:00
syuilo
0a1b83c70f feat(aiscript): Better env vars 2020-04-19 16:28:19 +09:00
syuilo
e60048eb96 12.35.1 2020-04-19 16:19:01 +09:00
syuilo
8957eec475 fix(client): Fix canvas overflow 2020-04-19 16:15:24 +09:00
syuilo
e790af566c chore: 🎨 2020-04-19 16:09:57 +09:00
syuilo
43fe0cd62e feat(pages): Improve chart 2020-04-19 15:48:05 +09:00
syuilo
e22a296dc7 12.35.0 2020-04-19 09:44:20 +09:00
syuilo
ac19ebc850 New Crowdin translations (#6277)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-04-19 09:41:03 +09:00
syuilo
1e0060193a chore: Update deps 2020-04-19 09:40:19 +09:00
syuilo
72271d905d fix(pages): Fix chart type detection 2020-04-19 09:09:38 +09:00
syuilo
8d39283d46 Resolve #6276 2020-04-19 09:05:20 +09:00
syuilo
4364122804 feat(pages): Improve chart 2020-04-19 08:25:22 +09:00
syuilo
3b6dbd6dc3 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-04-18 18:33:50 +09:00
syuilo
7c61fc37c5 Resolve #6274 2020-04-18 18:33:45 +09:00
MeiMei
f530b5237d TLにNote追加時にdeepcopyする (#6275) 2020-04-18 16:05:39 +09:00
syuilo
9b9b6ade64 New Crowdin translations (#6271)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)
2020-04-18 12:07:01 +09:00
Xeltica
e184c1cdfb カスタム絵文字リアクションがプレビューされない不具合を修正 fix #6272 (#6273)
* カスタム絵文字リアクションがプレビューされない不具合を修正

* add comments
2020-04-18 12:06:44 +09:00
syuilo
e0e4b43707 chore: Update dep 2020-04-18 12:02:33 +09:00
syuilo
1d70b33894 12.34.0 2020-04-17 22:31:21 +09:00
syuilo
44ea1be930 chore(client): 🎨 2020-04-17 20:36:51 +09:00
syuilo
a1bf54fe16 chore(client): 🎨 2020-04-17 20:30:12 +09:00
syuilo
88c57359b3 New Crowdin translations (#6268)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)
2020-04-17 20:26:16 +09:00
syuilo
050564f717 chore: Update dep 2020-04-17 20:25:26 +09:00
syuilo
75d59a9c9b feat(pages): Add rect method 2020-04-17 15:51:36 +09:00
syuilo
9139c863bf feat(pages): Disable AiScript step limitation to improve usability 2020-04-17 15:51:25 +09:00
syuilo
84a1ec01bc 12.33.0 2020-04-16 23:23:07 +09:00
syuilo
36e59c5b5f chore: update deps 2020-04-16 23:20:34 +09:00
syuilo
5389b16c59 chore(client): 🎨 2020-04-16 23:13:33 +09:00
syuilo
da3008af1c fix(pages): AiScript変数があると型チェックができない問題を修正 2020-04-16 23:04:49 +09:00
syuilo
6637766554 feat(pages): Add arc method 2020-04-16 18:11:13 +09:00
syuilo
2bc63631a4 12.32.0 2020-04-16 01:09:28 +09:00
syuilo
5215721942 New Crowdin translations (#6255)
* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-04-16 01:05:32 +09:00
tamaina
d02e14cb94 Fix Media List in CW Content (#6099) 2020-04-16 01:04:21 +09:00
MeiMei
fa75b40dfd リアクションの修正 (#6260) 2020-04-16 00:47:17 +09:00
MeiMei
f32d8b7069 0以下のリアクションは送らないように Resolve #6263 (#6264) 2020-04-16 00:45:43 +09:00
syuilo
90e8527556 Resolve #6256 2020-04-16 00:39:21 +09:00
MeiMei
66377d3f27 Update CHANGELOG.md 2020-04-14 01:49:52 +09:00
MeiMei
c6ae93df80 Update CHANGELOG.md 2020-04-14 01:45:55 +09:00
MeiMei
55e9099091 Update CHANGELOG.md 2020-04-14 01:37:24 +09:00
MeiMei
c6ace29446 Update CHANGELOG.md 2020-04-14 01:35:33 +09:00
mei23
b0b885aacd lint 2020-04-14 01:13:01 +09:00
syuilo
3615b5d353 12.31.0 2020-04-14 00:54:39 +09:00
syuilo
74c71e6283 New Crowdin translations (#6240)
* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)
2020-04-14 00:49:43 +09:00
MeiMei
9b07c5af05 リモートのカスタム絵文字リアクションを表示できるように (#6239)
* リモートのカスタム絵文字リアクションを表示できるように

* AP

* DBマイグレーション

* ローカルのリアクションの.

* fix

* fix

* fix

* space
2020-04-14 00:42:59 +09:00
syuilo
cda1803e59 chore(client): 🎨 2020-04-14 00:13:49 +09:00
tamaina
96eab7e12b 投稿のURLプレビューポップアップを改良 (#6226)
* URLプレビューポップアップを改良

- タッチデバイスでは表示しないように
- 幅をレスポンシブに

* Use maxTouchPoints to detect touch device

* fix
2020-04-14 00:00:52 +09:00
MeiMei
916512fd47 同じリアクション削除を同時に行うとリアクションカウントがおかしくなることがあるのを修正 (#6253)
* Fix #6252

* quote

* Use IdentifiableError
2020-04-13 23:58:38 +09:00
Satsuki Yanagi
58d3a37908 sensitiveではないメディアも非表示にできるように (#6248)
* sensitiveではないメディアも非表示にできるように

* mounted -> created

* remove unnecessary v-if
2020-04-13 23:55:36 +09:00
syuilo
a19e252c9e feat(client): Improve pages aiscript 2020-04-13 23:46:53 +09:00
Satsuki Yanagi
63225ed0fd モデレーション周りのv11の機能復元 (#6249)
* モデレーション周りのv11の機能復元

* i18n

* wip

* wip

Co-authored-by: syuilo <syuilotan@yahoo.co.jp>
2020-04-13 23:27:12 +09:00
syuilo
11cc9cbc7c Resolve #5755 2020-04-13 03:23:23 +09:00
MeiMei
36b9a0d42f プロキシの除外ホスト (#6244)
* プロキシの除外ホスト

* オブジェクトストレージとの通信にProxyを使うかを選択できるように

* fix lint

* コメント

Co-authored-by: rinsuki <428rinsuki+git@gmail.com>
2020-04-12 20:32:34 +09:00
syuilo
e7da10ae58 Resolve #6242 2020-04-12 19:57:18 +09:00
syuilo
f07047d1e8 AiScript関連 2020-04-12 19:38:19 +09:00
syuilo
c62aff76af 12.30.0 2020-04-11 23:14:23 +09:00
MeiMei
1c20de4e9c Add yarn.lock (#6241) 2020-04-11 23:01:29 +09:00
syuilo
4903eb4a4a Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-04-11 22:44:36 +09:00
syuilo
b5981ab544 feat(client): Implement AiScript scratchpad 2020-04-11 22:44:32 +09:00
MeiMei
00e1dbfdfb Fix typo (#6238) 2020-04-11 19:49:25 +09:00
syuilo
df69ca4d56 New Crowdin translations (#6227)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)
2020-04-11 18:34:26 +09:00
Balazs Nadasdi
cb631d4abb Option to hide revealed sensitive media (#6209)
* Option to hide revealed sensitive media

This PR commit adds a button on sensitive images and videos
to to hide them without refreshing the page.

* fix position with multiple images

* Fixing some lint problems

Not related to this PR, but "Node.js CI / lint" failed on it.
2020-04-11 18:32:55 +09:00
Satsuki Yanagi
3c351d8300 ドイツ語と中国語(繁体)を有効に (#6223) 2020-04-11 18:30:39 +09:00
MeiMei
ca66acac2b ファイルのダウンロードがタイムアウトしなくなっているのを修正など (#6233)
* Refactor download / file-info

* body read timeout on download Fix syuilo#6232
2020-04-11 18:28:40 +09:00
MeiMei
9fcf94b197 Fix url type of AP object #6231 (#6234) 2020-04-11 18:27:58 +09:00
syuilo
aa34000f0b Update package 2020-04-11 18:25:36 +09:00
MeiMei
d3c0f3c251 Use node-fetch instead of request (#6228)
* requestをnode-fetchになど

* format

* fix error

* t

* Fix test
2020-04-09 23:42:23 +09:00
syuilo
bb7edfee04 Create aiscript.ja-JP.md 2020-04-05 18:02:39 +09:00
syuilo
caa14c70ef 12.29.0 2020-04-05 18:00:23 +09:00
syuilo
3a0f72867f New Crowdin translations (#6198)
* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

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

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

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (German)
2020-04-05 17:56:43 +09:00
Balazs Nadasdi
10d72742f5 Ability to set header image for a Page (#6210)
* Ability to set header image for a Page

 - Add header image to Page
 - Show it on Page view
 - Show correctly it on Page list view
 - On the Page list view, pages have a light border
   to make it easier to see an image belongs to a page

* Maybe it looks better

* Use <img> instead if <x-image>

* src -> :src; set width

* Update page.vue

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2020-04-05 17:55:51 +09:00
syuilo
1b9f8a87d3 chore: Update dependencies 🚀 2020-04-04 11:08:10 +09:00
syuilo
d4a630902d refactor: Use === 2020-04-04 08:46:54 +09:00
syuilo
fef5ec874b enhance(server): Log error message when internal error occured 2020-04-04 08:27:16 +09:00
syuilo
f2e347fec1 perf(client): Lazy load themes 2020-04-04 08:25:28 +09:00
tamaina
cd3c2484ee Update CHANGELOG.md 2020-04-03 23:57:57 +09:00
MeiMei
6a396ef5e3 APIリファレンスでレスポンスのスキーマを見るのにいちいち2回クリックさせられるのを修正 (#6217) 2020-04-03 23:36:13 +09:00
syuilo
eec1af1f52 Revert 2020-04-03 23:35:14 +09:00
MeiMei
99fc77b678 APメンションはaudienceじゃなくてtagを参照するなど (#6128)
* APメンションはaudienceじゃなくてtagを参照するなど

* AP/tag/Mentionではurlじゃなくてuriを提示する

* createPersonでaliasが入力された場合に対応

* AP HTMLパースでMention/Hashtag判定にtagを使うように

* fix

* indent

* use hashtag name

* fix

* URLエンコード不要だったら<>を使わないの条件が消えたたのを修正
2020-04-03 22:51:38 +09:00
MeiMei
8bb311df51 APIリファレンスのカテゴリ処理の修正 (#6218)
* APIリファレンスのカテゴリ処理の修正

* tune
2020-04-03 22:42:29 +09:00
Satsuki Yanagi
a77df249c2 i18n (#6219) 2020-04-03 22:41:18 +09:00
Acid Chicken (硫酸鶏)
2883bca257 Merge pull request #6211 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-04-03 21:43:09 +09:00
Acid Chicken (硫酸鶏)
dd3af6886b Update README.md [AUTOGEN] 2020-04-03 20:52:08 +09:00
tamaina
33bcf2d1ea Fix lint 2020-04-03 17:17:46 +09:00
tamaina
795fb0eb60 Pre-render ReDoc
redoc-cliはexpandResponsesは200のみとすると数値と認識されてしまい設定できないため202,204という指定にしています
2020-04-03 17:13:41 +09:00
syuilo
9e9d378bf1 feat(streaming): Add emoji added event 2020-04-02 22:17:17 +09:00
syuilo
4a6b0edce6 Update api.ja-JP.md 2020-04-02 22:09:25 +09:00
MeiMei
356225af14 Use url if available (#6214)
* Fix #6213

* other link

* fix
2020-04-02 21:59:14 +09:00
mei23
331305e6c7 lint 2020-04-02 05:41:03 +09:00
Acid Chicken (硫酸鶏)
917b9475a5 Merge pull request #6208 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-04-01 03:46:30 +09:00
Acid Chicken (硫酸鶏)
e895fc954b Update README.md [AUTOGEN] 2020-04-01 01:58:08 +09:00
Acid Chicken (硫酸鶏)
6d3e18a6a1 Merge pull request #6191 from syuilo/patch/autogen/v11
[AUTOMATED] Update README.md
2020-04-01 01:41:53 +09:00
syuilo
79354f4faf Refactoring 2020-03-31 09:15:04 +09:00
syuilo
28f8933c3c Refactoring 2020-03-31 09:11:43 +09:00
syuilo
10356b4041 Merge branch 'master' into develop 2020-03-31 08:08:22 +09:00
syuilo
6a732ab1cd Fix #6203 2020-03-31 08:07:10 +09:00
MeiMei
47322b35ff APIの権限設定漏れを修正 (#6202)
* Fix: 権限設定漏れ

* requireAdmin
2020-03-30 11:45:57 +09:00
MeiMei
4c6d0386b9 admin/accounts/createで一般ユーザーがアカウントを作成し放題なのを修正 (#6205) 2020-03-30 09:27:39 +09:00
MeiMei
a448172952 Fix #6199 (#6201) 2020-03-29 23:18:03 +09:00
MeiMei
244ef0cb8f トークン系の乱数ソースではcryptoを使うように (#6200) 2020-03-29 23:16:36 +09:00
syuilo
cc66a1f9c7 Merge branch 'develop' 2020-03-29 17:46:31 +09:00
syuilo
e2183400e5 12.28.0 2020-03-29 17:45:53 +09:00
syuilo
afc531bd26 New Crowdin translations (#6194)
* 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 (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

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

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)
2020-03-29 17:45:38 +09:00
syuilo
02cc1891f2 Add miauth info into meta.features 2020-03-29 17:44:14 +09:00
syuilo
09e3ddbd57 アプリの権限を確認できるように 2020-03-29 17:06:36 +09:00
syuilo
d0fff562ea Fix bug 2020-03-29 17:04:22 +09:00
syuilo
8ce5366e80 テーマ関係 2020-03-29 16:09:44 +09:00
syuilo
bfcda7cc02 Better sql log 2020-03-29 14:08:19 +09:00
syuilo
c52aeb6618 Fix type 2020-03-29 11:28:55 +09:00
syuilo
f5ebfdca61 🎨 2020-03-29 11:06:58 +09:00
syuilo
db93838729 Clean up 2020-03-29 10:59:05 +09:00
syuilo
bb835a6e8a Fix bug 2020-03-29 10:49:43 +09:00
syuilo
52feba0e3a Refactor: Use === 2020-03-29 10:39:36 +09:00
syuilo
a1076c3108 🎨 2020-03-29 10:34:46 +09:00
syuilo
bad068b20e ✌️ 2020-03-29 10:17:23 +09:00
syuilo
ec41d461c0 ✌️ 2020-03-29 10:16:32 +09:00
syuilo
a826cd6845 ✌️ 2020-03-29 10:15:33 +09:00
syuilo
a950b6193a インスタンス一覧でソートできるように 2020-03-29 10:14:33 +09:00
MeiMei
2cc4de2b23 Update CHANGELOG.md 2020-03-29 03:13:48 +09:00
MeiMei
03ef6996ff Update CHANGELOG.md 2020-03-29 03:01:45 +09:00
tamaina
d1e5def30e Update CHANGELOG.md 2020-03-29 00:41:28 +09:00
tamaina
02fbda2154 Update CHANGELOG.md 2020-03-29 00:40:08 +09:00
tamaina
c21694a24a Update CHANGELOG.md 2020-03-29 00:38:56 +09:00
tamaina
cb98336b0a Update CHANGELOG.md 2020-03-29 00:22:43 +09:00
syuilo
97d25bc6a3 Merge branch 'develop' 2020-03-28 22:35:19 +09:00
syuilo
b36a1a9d0e 12.27.1 2020-03-28 22:35:05 +09:00
syuilo
cd44ff0aaa 🎨 2020-03-28 22:25:52 +09:00
syuilo
032571c326 Update coloring 🎨 2020-03-28 22:14:13 +09:00
syuilo
6b890e3f82 Fix style 2020-03-28 22:10:14 +09:00
syuilo
9998845b21 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2020-03-28 22:04:26 +09:00
syuilo
7ee4385deb Fix bug 2020-03-28 22:04:23 +09:00
mei23
695277c9eb lint fix 2020-03-28 20:56:17 +09:00
syuilo
f014a79f8d Merge branch 'develop' 2020-03-28 19:52:41 +09:00
syuilo
1a6d47a633 12.27.0 2020-03-28 19:52:00 +09:00
syuilo
12eed8f859 New Crowdin translations (#6189)
* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)
2020-03-28 19:50:10 +09:00
syuilo
549092d9aa Update gen-token.ts 2020-03-28 19:44:57 +09:00
syuilo
b245393bc4 Update apps.vue 2020-03-28 19:40:03 +09:00
syuilo
dcd43a17ba インストールしたアプリ見れるようにしたり削除できるようにしたり 2020-03-28 19:33:11 +09:00
syuilo
b8088dc01a ✌️ 2020-03-28 18:33:24 +09:00
syuilo
8e1b90ab43 Improve log handling 2020-03-28 18:28:21 +09:00
syuilo
614a1d74dd Resolve #6192 2020-03-28 18:07:41 +09:00
syuilo
9ea1ed8559 Add i/apps private API 2020-03-28 16:52:52 +09:00
syuilo
3e1e234799 Resolve #6193 2020-03-28 15:57:31 +09:00
syuilo
62f5ecd278 🎨 2020-03-28 11:36:44 +09:00
syuilo
27733e2119 Fix doc page 2020-03-28 11:32:19 +09:00
syuilo
6be127e18b Implement MiAuth 2020-03-28 11:24:37 +09:00
Acid Chicken (硫酸鶏)
4bf1c23b3c Update README.md [AUTOGEN] 2020-03-28 04:55:08 +09:00
syuilo
608b8bb741 wip 2020-03-27 20:24:32 +09:00
syuilo
ef01eec36e Merge branch 'develop' 2020-03-25 23:21:48 +09:00
syuilo
5dbdd0e685 12.26.0 2020-03-25 23:21:31 +09:00
syuilo
5273050ab3 Update patrons 2020-03-25 23:21:23 +09:00
syuilo
fae3b02e5a 🎨 2020-03-25 23:15:08 +09:00
syuilo
3489e4af1e 🎨 2020-03-25 22:57:13 +09:00
syuilo
8e9bd0bbd5 Fix dark mode sync bug 2020-03-25 22:49:42 +09:00
syuilo
3725b5bc34 New Crowdin translations (#6181)
* New translations ja-JP.yml (Chinese Simplified)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)
2020-03-25 22:40:31 +09:00
和風ドレッシング
998a59aa5e Fix #5986 (#6184)
* Fix #5986

* Fix #5986 追加修正
2020-03-25 22:26:50 +09:00
syuilo
86c017674a Update favicon.png 2020-03-25 21:24:33 +09:00
syuilo
cbae87cd11 🎨 2020-03-25 21:19:39 +09:00
syuilo
5bc1f8d468 Update icon.svg 2020-03-25 19:15:34 +09:00
syuilo
d3a355e164 Adjust icon size 🎨 2020-03-25 19:10:41 +09:00
和風ドレッシング
45413c9d28 Fix #6176 (#6183) 2020-03-25 15:57:35 +09:00
syuilo
082ee8836f Update icon 🎨 2020-03-25 14:54:34 +09:00
Xeltica
2f5bd5e6d7 Update CHANGELOG.md 2020-03-24 21:12:24 +09:00
syuilo
639e0137cc Merge branch 'develop' 2020-03-23 19:48:33 +09:00
syuilo
2f898aa037 12.25.0 2020-03-23 19:48:19 +09:00
syuilo
a43a225740 Fix #6180 2020-03-23 19:47:02 +09:00
syuilo
833c39969b Refactor 2020-03-23 19:42:26 +09:00
syuilo
e25dea27e7 Better theme validation 2020-03-23 19:09:20 +09:00
syuilo
dac962580b テーマインポート機能を実装するなど 2020-03-23 19:06:46 +09:00
tamaina
b12bf78c6d Update CHANGELOG.md 2020-03-23 13:17:29 +09:00
261 changed files with 5761 additions and 3335 deletions

View File

@@ -142,6 +142,11 @@ id: 'aid'
# Proxy for HTTP/HTTPS
#proxy: http://127.0.0.1:3128
#proxyBypassHosts: [
# 'example.com',
# '192.0.2.8'
#]
# Proxy for SMTP/SMTPS
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4

View File

@@ -1,6 +1,165 @@
ChangeLog
=========
12.35.1 (2020/4/19)
-------------------
### 🐛Fixes
* Pagesのチャート描画を調整
12.35.0 (2020/4/19)
-------------------
### ✨Improvements
* Pagesでチャートを描画できるように
* Pagesでキャンバスの画像を投稿フォームで添付できるように
* AiScriptのバージョンアップ
### 🐛Fixes
* タイムラインウィジェットの数が多ければ多いほど、リアクションが多く付いて見える問題を修正
* カスタム絵文字リアクションがプレビューされない不具合を修正
12.34.0 (2020/4/17)
-------------------
### ✨Improvements
* Pagesでrectメソッドを追加
* AiScriptのバージョンアップ
12.33.0 (2020/4/16)
-------------------
### ✨Improvements
* Pagesで円を書くメソッドを追加
* AiScriptのバージョンアップ
### 🐛Fixes
* PagesでAiScript変数があると編集が機能しなくなる問題を修正
12.32.0 (2020/4/16)
-------------------
### ✨Improvements
* Pagesで画像を描画できるように
* AiScriptのバージョンアップ
* 0以下のリアクションは送らないように
### 🐛Fixes
* リアクションの修正
* Fix Media List in CW Content
12.31.0 (2020/4/14)
-------------------
### ✨Improvements
* プロキシの除外ホスト指定とオブジェクトストレージへの適用を除外するオプション
* AiScript
* モデレーション関連機能
* sensitiveではないメディアも非表示にできるように
* 投稿のURLプレビューポップアップを改良
* リモートのカスタム絵文字リアクションを表示できるように
### 🐛Fixes
* リアクションカウントがおかしくなることがあるのを修正
12.30.0 (2020/4/11)
-------------------
### ✨Improvements
* リクエストライブラリをrequestからnode-fetchに変更
* オブジェクトストレージのhttpスキーマリクエストでもProxyが適用されるように
* DNSキャッシュとKeep-Alive適用箇所を増やす
* ドイツ語と中国語(繁体)を有効に
* NSFWを再度隠せるように
* Implement AiScript scratchpad (/scratchpad)
### 🐛Fixes
* APのurl処理の修正
12.29.0 (2020/4/5)
-------------------
### ✨Improvements
* トークン系の乱数ソースではcryptoを使うように
* broadcast stream が追加され emojiAdded イベントをサポート
* APIリファレンスの高速化等
* Ability to set header image for a Page
* ログの改善
### 🐛Fixes
* アプリ一覧に1回も使用していないアプリが表示されないのを修正
* admin/accounts/createで一般ユーザーがアカウントを作成し放題なのを修正
* 翻訳の未適用箇所を修正
* APIの権限設定漏れを修正
* インストール直後にアクティビティが飛んで来たりするともう初期管理者セットアップがができなくなるのを修正
* リモート投稿でurlがあればそちらをリンクするように修正
12.28.0 (2020/3/29)
-------------------
### ✨Improvements
* インストールされたアプリのページでアプリの権限を確認できるように
* API: api/meta.features.miauthを追加
MiAuthに対応しているかどうかを確認するために利用できます。
値はつねにtrueを取ります。
* インスタンス一覧でソートできるように
12.27.1 (2020/03/28)
-------------------
### ✨Improvements
* MiAuthのバグを修正
12.27.0 (2020/03/28)
-------------------
### ✨Improvements
* サードパーティーアプリケーションの認証方法にMiAuthを追加 ([Misskey API ドキュメント](https://github.com/syuilo/misskey/blob/b8088dc01a0c53b264c0697082ff5b16b06c4cda/src/docs/api.ja-JP.md#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%81%97%E3%81%A6%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B))
従来の、API `app/create` => `auth/session/generate` => `auth/session/userkey` を使用する方法は依然として使用可能です。
UIからアプリを作成する画面 (`/dev/apps`) は廃止されました、同等の操作を行いたい場合は API `app/create` で可能です。
MiAuthに対応しているかどうかは`api/meta.features.miauth`で確認できます12.28.0~)。
* テーマをインポートする前にプレビューできるように
* アプリから通知を作成できるように
* インストールしたアプリを見たり削除したりできるように
12.26.0 (2020/03/25)
-------------------
### ✨Improvements
* ロゴが新しく
* インスタンス設定の「ユーザー」が登録の逆順で表示されるように
### 🐛Fixes
* 新規登録フォームの「利用規約」のリンク色が通常の文字と同じだった問題を修正
* ダークモードの同期の問題を修正
12.25.0 (2020/03/24)
-------------------
### ✨Improvements
* テーマインポート機能を実装
### 🐛Fixes
* 誰もフォローしていないときにタイムラインの読み込みが遅い問題を修正
12.24.2 (2020/03/22)
-------------------
### 🐛Fixes
* ダークモードの同期を修正
12.24.1 (2020/03/22)
-------------------
### ✨Improvements
* SVG形式のアイコンファイルを追加
### 🐛Fixes
* iOSで起動できない問題を修正
* Pages画面にタイトルがない問題を修正
12.24.0 (2020/03/22)
-------------------
### ✨Improvements
* クライアント設定にアカウント設定へのリンクを追加
* ダークモードの同期を強化
### 🐛Fixes
* 画面が小さいとメニューがすべて見えない問題を修正
12.23.0 (2020/03/22)
-------------------

104
README.md
View File

@@ -109,100 +109,102 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
----------------------------------------------------------------
<!-- PATREON_START -->
<table><tr>
<td><img src="https://c8.patreon.com/2/200/20832595" alt="Roujo" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/20832595" alt="Roujo " width="100"></td>
<td><img src="https://c8.patreon.com/2/200/27956229" alt="Oliver Maximilian Seidel" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/605366/c9dc408fdcbf412fb183ca5b06235f8d/1.jpeg?token-time=2145916800&token-hash=oaqsjLqOFjWN5I9hm2epOaTXaEtKwQUy5OW-EpAz6-g%3D" alt="Jon Leibowitz" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/24430516/b1964ac5b9f746d2a12ff53dbc9aa40a/1.jpg?token-time=2145916800&token-hash=bmEiMGYpp3bS7hCCbymjGGsHBZM3AXuBOFO3Kro37PU%3D" alt="Eduardo Quiros" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/14215107/1cbe1912c26143919fa0faca16f12ce1/3.png?token-time=2145916800&token-hash=Zq1TCK2tdY7xudEm_aV70bc_wxmol6pNj3ZWbpFUNbI%3D" alt="Nesakko" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/14215107/1cbe1912c26143919fa0faca16f12ce1/3.png?token-time=2145916800&token-hash=Zq1TCK2tdY7xudEm_aV70bc_wxmol6pNj3ZWbpFUNbI%3D" alt="Nesakko " width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/user?u=20832595">Roujo</a></td>
<td><a href="https://www.patreon.com/user?u=20832595">Roujo </a></td>
<td><a href="https://www.patreon.com/user?u=27956229">Oliver Maximilian Seidel</a></td>
<td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
<td><a href="https://www.patreon.com/weepjp">weepjp </a></td>
<td><a href="https://www.patreon.com/jonleibowitz">Jon Leibowitz</a></td>
<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
<td><a href="https://www.patreon.com/user?u=19045173">kiritan </a></td>
<td><a href="https://www.patreon.com/user?u=24430516">Eduardo Quiros</a></td>
<td><a href="https://www.patreon.com/Nesakko">Nesakko</a></td>
<td><a href="https://www.patreon.com/Nesakko">Nesakko </a></td>
</tr></table>
<table><tr>
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/3075183/c2ae575c604e420297f000ccc396e395/1.jpeg?token-time=2145916800&token-hash=O9qmPtpo6wWb0OuvnkEekhk_1WO2MTdytLr7ZgsAr80%3D" alt="Liaizon Wakest" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/23915207/25428766ecd745478e600b3d7f871eb2/1.png?token-time=2145916800&token-hash=urCLLA4KjJZX92Y1CxcBP4d8bVTHGkiaPnQZp-Tqz68%3D" alt="kabo2468y" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/8249688/4aacf36b6b244ab1bc6653591b6640df/2.png?token-time=2145916800&token-hash=1ZEf2w6L34253cZXS_HlVevLEENWS9QqrnxGUAYblPo%3D" alt="AureoleArk" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/23915207/25428766ecd745478e600b3d7f871eb2/1.png?token-time=2145916800&token-hash=urCLLA4KjJZX92Y1CxcBP4d8bVTHGkiaPnQZp-Tqz68%3D" alt="kabo2468y " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/8249688/4aacf36b6b244ab1bc6653591b6640df/2.png?token-time=2145916800&token-hash=1ZEf2w6L34253cZXS_HlVevLEENWS9QqrnxGUAYblPo%3D" alt="AureoleArk " width="100"></td>
<td><img src="https://c8.patreon.com/2/200/21285325" alt="Nie(sha) " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon " width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61 " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5788159/af42076ab3354bb49803cfba65f94bee/1.jpg?token-time=2145916800&token-hash=iSaxp_Yr2-ZiU2YVi9rcpZZj9mj3UvNSMrZr4CU4qtA%3D" alt="mewl hayabusa" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
<td><a href="https://www.patreon.com/user?u=776209">Denshi </a></td>
<td><a href="https://www.patreon.com/wakest">Liaizon Wakest</a></td>
<td><a href="https://www.patreon.com/user?u=557245">mkatze</a></td>
<td><a href="https://www.patreon.com/user?u=23915207">kabo2468y</a></td>
<td><a href="https://www.patreon.com/AureoleArk">AureoleArk</a></td>
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
<td><a href="https://www.patreon.com/user?u=557245">mkatze </a></td>
<td><a href="https://www.patreon.com/user?u=23915207">kabo2468y </a></td>
<td><a href="https://www.patreon.com/AureoleArk">AureoleArk </a></td>
<td><a href="https://www.patreon.com/user?u=21285325">Nie(sha) </a></td>
<td><a href="https://www.patreon.com/osapon">osapon </a></td>
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ </a></td>
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61 </a></td>
<td><a href="https://www.patreon.com/hs_sh_net">mewl hayabusa</a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/28779508/3cd4cb7f017f4ee0864341e3464d42f9/1.png?token-time=2145916800&token-hash=eGQtR15be44kgvh8fw2Jx8Db4Bv15YBp2ldxh0EKRxA%3D" alt="S Y" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16542964" alt="Takumi Sugita" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/26340354/08834cf767b3449e93098ef73a434e2f/2.png?token-time=2145916800&token-hash=nyM8DnKRL8hR47HQ619mUzsqVRpkWZjgtgBU9RY15Uc%3D" alt="totokoro" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5827393/59893c191dda408f9cabd0f20a3a5627/1.jpeg?token-time=2145916800&token-hash=i9N05vOph-eP1LTLb9_npATjYOpntL0ZsHNaZFSsPmE%3D" alt="motcha" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20494440/540beaf2445f408ea6597bc61e077bb3/1.png?token-time=2145916800&token-hash=UJ0JQge64Bx9XmN_qYA1inMQhrWf4U91fqz7VAKJeSg%3D" alt="axtuki1" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/26340354/08834cf767b3449e93098ef73a434e2f/2.png?token-time=2145916800&token-hash=nyM8DnKRL8hR47HQ619mUzsqVRpkWZjgtgBU9RY15Uc%3D" alt="totokoro " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5827393/59893c191dda408f9cabd0f20a3a5627/1.jpeg?token-time=2145916800&token-hash=i9N05vOph-eP1LTLb9_npATjYOpntL0ZsHNaZFSsPmE%3D" alt="motcha " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20494440/540beaf2445f408ea6597bc61e077bb3/1.png?token-time=2145916800&token-hash=UJ0JQge64Bx9XmN_qYA1inMQhrWf4U91fqz7VAKJeSg%3D" alt="axtuki1 " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpg?token-time=2145916800&token-hash=nVAntpybQrznE0rg05keLrSE6ogPKJXB13rmrJng42c%3D" alt="takimura" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/user?u=28779508">S Y</a></td>
<td><a href="https://www.patreon.com/user?u=16542964">Takumi Sugita</a></td>
<td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin</a></td>
<td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin </a></td>
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
<td><a href="https://www.patreon.com/user?u=26340354">totokoro</a></td>
<td><a href="https://www.patreon.com/user?u=19356899">sheeta.s</a></td>
<td><a href="https://www.patreon.com/user?u=5827393">motcha</a></td>
<td><a href="https://www.patreon.com/user?u=20494440">axtuki1</a></td>
<td><a href="https://www.patreon.com/user?u=26340354">totokoro </a></td>
<td><a href="https://www.patreon.com/user?u=19356899">sheeta.s </a></td>
<td><a href="https://www.patreon.com/user?u=5827393">motcha </a></td>
<td><a href="https://www.patreon.com/user?u=20494440">axtuki1 </a></td>
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
<td><a href="https://www.patreon.com/takimura">takimura</a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpg?token-time=2145916800&token-hash=nVAntpybQrznE0rg05keLrSE6ogPKJXB13rmrJng42c%3D" alt="takimura " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13100201/fc5be4fa90444f09a9c8a06f72385272/1.png?token-time=2145916800&token-hash=i8PjlgfOB2LPEdbtWyx8ZPsBKhGcNZqcw_FQmH71UGU%3D" alt="aqz tamaina" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/28295158/cd2451bfb94a449dbf705ef4718cd355/2.jpeg?token-time=2145916800&token-hash=MRv3BxufHPuCyiBSxU5UYmLGvD6YZlhtSFRfMWg2k4U%3D" alt="012" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9109588/e3cffc48d20a4e43afe04123e696781d/3.png?token-time=2145916800&token-hash=T_VIUA0IFIbleZv4pIjiszZGnQonwn34sLCYFIhakBo%3D" alt="nafuchoco" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/28295158/cd2451bfb94a449dbf705ef4718cd355/2.jpeg?token-time=2145916800&token-hash=MRv3BxufHPuCyiBSxU5UYmLGvD6YZlhtSFRfMWg2k4U%3D" alt="012 " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9109588/e3cffc48d20a4e43afe04123e696781d/3.png?token-time=2145916800&token-hash=T_VIUA0IFIbleZv4pIjiszZGnQonwn34sLCYFIhakBo%3D" alt="nafuchoco " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/619ab87cc08448439222631ebb26802f/1.gif?token-time=2145916800&token-hash=o27K7M02s1z-LkDUEO5Oa7cu-GviRXeOXxryi4o_6VU%3D" alt="Atsuko Tominaga" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5923936/2a743cbfbff946c2af3f09026047c0da/2.png?token-time=2145916800&token-hash=h6yphW1qnM0n_NOWaf8qtszMRLXEwIxfk5beu4RxdT0%3D" alt="noellabo" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpg?token-time=2145916800&token-hash=7bkMqTwHPRsJPGAq42PYdDXDZBVGLqdgr1ZmBxX8GFQ%3D" alt="Hekovic" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5923936/2a743cbfbff946c2af3f09026047c0da/2.png?token-time=2145916800&token-hash=h6yphW1qnM0n_NOWaf8qtszMRLXEwIxfk5beu4RxdT0%3D" alt="noellabo " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpg?token-time=2145916800&token-hash=7bkMqTwHPRsJPGAq42PYdDXDZBVGLqdgr1ZmBxX8GFQ%3D" alt="Hekovic " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/24641572/b4fd175424814f15b0ca9178d2d2d2e4/1.png?token-time=2145916800&token-hash=e2fyqdbuJbpCckHcwux7rbuW6OPkKdERcus0u2wIEWU%3D" alt="uroco @99" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/takimura">takimura </a></td>
<td><a href="https://www.patreon.com/aqz">aqz tamaina</a></td>
<td><a href="https://www.patreon.com/user?u=28295158">012</a></td>
<td><a href="https://www.patreon.com/nijimiss">nafuchoco</a></td>
<td><a href="https://www.patreon.com/user?u=28295158">012 </a></td>
<td><a href="https://www.patreon.com/nijimiss">nafuchoco </a></td>
<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
<td><a href="https://www.patreon.com/noellabo">noellabo</a></td>
<td><a href="https://www.patreon.com/Corset">CG</a></td>
<td><a href="https://www.patreon.com/hekovic">Hekovic</a></td>
<td><a href="https://www.patreon.com/user?u=4389829">natalie </a></td>
<td><a href="https://www.patreon.com/noellabo">noellabo </a></td>
<td><a href="https://www.patreon.com/Corset">CG </a></td>
<td><a href="https://www.patreon.com/hekovic">Hekovic </a></td>
<td><a href="https://www.patreon.com/user?u=24641572">uroco @99</a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1.png?token-time=2145916800&token-hash=hBayGfOmQH3kRMdNnDe4oCZD_9fsJWSt29xXR3KRMVk%3D" alt="Nokotaro Takeda" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/23932002" alt="nenohi" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9481273/7fa89168e72943859c3d3c96e424ed31/4.jpeg?token-time=2145916800&token-hash=5w1QV1qXe-NdWbdFmp1H7O_-QBsSiV0haumk3XTHIEg%3D" alt="Efertone" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/23932002" alt="nenohi " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9481273/7fa89168e72943859c3d3c96e424ed31/4.jpeg?token-time=2145916800&token-hash=5w1QV1qXe-NdWbdFmp1H7O_-QBsSiV0haumk3XTHIEg%3D" alt="Efertone " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1.jpeg?token-time=2145916800&token-hash=vGe7wXGqmA8Q7m-kDNb6fyGdwk-Dxk4F-ut8ZZu51RM%3D" alt="Takashi Shibuya" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
<td><a href="https://www.patreon.com/user?u=23932002">nenohi</a></td>
<td><a href="https://www.patreon.com/efertone">Efertone</a></td>
<td><a href="https://www.patreon.com/user?u=23932002">nenohi </a></td>
<td><a href="https://www.patreon.com/efertone">Efertone </a></td>
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table>
**Last updated:** Tue, 17 Mar 2020 18:57:08 UTC
**Last updated:** Sun, 19 Apr 2020 11:51:06 UTC
<!-- PATREON_END -->
[backer-url]: #backers

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

View File

@@ -1,20 +1,27 @@
<?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 512 512" 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;">
<path d="M416,199.998C416,177.908 398.092,160 376.002,160L312.038,160C301.419,160 291.235,164.218 283.727,171.727C276.218,179.235 272,189.419 272,200.038L272,228C272,234.627 277.373,240 284,240L376.002,240C398.092,240 416,222.092 416,200.002L416,199.998Z" style="fill:url(#_Linear1);"/>
<g transform="matrix(6.12323e-17,1,-1,6.12323e-17,512,-4.54747e-13)">
<path d="M416,199.998C416,177.908 398.092,160 376.002,160L312.038,160C301.419,160 291.235,164.218 283.727,171.727C276.218,179.235 272,189.419 272,200.038L272,228C272,234.627 277.373,240 284,240L376.002,240C398.092,240 416,222.092 416,200.002L416,199.998Z" style="fill:url(#_Linear2);"/>
<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(-1,1.22465e-16,-1.22465e-16,-1,512,512)">
<path d="M416,199.998C416,177.908 398.092,160 376.002,160L312.038,160C301.419,160 291.235,164.218 283.727,171.727C276.218,179.235 272,189.419 272,200.038L272,228C272,234.627 277.373,240 284,240L376.002,240C398.092,240 416,222.092 416,200.002L416,199.998Z" style="fill:url(#_Linear3);"/>
<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>
<g transform="matrix(6.12323e-17,-1,1,6.12323e-17,0,512)">
<path d="M416,199.998C416,177.908 398.092,160 376.002,160L312.038,160C301.419,160 291.235,164.218 283.727,171.727C276.218,179.235 272,189.419 272,200.038L272,228C272,234.627 277.373,240 284,240L376.002,240C398.092,240 416,222.092 416,200.002L416,199.998Z" style="fill:url(#_Linear4);"/>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(144,0,0,80,272,200)"><stop offset="0" style="stop-color:rgb(198,230,111);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(90,200,113);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(144,0,0,80,272,200)"><stop offset="0" style="stop-color:rgb(97,232,195);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(90,200,113);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(144,0,0,80,272,200)"><stop offset="0" style="stop-color:rgb(198,230,111);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(90,200,113);stop-opacity:1"/></linearGradient>
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(144,0,0,80,272,200)"><stop offset="0" style="stop-color:rgb(98,232,195);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(90,200,113);stop-opacity:1"/></linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,11 +1,12 @@
---
_lang_: "Deutsch"
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"
notifications: "Benachrichtigungen"
username: "Benutzername"
password: "Passwort"
fetchingAsApObject: "Aus Fediverse holen"
fetchingAsApObject: "Wird aus dem Fediverse angefragt..."
ok: "OK"
gotIt: "Verstanden!"
cancel: "Abbrechen"
@@ -16,17 +17,17 @@ noNotifications: "Keine Benachrichtigungen"
instance: "Instanz"
settings: "Einstellungen"
profile: "Profil"
timeline: "Zeitleiste"
noAccountDescription: "Keine Selbsteinführung"
timeline: "Chronik"
noAccountDescription: "Dieser Nutzer hat seine Profilbeschreibung noch nicht ausgefüllt."
login: "Einloggen"
loggingIn: "Einloggen in bearbeitung"
loggingIn: "Du wirst eingeloggt..."
logout: "Ausloggen"
signup: "Registrieren"
uploading: "Upload läuft"
save: "Speichern"
users: "Benutzer"
addUser: "Benutzer hinzufügen"
favorite: "Favoriten"
favorite: "Zu Favoriten hinzufügen"
favorites: "Favoriten"
unfavorite: "Aus Favoriten entfernen"
pin: "Anheften"
@@ -34,14 +35,16 @@ unpin: "Lösen"
copyContent: "Inhalt kopieren"
copyLink: "Link kopieren"
delete: "Löschen"
deleteAndEdit: "Löschen und Bearbeiten"
deleteAndEditConfirm: "Möchtest du diese Notiz wirklich löschen und bearbeiten? Alle Reaktionen, Renotes und Antworten dieser Notiz werden verloren gehen."
addToList: "Zur Liste hinzufügen"
sendMessage: "Nachricht senden"
copyUsername: "Benutzernamen kopieren"
reply: "Antworten"
loadMore: "Zeige mehr"
youGotNewFollower: "Sie haben einen neuen Follower"
receiveFollowRequest: "Follow Request erhalten."
followRequestAccepted: "FollowRequestAkzeptiert"
loadMore: "Mehr anzeigen"
youGotNewFollower: "Du hast einen neuen Follower"
receiveFollowRequest: "Follow-Anfrage erhalten."
followRequestAccepted: "Follow-Anfrage akzeptiert"
mentions: "Erwähnungen"
directNotes: "Direktnachrichten"
importAndExport: "Importieren und Exportieren"
@@ -51,37 +54,39 @@ files: "Dateien"
download: "Download"
driveFileDeleteConfirm: "Möchtest du die Datei \"{name}\" löschen? Die zugehörige Notiz wird ebenso verschwinden."
unfollowConfirm: "Möchtest du {name} nicht mehr folgen?"
exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt."
importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen."
lists: "Listen"
noLists: "Keine Liste!"
noLists: "Du hast keine Listen"
note: "Notiz"
notes: "Notizen"
following: "Folgen"
followers: "Folgende"
followsYou: "Folgt dir"
createList: "Liste erstellen"
manageLists: "Liste verwalten"
manageLists: "Listen verwalten"
error: "Ein Problem ist aufgetreten"
retry: "Wiederholen"
enterListName: "Listennamen eingeben"
privacy: "Privatsphäre"
makeFollowManuallyApprove: "Folgeanfragen benötigen Bestätigung"
defaultNoteVisibility: "Die Standardsichtbarkeit"
makeFollowManuallyApprove: "Follow-Anfragen benötigen Bestätigung"
defaultNoteVisibility: "Standardsichtbarkeit"
follow: "Folgen"
followRequest: "Follower-Anfragen"
followRequests: "Follower-Anfragen"
followRequest: "Follow-Anfrage"
followRequests: "Follow-Anfragen"
unfollow: "Nicht mehr folgen"
followRequestPending: "Ausstehend"
followRequestPending: "Ausstehende Follow-Anfrage"
enterEmoji: "Gib ein Emoji ein"
renote: "Renote"
unrenote: "Renote zurücknehmen"
quote: "Zitieren"
pinnedNote: "Angepinnte Notiz"
you: "Du"
clickToShow: "Klicke zum den Inhalt anzusehen"
clickToShow: "Klicke um diesen Inhalt anzusehen"
sensitive: "Dieser Inhalt ist NSFW"
add: "Hinzufügen"
reaction: "Reaktionen"
reactionSettingDescription: "Weisen Sie Ihre lieblings reaktionen zu, die Sie in den Reaktionenswähler stecken möchten."
reactionSettingDescription: "Gib deine Lieblingsreaktionen ein, um sie der Reaktionsauswahl hinzuzufügen."
rememberNoteVisibility: "Notizsichtbarkeit merken"
attachCancel: "Anhängen abbrechen"
markAsSensitive: "Als sensitiv markieren"
@@ -93,40 +98,54 @@ block: "Blockieren"
unblock: "Blockierung aufheben"
suspend: "Sperren"
unsuspend: "Sperrung aufheben"
blockConfirm: "Möchtest du diesen Account wirklich blockieren?"
blockConfirm: "Möchtest du diesen Benutzer wirklich blockieren?"
unblockConfirm: "Möchtest du diese Blockierung wirklich aufheben?"
suspendConfirm: "Möchtest du diesen Account wirklich sperren?"
unsuspendConfirm: "Möchtest du die Sperrung dieses Accounts wirklich aufheben?"
suspendConfirm: "Möchtest du diesen Benutzer wirklich sperren?"
unsuspendConfirm: "Möchtest du die Sperrung dieses Benutzers wirklich aufheben?"
selectList: "Wähle eine Liste aus"
customEmojis: "Benutzerdefinierte Emojis"
emojiName: "Emojiname"
emojiUrl: "Emoji-URL"
addEmoji: "Emoji hinzufügen"
cacheRemoteFiles: "Dateien von anderen Instanzen im Cache speichern"
cacheRemoteFilesDescription: "Wenn diese Einstellung deaktiviert ist, werden Dateien anderer Instanzen direkt von dort geladen. Hierdurch wird Speicherplatz gespart, aber mehr Bandbreite verbraucht, da keine Vorschaubilder generiert werden."
flagAsBot: "Als Bot markieren"
flagAsCat: "Als Katze markieren"
autoAcceptFollowed: "Folgeanfragen automatisch akzeptieren"
autoAcceptFollowed: "Follow-Anfragen automatisch akzeptieren"
addAcount: "Benutzerkonto hinzufügen"
loginFailed: "Login fehlgeschlagen"
showOnRemote: "Auf Ursprungsinstanz ansehen"
general: "Allgemein"
wallpaper: "Hintergrund"
setWallpaper: "Hintergrund festlegen"
removeWallpaper: "Hintergrund entfernen"
searchWith: "Suche: {q}"
youHaveNoLists: "Du hast keine Listen"
followConfirm: "Möchtest du {name} wirklich folgen?"
proxyAccount: "Proxy-Benutzerkonto"
proxyAccountDescription: "Ein Proxy-Benutzerkonto ist ein Benutzerkonto, das sich für Nutzer unter bestimmten Konditionen wie ein Follower aus einer fremden Instanz verhält. Zum Beispiel wird die Aktivität eines Nutzers aus einer fremden Instanz nicht an diese Instanz übermittelt, falls es keinen Benutzer dieser Instanz gibt, der diesem Nutzer aus fremder Instanz folgt. In diesem Fall folgt stattdessen das Proxy-Benutzerkonto."
host: "Host"
selectUser: "Benutzer wählen"
recipient: "Empfänger"
annotation: "Anmerkung"
federation: "Föderation"
instances: "Instanz"
registeredAt: "Registriert am"
latestRequestSentAt: "Letzte Anfrage gesendet am"
latestRequestReceivedAt: "Letzte Anfrage erhalten am"
latestStatus: "Neuester Status"
storageUsage: "Speicherplatzverbrauch"
charts: "Charts"
charts: "Diagramme"
perHour: "Pro Stunde"
perDay: "Pro Tag"
stopActivityDelivery: "Senden von Aktivitäten einstellen"
blockThisInstance: "Diese Instanz blockieren"
operations: "Aktionen"
software: "Software"
version: "Version"
metadata: "Metadaten"
withNFiles: "{n} Datei(en)"
monitor: "Beobachten"
jobQueue: "Job-Warteschlange"
cpuAndMemory: "CPU und Arbeitsspeicher"
network: "Netzwerk"
@@ -137,6 +156,7 @@ clearQueue: "Warteschlange leeren"
clearQueueConfirmTitle: "Möchtest du die Warteschlange wirklich leeren?"
clearQueueConfirmText: "Jegliche Notizen, die sich noch in der Warteschlange befinden, werden hierdurch nicht föderiert. Diese Aktion wird normalerweise NICHT benötigt."
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"
@@ -152,11 +172,18 @@ processing: "In Bearbeitung"
preview: "Vorschau"
default: "Standard"
noCustomEmojis: "Es existieren keine Emojis"
customEmojisOfRemote: "Emojis von anderen Instanzen"
noJobs: "Es gibt keine Jobs"
federating: "Föderiert"
blocked: "Blockiert"
suspended: "Gesperrt"
all: "Alles"
subscribing: "Abonnieren"
publishing: "Veröffentlichen"
notResponding: "Antwortet nicht"
instanceFollowing: "Gefolgt auf der Instanz"
instanceFollowers: "Follower der Instanz"
instanceUsers: "Benutzer dieser Instanz"
changePassword: "Passwort ändern"
security: "Sicherheit"
retypedNotMatch: "Eingaben stimmen nicht überein."
@@ -168,44 +195,65 @@ more: "Mehr!"
featured: "Hervorgehoben"
usernameOrUserId: "Benutzername oder Benutzer-ID"
noSuchUser: "Benutzer nicht gefunden"
lookup: "Abfragen"
announcements: "Ankündigungen"
imageUrl: "Bild-URL"
remove: "Löschen"
removed: "Erfolgreich gelöscht"
removeAreYouSure: "Möchtest du \"{x}\" wirklich löschen?"
saved: "Gespeichert"
messaging: "Nachrichten"
messaging: "Privatnachrichten"
upload: "Hochladen"
fromDrive: "Aus Drive"
fromUrl: "Von einer URL"
uploadFromUrl: "Von einer URL hochladen"
uploadFromUrlDescription: "URL der hochzuladenden Datei"
uploadFromUrlRequested: "Upload angefordert"
uploadFromUrlMayTakeTime: "Es kann eine Weile dauern, bis der Upload abgeschlossen ist."
explore: "Erkunden"
games: "Misskey Spiele"
messageRead: "Gelesen"
noMoreHistory: "Kein weiterer Verlauf vorhanden"
startMessaging: "Neue Privatnachricht erstellen"
nUsersRead: "Von {n} gelesen"
agreeTo: "Ich stimme {0} zu"
tos: "Nutzungsbedingungen"
start: "Anfangen"
home: "Startseite"
remoteUserCaution: "Diese Informationen sind möglicherweise veraltet, da der Benutzer von einer anderen Instanz stammt."
activity: "Aktivität"
images: "Bilder"
birthday: "Geburtstag"
yearsOld: "{age} Jahre alt"
registeredDate: "Registierdatum"
registeredDate: "Registrationsdatum"
location: "Ort"
theme: "Farbthemen"
themeForLightMode: "Farbthema, das im Hellmodus genutzt wird"
themeForDarkMode: "Farbthema, das im Dunkelmodus genutzt wird"
light: "Hell"
dark: "Dunkel"
lightThemes: "Helle Farbthemen"
darkThemes: "Dunkle Farbthemen"
syncDeviceDarkMode: "Dunkelmodus mit den Einstellungen deines Gerätes synchronisieren"
drive: "Drive"
fileName: "Dateiname"
selectFile: "Datei auswählen"
selectFiles: "Dateien auswählen"
selectFolder: "Wähle einen Ordner"
selectFolders: "Wähle Ordner"
renameFile: "Datei umbenennen"
folderName: "Ordnername"
createFolder: "Ordner erstellen"
renameFolder: "Ordner umbenennen"
deleteFolder: "Ordner löschen"
addFile: "Datei hinzufügen"
emptyDrive: "Drive ist leer"
emptyFolder: "Der Ordner ist leer"
unableToDelete: "Nicht löschbar"
inputNewFileName: "Gib einen neuen Dateinamen ein"
inputNewFolderName: "Gib einen neuen Ordnernamen ein"
circularReferenceFolder: "Der Zielordner ist ein Unterorder des Ordners, den du verschieben möchtest."
hasChildFilesOrFolders: "Dieser Ordner kann nicht gelöscht werden, da er nicht leer ist."
copyUrl: "URL kopieren"
rename: "Umbenennen"
avatar: "Profilbild"
@@ -214,26 +262,37 @@ nsfw: "Dieser Inhalt ist NSFW"
disconnectedFromServer: "Verbindung zum Server wurde getrennt"
reload: "Aktualisieren"
doNothing: "Ignorieren"
reloadConfirm: "Möchtest du die Chronik aktualisieren?"
watch: "Beobachten"
unwatch: "Nicht mehr beobachten"
accept: "Akzeptieren"
reject: "Ablehnen"
normal: "Normal"
instanceName: "Name der Instanz"
instanceDescription: "Beschreibung der Instanz"
maintainerName: "Betreiber"
maintainerEmail: "Betreiberemail"
maintainerEmail: "Betreiber-Email"
tosUrl: "URL der Nutzungsbedingungen"
thisYear: "Dieses Jahr"
thisMonth: "Dieser Monat"
today: "Heute"
dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Seiten"
integration: "Integration"
connectSerice: "Verbinden"
disconnectSerice: "Trennen"
enableLocalTimeline: "Lokale Chronik aktivieren"
enableGlobalTimeline: "Globale Chronik aktivieren"
disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle Chroniken, auch wenn diese deaktiviert sind."
registration: "Registrieren"
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"
driveCapacityPerRemoteAccount: "Drive-Kapazität pro Benutzer anderer Instanzen"
inMb: "In Megabytes"
iconUrl: "Icon-URL"
bannerUrl: "Banner-URL"
@@ -242,6 +301,8 @@ pinnedUsers: "Angepinnte Benutzer"
pinnedUsersDescription: "Gib einen Benutzernamen pro Zeile ein. Diese werden im \"Erkunden\" Tab angezeigt."
recaptcha: "reCAPTCHA"
enableRecaptcha: "reCAPTCHA aktivieren"
recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret key"
antennas: "Antennen"
manageAntennas: "Antennen verwalten"
name: "Name"
@@ -249,6 +310,8 @@ antennaSource: "Antennenquelle"
antennaKeywords: "Schlüsselwörter, die beobachtet werden sollen"
antennaExcludeKeywords: "Schlüsselwörter, die ignoriert werden sollen"
antennaKeywordsDescription: "Mit Leerzeichen für eine \"UND\"-Verknüpfung trennen, durch Zeilenumbrüche für eine \"ODER\"-Verknüpfung trennen."
notifyAntenna: "Über neue Notizen benachrichtigen"
withFileAntenna: "Nur Notizen mit Dateien"
serviceworker: "ServiceWorker"
enableServiceworker: "ServiceWorker aktivieren"
antennaUsersDescription: "Benutzernamen getrennt durch Zeilenumbrüche angeben"
@@ -256,20 +319,43 @@ caseSensitive: "Groß-/Kleinschreibung unterscheiden"
withReplies: "Antworten beinhalten"
connectedTo: "Mit folgenden Benutzerkonten verknüpft"
notesAndReplies: "Notizen und Antworten"
withFiles: "Dateien beinhalten"
withFiles: "Notizen mit Dateien"
silence: "Instanzweit stummschalten"
silenceConfirm: "Möchtest du diesen Benutzer wirklich instanzweit stummschalten?"
unsilence: "Instanzweite Stummschaltung aufheben"
unsilenceConfirm: "Möchtest du die instanzweite Stummschaltung dieses Benutzers wirklich aufheben?"
popularUsers: "Beliebte Benutzer"
recentlyUpdatedUsers: "Vor kurzem aktive Benutzer"
recentlyRegisteredUsers: "Vor kurzem registrierte Benutzer"
recentlyDiscoveredUsers: "Vor kurzem gefundene Benutzer"
exploreUsersCount: "Es gibt {count} Benutzer"
exploreFediverse: "Das Fediverse erkunden"
popularTags: "Beliebte Schlagwörter"
userList: "Listen"
about: "Über"
aboutMisskey: "Über Misskey"
aboutMisskeyText: "Misskey ist Open-Source-Software die von syuilo seit 2014 entwickelt wird."
misskeyMembers: "Misskey wird momentan von den unten aufgelisteten Mitgliedern weiterentwickelt und instand gehalten:"
misskeySource: "Der Quelltext ist hier verfügbar:"
misskeyTranslation: "Hilf dabei, Misskey zu übersetzen:"
misskeyDonate: "Spende an Misskey, um die Weiterentwicklung zu unterstützen:"
morePatrons: "Wir schätzen ebenso die Unterstützung vieler anderer hier nicht gelisteter Personen sehr. Danke! 🥰"
patrons: "UnterstützerInnen"
administrator: "Administrator"
token: "Token"
twoStepAuthentication: "Zwei-Faktor-Authentifizierung"
moderator: "Moderator"
nUsersMentioned: "{n} Benutzer reden darüber"
securityKey: "Sicherheitsschlüssel"
securityKeyName: "Schlüsselname"
registerSecurityKey: "Sicherheitsschlüssel registrieren"
lastUsed: "Zuletzt benutzt"
unregister: "Deaktivieren"
passwordLessLogin: "Passwortloses Anmelden einrichten"
resetPassword: "Passwort zurücksetzen"
newPasswordIs: "Das neue Passwort ist \"{password}\""
posted: "Gesendet"
autoReloadWhenDisconnected: "Automatisch aktualisieren wenn die Serververbindung getrennt wird"
autoNoteWatch: "Notiz automatisch beobachten"
autoNoteWatch: "Notizen automatisch beobachten"
autoNoteWatchDescription: "Werde über Notizen, auf die du reagiert oder geantwortet hast, informiert"
reduceUiAnimation: "Animationen der Benutzeroberfläche reduzieren"
share: "Teilen"
@@ -279,7 +365,21 @@ uploadFolder: "Standardordner für Uploads"
cacheClear: "Cache leeren"
markAsReadAllNotifications: "Alle Benachrichtigungen als gelesen markieren"
markAsReadAllUnreadNotes: "Alle Notizen als gelesen markieren"
invites: "Einladen"
markAsReadAllTalkMessages: "Alle Nachrichten als gelesen markieren"
help: "Hilfe"
inputMessageHere: "Hier Nachricht eingeben"
close: "Schließen"
group: "Gruppe"
groups: "Gruppen"
createGroup: "Gruppe erstellen"
ownedGroups: "Eigene Gruppen"
joinedGroups: "Beigetretene Gruppen"
invites: "Einladungen"
groupName: "Gruppenname"
members: "Mitglieder"
transfer: "Übertragen"
messagingWithUser: "Privatnachrichten mit einem Benutzer"
messagingWithGroup: "Privatnachrichten mit einer Gruppe"
title: "Betreff"
text: "Text"
enable: "Aktivieren"
@@ -288,12 +388,40 @@ retype: "Erneut eingeben"
noteOf: "Notiz von {user}"
inviteToGroup: "Zu Gruppe einladen"
maxNoteTextLength: "Maximale Länge von Notizen"
quoteAttached: "Zitiert"
quoteQuestion: "Als Zitat anfügen?"
noMessagesYet: "Noch keine Nachrichten"
newMessageExists: "Du hast eine neue Nachricht"
onlyOneFileCanBeAttached: "Es kann pro Nachricht nur eine Datei angehängt werden"
signinRequired: "Anmeldung erforderlich"
invitationCode: "Einladungscode"
checking: "Wird überprüft..."
available: "Verfügbar"
unavailable: "Unverfügbar"
usernameInvalidFormat: "Buchstaben, Zahlen und Unterstriche sind verwendbar."
tooShort: "Zu kurz"
tooLong: "Zu lang"
weakPassword: "Schwaches Passwort"
normalPassword: "Standardpasswort"
strongPassword: "Starkes Passwort"
passwordMatched: "Stimmt überein"
passwordNotMatched: "Stimmt nicht überein"
signinWith: "Mit {x} anmelden"
signinFailed: "Anmeldung fehlgeschlagen. Überprüfe Benutzername und Passswort."
tapSecurityKey: "Tippe deinen Sicherheitsschlüssel an"
or: "Oder"
uiLanguage: "Sprache der Benutzeroberfläche"
groupInvited: "Du wurdest in eine Gruppe eingeladen"
aboutX: "Über {x}"
useOsNativeEmojis: "Eingebaute Emojis des Betriebssystems benutzen"
youHaveNoGroups: "Keine Gruppen vorhanden"
joinOrCreateGroup: "Lass dich zu einer Gruppe einladen oder erstelle deine eigene."
noHistory: "Kein Verlauf"
disableAnimatedMfm: "MFM, die Animationen enthalten, deaktivieren"
doing: "In Bearbeitung"
category: "Kategorie"
tags: "Schlagwörter"
docSource: "Quelle dieses Dokuments"
createAccount: "Benutzerkonto erstellen"
existingAcount: "Bestehendes Benutzerkonto"
regenerate: "Regenerieren"
@@ -302,29 +430,81 @@ noFollowRequests: "Du hast keine Follow-Anfragen"
openImageInNewTab: "Bilder in neuem Tab öffnen"
dashboard: "Dashboard"
local: "Lokal"
remote: "Fremd"
total: "Gesamt"
weekOverWeekChanges: "Wöchentlich"
dayOverDayChanges: "Täglich"
accessibility: "Barrierefreiheit"
clinetSettings: "Client-Einstellungen"
accountSettings: "Benutzerkontoeinstellungen"
accountSettings: "Benutzerkonto-Einstellungen"
promotion: "Hervorgehoben"
promote: "Hervorheben"
numberOfDays: "Anzahl der Tage"
hideThisNote: "Diese Notiz verstecken"
showFeaturedNotesInTimeline: "Beliebte Notizen in Chronik anzeigen"
objectStorage: "Objektspeicher"
useObjectStorage: "Objektspeicher verwenden"
objectStorageBaseUrl: "Basis-URL"
objectStorageBaseUrlDesc: "URL-Prefix, der zum Konstruieren der Objekt- bzw. Mediareferenz-URL genutzt wird. Falls du ein CDN- oder einen Proxy nutzt, gib dessen URL ein. Ansonsten gib die Adresse, der dir von deinem Anbieter z.B. in dessen Servicehandbuch gegeben wurde, an. Beispielsweise 'https://<bucket>.s3.amazonaws.com' für AWS S3 oder 'https://storage.googleapis.com/<bucket>' für GCS."
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Bitte gib den Bucket-Namen an, der bei deinem Anbieter verwendet wird."
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "Dateien werden im Ordner dieses Prefixes gespeichert."
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Dieses Feld leerlassen, falls du AWS S3 verwendest. Ansonsten trage den Endpoint im Format \"<host>\" oder \"<host>:<port>\" an, den Angaben deines Anbieters entsprechend."
objectStorageRegion: "Region"
objectStorageRegionDesc: "Gib eine Region (wie z.B. \"xx-east-1\") an. Falls dein Anbieter nicht zwischen Regionen unterscheidet, lass dieses Feld leer oder gib \"us-east-1\" an."
objectStorageUseSSL: "SSL verwenden"
objectStorageUseSSLDesc: "Deaktiviere dies falls du für die API-Verbindungen kein HTTPS verwenden wirst"
objectStorageUseProxy: "Über Proxy verbinden"
objectStorageUseProxyDesc: "Deaktiviere dies falls du keinen Proxy für den Objektspeicher verwenden wirst"
serverLogs: "Serverprotokolle"
deleteAll: "Alle löschen"
showFixedPostForm: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik anzeigen"
newNoteRecived: "Es gibt neue Notizen"
sounds: "Töne"
listen: "Anhören"
none: "Keine"
volume: "Lautstärke"
details: "Details"
chooseEmoji: "Wähle ein Emoji"
unableToProcess: "Der Vorgang konnte nicht abgeschlossen werden."
recentUsed: "Vor kurzem verwendet"
install: "Installieren"
uninstall: "Uninstallieren"
installedApps: "Authorisierte Anwendungen"
nothing: "Hier gibt es nichts zu sehen"
installedDate: "Authorisiert"
lastUsedDate: "Zuletzt verwendet"
state: "Status"
sort: "Sortieren"
ascendingOrder: "Aufsteigende Reihenfolge"
descendingOrder: "Absteigende Reihenfolge"
scratchpad: "Testumgebung"
scratchpadDescription: "Die Testumgebung bietet eine experimentale Umgebung für AiScript. Dort kannst du AiScript schreiben, ausführen sowie dessen Auswirkungen auf Misskey überprüfen."
output: "Ausgabe"
script: "Skript"
disablePagesScript: "AiScript auf Seiten deaktivieren"
updateRemoteUser: "Informationen über den Benutzer der fremder Instanz aktualisieren"
deleteAllFiles: "Alle Dateien löschen"
deleteAllFilesConfirm: "Möchtest du wirklich alle Dateien löschen?"
removeAllFollowing: "Allen gefolgten Benutzern entfolgen"
removeAllFollowingDescription: "Allen Benutzerkonten von {host} entfolgen. Bitte führe dies durch, falls diese Instanz nicht mehr existiert."
userSuspended: "Dieser Benutzer wurde gesperrt."
userSilenced: "Dieser Benutzer wurde instanzweit stummgeschaltet."
_theme:
explore: "Themen erforschen"
install: "Thema installieren"
manage: "Themaverwaltung"
code: "Themencode"
installed: "{name} wurde installiert"
alreadyInstalled: "Dieses Thema ist bereits installiert"
invalid: "Themenformat ist ungültig"
_sfx:
note: "Notizen"
noteMy: "Meine Notizen"
notification: "Benachrichtigungen"
chat: "Nachrichten"
chat: "Privatnachrichten"
chatBg: "Nachrichten (Hintergrund)"
antenna: "Antennen"
_ago:
@@ -342,11 +522,79 @@ _time:
second: "Sekunde"
minute: "Minute"
hour: "Stunde"
day: "t"
_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."
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?"
step3_2: "Der nächste Schritt ist das Schreiben einer Notiz. Dies kannst du tun, indem du auf das Stift-Icon auf dem Bildschirm drückst."
step3_3: "Fülle das Fenster aus und drücke auf den Knopf oben rechts zum Senden."
step3_4: "Gibt es nichts, das du momentan sagen möchtest? Versuch's mit \"Hallo Misskey!\""
step4_1: "Fertig mit dem Senden deiner ersten Notiz?"
step4_2: "Falls deine Notiz nun auf deiner Chronik auftaucht, hast du alles richtig gemacht."
step5_1: "Lass uns nun deiner Chronik etwas mehr Leben einhauchen, indem du einigen anderen Benutzern folgst."
step5_2: "{featured} zeigt dir beliebte Notizen dieser Instanz. In {explore} kannst du beliebte Benutzer finden. Schau dort, ob du Benutzer findest, die dich interessieren."
step5_3: "Um anderen Benutzern zu folgen, klicke auf ihr Profilbild und klicke dann auf den \"Folgen\" Knopf in ihrem Profil."
step5_4: "Wenn der Benutzer neben seinem Namen ein Schloss hat, dann muss er deine Follow-Anfrage manuell bestätigen."
step6_1: "Wenn du nun auch die Notizen anderer Benutzer auf deiner Chronik siehst, hast du alles richtig gemacht."
step6_2: "Du kannst ebenso \"Reaktionen\" verwenden, um schnell auf Notizen anderer Benutzer zu antworten."
step6_3: "Um eine \"Reaktion\" anzufügen, klicke auf das \"+\"-Symbol in der Notiz eines anderen Benutzers und wähle ein Emoji, mit dem du reagieren möchtest."
step7_1: "Glückwunsch! Du hast die Misskey-Einführung abgeschlossen."
step7_2: "Wenn du mehr über Misskey lernen möchtest, schau dich im {help}-Bereich um."
step7_3: "Und nun, viel Spaß mit Misskey! 🚀"
_2fa:
alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert"
registerDevice: "Neues Gerät registrieren"
registerKey: "Neuen Sicherheitsschlüssel registrieren"
step1: "Als Erstes, installiere eine Authentifizierungsapp (z.B. {a} oder {b}) auf deinem Gerät."
step2: "Dann, scanne den angezeigten QR-Code mit deinem Gerät."
step3: "Um die Einrichtung abzuschließen, gib den Token ein, der von deiner Authentifizierungsapp angezeigt wird."
step4: "Ab jetzt benötigen alle Loginversuche auch einen Login-Token."
securityKeyInfo: "Du kannst neben Fingerabdruck- oder PIN-Authentifizierung auf deinem Gerät auch Authentifizierung mit FIDO2-kompatiblen Hardware-Sicherheitsschlüsseln einrichten."
_permissions:
"read:messaging": "Nachrichten lesen"
"write:messaging": "Nachrichten schicken oder löschen"
"read:reactions": "Reaktionen sehen"
"read:account": "Deine Benutzerkontoinformationen lesen"
"write:account": "Deine Benutzerkontoinformationen bearbeiten"
"read:blocks": "Die Liste deiner blockierten Benutzer lesen"
"write:blocks": "Die Liste deiner blockierten Benutzer bearbeiten"
"read:drive": "Deine Drive-Dateien und Ordner lesen"
"write:drive": "Deine Drive-Dateien und Ordner bearbeiten oder löschen"
"read:favorites": "Deine Favoriten-Liste lesen"
"write:favorites": "Deine Favoriten-Liste bearbeiten"
"read:following": "Deine Follower-Liste lesen"
"write:following": "Anderen Benutzern folgen oder entfolgen"
"read:messaging": "Privatnachrichten lesen"
"write:messaging": "Privatnachrichten schicken oder löschen"
"read:mutes": "Stummschaltungen lesen"
"write:mutes": "Stummschaltungen bearbeiten"
"write:notes": "Notizen schreiben oder löschen"
"read:notifications": "Benachrichtigungen lesen"
"write:notifications": "Mit Benachrichtigungen arbeiten"
"read:reactions": "Reaktionen lesen"
"write:reactions": "Reaktionen hinzufügen und bearbeiten"
"write:votes": "In Umfragen abstimmen"
"read:pages": "Deine Seiten lesen"
"write:pages": "Deine Seiten bearbeiten oder löschen"
"read:page-likes": "Liste der Seiten, die mir gefallen, lesen"
"write:page-likes": "Liste der Seiten, die mir gefallen, bearbeiten"
"read:user-groups": "Benutzergruppen lesen"
"write:user-groups": "Benutzergruppen bearbeiten oder löschen"
_auth:
shareAccess: "Möchtest du \"{name}\" authorisieren, auf dieses Benuzerkonto zugreifen zu können?"
shareAccessAsk: "Bist du dir sicher, dass du diese Anwendung authorisieren möchtest, auf dein Benutzerkonto zugreifen zu können?"
permissionAsk: "Diese Anwendung erfordert folgende Berechtigungen:"
pleaseGoBack: "Bitte gehe zurück zur Anwendung"
callback: "Rückkehr zur Anwendung"
denied: "Zugriff verweigert"
_antennaSources:
all: "Alle Notizen"
homeTimeline: "Notizen von Benutzern, denen gefolgt wird"
users: "Notizen von konkreten Benutzern"
userList: "Notizen von allen Benutzern aus einer Liste"
userGroup: "Notizen von allen Benutzern aus einer Gruppe"
_weekday:
sunday: "Sonntag"
monday: "Montag"
@@ -358,7 +606,7 @@ _weekday:
_widgets:
memo: "Memo"
notifications: "Benachrichtigungen"
timeline: "Zeitleiste"
timeline: "Chronik"
calendar: "Kalender"
trends: "Trends"
clock: "Uhr"
@@ -367,20 +615,20 @@ _widgets:
photos: "Fotos"
_cw:
hide: "Ausblenden"
show: "Zeige mehr"
show: "Mehr anzeigen"
chars: "{count} Zeichen"
files: "{count} Dateien"
poll: "Umfrage"
_poll:
noOnlyOneChoice: "Mindestens zwei Antwortmöglichkeiten werden benötigt."
choiceN: "Auswahl {n}"
noMore: "Du kannst keine weiteren Auswahlen hinzufügen"
noMore: "Du kannst keine weiteren Auswahlmöglichkeiten hinzufügen"
canMultipleVote: "Mehrfachantworten erlauben"
expiration: "Abstimmung endet am"
infinite: "Nie"
at: "Beenden am..."
after: "Beenden nach..."
deadlineDate: "Enddatum"
deadlineDate: "Abstimmungsende"
deadlineTime: "Stunde"
duration: "Laufzeit"
votesCount: "{n} Stimmen"
@@ -397,10 +645,11 @@ _visibility:
public: "Öffentlich"
publicDescription: "Deine Notiz wird global sichtbar sein"
home: "Startseite"
followers: "Folgende"
homeDescription: "Deine Notiz wird nur in der Chronik deiner Instanz angezeigt."
followers: "Follower"
followersDescription: "Nur für Follower sichtbar"
specified: "Direkt"
specifiedDescription: "Nur für erwähnte Benutzer sichtbar"
specifiedDescription: "Nur für bestimmte Benutzer sichtbar"
localOnly: "Nur Lokal"
_postForm:
replyPlaceholder: "Dieser Notiz antworten..."
@@ -422,9 +671,9 @@ _profile:
metadataContent: "Inhalt"
_exportOrImport:
allNotes: "Alle Notizen"
followingList: "Folgen"
muteList: "Stummschalten"
blockingList: "Blockieren"
followingList: "Gefolgte Benutzer"
muteList: "Stummschaltungen"
blockingList: "Blockierungen"
userLists: "Listen"
_charts:
federationInstancesIncDec: "Unterschied in der Anzahl von förderierenden Instanzen"
@@ -434,6 +683,7 @@ _charts:
activeUsers: "Aktive Benutzer"
notesIncDec: "Unterschied in der Anzahl von Notizen"
localNotesIncDec: "Unterschied in der Anzahl von lokalen Notizen"
remoteNotesIncDec: "Differenz in Anzahl der Notizen von anderen Instanzen"
notesTotal: "Anzahl aller Notizen"
filesIncDec: "Unterschied in der Anzahl von Dateien"
filesTotal: "Anzahl aller Dateien"
@@ -442,31 +692,57 @@ _charts:
_instanceCharts:
requests: "Anfragen"
users: "Unterschied in der Anzahl von Benutzern"
usersTotal: "Anzahl aller Benutzer"
usersTotal: "Gesamtanzahl an Benutzern"
notes: "Unterschied in der Anzahl von Notizen"
notesTotal: "Anzahl aller Notizen"
notesTotal: "Gesamtanzahl an Notizen"
ff: "Unterschied in der Anzahl von Followern"
ffTotal: "Gesamtanzahl der Follower"
ffTotal: "Gesamtanzahl an Followern"
cacheSize: "Unterschied in der Größe des Caches"
cacheSizeTotal: "Gesamtgröße des Caches"
files: "Unterschied in der Anzahl der Dateien"
filesTotal: "Gesamtanzahl der Dateien"
filesTotal: "Gesamtanzahl an Dateien"
_timelines:
home: "Startseite"
local: "Lokal"
social: "Sozial"
global: "Global"
_pages:
newPage: "Seite erstellen"
editPage: "Diese Seite bearbeiten"
readPage: "Quelltext-Ansicht"
created: "Seite erfolgreich erstellt"
updated: "Seite erfolgreich aktualisiert"
deleted: "Seite erfolgreich gelöscht"
nameAlreadyExists: "Die angegebene Seiten-URL existiert bereits"
invalidNameTitle: "Die angegebene Seiten-URL ist ungültig"
invalidNameText: "Überprüfe, ob der Seitentitel nicht leer ist"
editThisPage: "Diese Seite bearbeiten"
viewSource: "Quelltext anzeigen"
viewPage: "Seite anschauen"
like: "Gefällt mir"
unlike: "\"Gefällt mir\" entfernen"
my: "Meine Seiten"
liked: "Seiten, die mir gefallen"
inspector: "Inspektor"
content: "Inhalt"
variables: "Variablen"
title: "Titel"
url: "Seiten-URL"
summary: "Zusammenfassung"
alignCenter: "Mittig ausrichten"
alignCenter: "Bestandteile zentrieren"
hideTitleWhenPinned: "Seitentitel ausblenden, wenn an dein Profil angepinnt "
font: "Schriftart"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
eyeCatchingImageSet: "Vorschaubild festlegen"
eyeCatchingImageRemove: "Vorschaubild entfernen"
chooseBlock: "Block hinzufügen"
selectType: "Wähle einen Typ"
enterVariableName: "Gib einen Namen für deine Variable ein"
variableNameIsAlreadyUsed: "Dieser Name wird bereits von einer anderen Variable verwendet"
contentBlocks: "Inhalt"
inputBlocks: "Eingabe"
specialBlocks: "Spezial"
blocks:
text: "Text"
textarea: "Textfeld"
@@ -479,24 +755,36 @@ _pages:
post: "Neue Notiz anfertigen"
_post:
text: "Inhalt"
attachCanvasImage: "Leinwand als Bild anfügen"
canvasId: "Leinwand-ID"
textInput: "Texteingabe"
_textInput:
name: "Variablenname"
text: "Titel"
default: "Standardwert"
textareaInput: "Eingabe des mehrzeiligen Textfelds"
_textareaInput:
name: "Variablenname"
text: "Titel"
default: "Standardwert"
numberInput: "Nummereingabe"
_numberInput:
name: "Variablenname"
text: "Titel"
default: "Standardwert"
canvas: "Leinwand"
_canvas:
id: "Leinwand-ID"
width: "Breite"
height: "Höhe"
switch: "Fallunterscheidung"
_switch:
name: "Variablenname"
text: "Titel"
default: "Standardwert"
counter: "Zähler"
_counter:
name: "Variablenname"
text: "Titel"
inc: "Erhöhen um"
_button:
@@ -514,13 +802,18 @@ _pages:
message: "Nachricht, die bei Aktivierung gezeigt werden soll"
variable: "Variable, die gesendet werden soll"
no-variable: "Keine"
callAiScript: "AiScript ausführen"
_callAiScript:
functionName: "Funktionsname"
radioButton: "Optionsfeld"
_radioButton:
name: "Variablenname"
title: "Titel"
values: "Auswahlmöglichkeiten (getrennt durch Zeilenumbrüche)"
default: "Standardwert"
script:
categories:
flow: "Steuerung"
logical: "Logische Operationen"
operation: "Berechnungen"
comparison: "Vergleiche"
@@ -671,17 +964,26 @@ _pages:
splitStrByLine: "Text nach Zeilenumbrüchen aufteilen"
_splitStrByLine:
arg1: "Text"
ref: "Variablen"
aiScriptVar: "AiScript Variablen"
fn: "Funktionen"
_fn:
slots: "Slots"
slots-info: "Trenne jeden Slot mit einem Zeilenumbruch"
arg1: "Ausgabe"
for: "Wiederholen"
_for:
arg1: "Anzahl der Wiederholungen"
arg2: "Aktion"
typeError: "Slot {slot} akzeptiert Werte vom Typ \"{expect}\", aber es wurde ein \"{actual}\" Wert angegeben!"
thereIsEmptySlot: "Slot {slot} ist leer!"
types:
string: "Text"
number: "Nummer"
boolean: "Flag"
array: "Listen"
stringArray: "Textliste"
emptySlot: "Leerer Slot"
enviromentVariables: "Umgebungsvariable"
pageVariables: "Seitenelement"
argVariables: "Eingabe-Slot"

View File

@@ -18,7 +18,7 @@ instance: "Instance"
settings: "Settings"
profile: "Profile"
timeline: "Timeline"
noAccountDescription: "This user has not created their bio yet."
noAccountDescription: "This user has not written their bio yet."
login: "Sign In"
loggingIn: "Signing In"
logout: "Sign Out"
@@ -239,6 +239,8 @@ drive: "Drive"
fileName: "Filename"
selectFile: "Select a file"
selectFiles: "Select files"
selectFolder: "Select a folder"
selectFolders: "Select folders"
renameFile: "Rename file"
folderName: "Folder name"
createFolder: "Create a folder"
@@ -257,14 +259,15 @@ rename: "Rename"
avatar: "Avatar"
banner: "Banner"
nsfw: "NSFW"
disconnectedFromServer: "Connection to the server was inturrupted"
disconnectedFromServer: "Connection to the server was interrupted."
reload: "Refresh"
doNothing: "Ignore"
reloadConfirm: "Would you like to retry?"
reloadConfirm: "Would you like to refresh timeline?"
watch: "Watch"
unwatch: "Undo Watch"
accept: "Accept"
reject: "Reject"
normal: "Normal"
instanceName: "Instance name"
instanceDescription: "Instance description"
maintainerName: "Maintainer"
@@ -319,6 +322,7 @@ notesAndReplies: "Notes and replies"
withFiles: "Media"
silence: "Silence"
silenceConfirm: "Are you sure that you want to silence this user?"
unsilence: "Unsilence"
unsilenceConfirm: "Are you sure that you want to undo silence of this user?"
popularUsers: "Trending users"
recentlyUpdatedUsers: "Users with recent activity"
@@ -331,7 +335,7 @@ userList: "Lists"
about: "About"
aboutMisskey: "About Misskey"
aboutMisskeyText: "Misskey is an open-source software developed by syuilo since 2014."
misskeyMembers: "It is currently developed an maintained by the members listed below:"
misskeyMembers: "It is currently developed and maintained by the members listed below:"
misskeySource: "Source code is available here:"
misskeyTranslation: "Help us with your contribution to translate Misskey:"
misskeyDonate: "Help us to keep improving the software by donating here:"
@@ -350,12 +354,10 @@ unregister: "Unregister"
passwordLessLogin: "Set up password-less login"
resetPassword: "Reset password"
newPasswordIs: "The new password is \"{password}\""
post: "Post"
posted: "Posted!"
autoReloadWhenDisconnected: "Auto reload when disconnected with server"
autoReloadWhenDisconnected: "Auto refresh when disconnected from server"
autoNoteWatch: "Watch note automatically"
autoNoteWatchDescription: "Get notified about the notes which you reactioned or replied."
reduceUiAnimation: "Reduce animations of User Interface"
reduceUiAnimation: "Reduce UI animation"
share: "Share"
notFound: "Not found"
notFoundDescription: "There was no page corresponding to the specified URL."
@@ -411,11 +413,11 @@ or: "Or"
uiLanguage: "UI display language"
groupInvited: "Invited to group"
aboutX: "About {x}"
useOsNativeEmojis: "Use the OS native Emojis"
useOsNativeEmojis: "Use OS native Emojis"
youHaveNoGroups: "You have no groups"
joinOrCreateGroup: "Get invited to join the groups or you can create your own group."
noHistory: "No history items"
disableAnimatedMfm: "Disable MFM which has animations"
disableAnimatedMfm: "Disable MFM with animation"
doing: "On my way"
category: "Category"
tags: "Tags"
@@ -454,6 +456,8 @@ objectStorageRegion: "Region"
objectStorageRegionDesc: "Specify a region like 'xx-east-1'. If your service does not have distinction about regions, leave it blank or fill with 'us-east-1'."
objectStorageUseSSL: "Use SSL"
objectStorageUseSSLDesc: "Turn off this if you are not going to use HTTPS for API connection"
objectStorageUseProxy: "Connect over Proxy"
objectStorageUseProxyDesc: "Turn off this if you are not going to use Proxy for ObjectStorage connection"
serverLogs: "Server logs"
deleteAll: "Delete all"
showFixedPostForm: "Display the posting form at the top of the timeline"
@@ -466,6 +470,36 @@ details: "Details"
chooseEmoji: "Choose an emoji"
unableToProcess: "The operation could not be completed."
recentUsed: "Recently used"
install: "Install"
uninstall: "Uninstall"
installedApps: "Authorized Applications"
nothing: "There's nothing to see here"
installedDate: "Authorized"
lastUsedDate: "Last used"
state: "State"
sort: "Sort"
ascendingOrder: "Ascending"
descendingOrder: "Descending"
scratchpad: "Scratch pad"
scratchpadDescription: "Scratchpad provides experimental environment for AiScript. You can write, execute, and check the results that interact with Misskey."
output: "Output"
script: "Script"
disablePagesScript: "Disable AiScript on Pages"
updateRemoteUser: "Update remote user information"
deleteAllFiles: "Delete All Files"
deleteAllFilesConfirm: "Are you sure that you want to delete all files?"
removeAllFollowing: "Withhold All Followings"
removeAllFollowingDescription: "Unfollow all accounts from {host}. Please run this if the instance no longer exists."
userSuspended: "This user has been suspended."
userSilenced: "This user has been silenced."
_theme:
explore: "Explore Themes"
install: "Install theme"
manage: "Themes manager"
code: "Theme code"
installed: "{name} has been installed"
alreadyInstalled: "The theme is already installed"
invalid: "Theme format is invalid"
_sfx:
note: "New note"
noteMy: "My note"
@@ -498,7 +532,7 @@ _tutorial:
step2_2: "Providing some information about who you are will make it easier for others to follow you back."
step3_1: "Finished setting up your profile?"
step3_2: "The next step is to post a note. You can do this by pressing the pencil icon on the screen."
step3_3: "Fill in the modal and press the button on the right top to post."
step3_3: "Fill in the modal and press the button on the top right to post."
step3_4: "Have nothing to say? Try \"just setting up my msky\"!"
step4_1: "Finished posting your first note?"
step4_2: "Hurray! Now your first note is displayed on your timeline."
@@ -550,7 +584,11 @@ _permissions:
"write:user-groups": "Edit or delete user groups"
_auth:
shareAccess: "Would you like to authorize \"{name}\" to access this account?"
shareAccessAsk: "Are you sure you want to authorize this application to access your account?"
permissionAsk: "This application requires following permissions:"
pleaseGoBack: "Please go back to the application"
callback: "Returning back to the application"
denied: "Access Denied"
_antennaSources:
all: "All notes"
homeTimeline: "Notes from following users"
@@ -654,15 +692,15 @@ _charts:
_instanceCharts:
requests: "Requests"
users: "Difference in # of users"
usersTotal: "Total # of users"
usersTotal: "Cumulative total # of users"
notes: "Difference in # of notes"
notesTotal: "Total # of notes"
notesTotal: "Cumulative total # of notes"
ff: "Difference in # of followers"
ffTotal: "Total # of followers"
ffTotal: "Cumulative total # of followers"
cacheSize: "Difference in cache size"
cacheSizeTotal: "Total accumulated cache"
cacheSizeTotal: "Cumulative total cache size"
files: "Difference in # of files"
filesTotal: "Total # of files"
filesTotal: "Cumulative total # of files"
_timelines:
home: "Home"
local: "Local"
@@ -717,6 +755,8 @@ _pages:
post: "Compose a note"
_post:
text: "Content"
attachCanvasImage: "Post with Canvas as Image"
canvasId: "Canvas ID"
textInput: "Text input"
_textInput:
name: "Variable name"
@@ -732,6 +772,11 @@ _pages:
name: "Variable name"
text: "Title"
default: "Default value"
canvas: "Canvas"
_canvas:
id: "Canvas ID"
width: "Width"
height: "Height"
switch: "Switch"
_switch:
name: "Variable name"
@@ -757,6 +802,9 @@ _pages:
message: "Message to display when activated"
variable: "Variable to send"
no-variable: "None"
callAiScript: "Invoke AiScript"
_callAiScript:
functionName: "Function name"
radioButton: "Choice"
_radioButton:
name: "Variable name"
@@ -917,6 +965,7 @@ _pages:
_splitStrByLine:
arg1: "Text"
ref: "Variables"
aiScriptVar: "Variable of AiScript"
fn: "Functions"
_fn:
slots: "Slots"

View File

@@ -60,7 +60,7 @@ lists: "Listas"
noLists: "No tiene listas"
note: "Notas"
notes: "Notas"
following: "Sigue"
following: "Siguiendo"
followers: "Seguidores"
followsYou: "Te sigue"
createList: "Crear lista"
@@ -239,6 +239,8 @@ drive: "Drive"
fileName: "Nombre de archivo"
selectFile: "Elegir archivo"
selectFiles: "Elegir archivos"
selectFolder: "Seleccione una carpeta"
selectFolders: "Seleccione carpetas"
renameFile: "Renombrar archivo"
folderName: "Nombre de la carpeta"
createFolder: "Crear carpeta"
@@ -265,6 +267,7 @@ watch: "Ver"
unwatch: "Dejar de ver"
accept: "Aceptar"
reject: "Rechazar"
normal: "Normal"
instanceName: "Nombre de la instancia"
instanceDescription: "Descripción de la instancia"
maintainerName: "Nombre del administrador"
@@ -319,6 +322,7 @@ notesAndReplies: "Notas y respuestas"
withFiles: "Adjuntos"
silence: "Silenciar"
silenceConfirm: "¿Desea silenciar al usuario?"
unsilence: "Dejar de silenciar"
unsilenceConfirm: "¿Desea dejar de silenciar al usuario?"
popularUsers: "Usuarios populares"
recentlyUpdatedUsers: "Usuarios activos recientemente"
@@ -350,8 +354,6 @@ unregister: "Cancelar registro"
passwordLessLogin: "Iniciar sesión sin contraseña"
resetPassword: "Resetear contraseña"
newPasswordIs: "La nueva contraseña es \"{password}\""
post: "Nota"
posted: "Posteado"
autoReloadWhenDisconnected: "Recargar automáticamente cuando el servidor está desconectado"
autoNoteWatch: "Ver nota automáticamente"
autoNoteWatchDescription: "Recibe notificaciones sobre las notas de otros usuarios que a los que respondiste y reaccionaste"
@@ -454,6 +456,8 @@ objectStorageRegion: "Region"
objectStorageRegionDesc: "Especifique una región como 'xx-east-1'. Si su servicio no tiene distinción sobre regiones, déjelo en blanco o complete con 'us-east-1'."
objectStorageUseSSL: "Usar SSL"
objectStorageUseSSLDesc: "Desactive esto si no va a usar HTTPS para la conexión API"
objectStorageUseProxy: "Conectarse a través de Proxy"
objectStorageUseProxyDesc: "Desactive esto si no va a usar Proxy para la conexión de Almacenamiento de objetos"
serverLogs: "Registros del servidor"
deleteAll: "Eliminar todos"
showFixedPostForm: "Mostrar el formulario de las entradas encima de la línea de tiempo"
@@ -466,6 +470,36 @@ details: "Detalles"
chooseEmoji: "Elije un emoji"
unableToProcess: "La operación no se puede llevar a cabo"
recentUsed: "Usado recientemente"
install: "Instalación"
uninstall: "Desinstalar"
installedApps: "Aplicaciones Autorizadas"
nothing: "No hay nada que ver aqui"
installedDate: "Autorizado"
lastUsedDate: "Utilizado el"
state: "Estado"
sort: "Ordenar"
ascendingOrder: "Ascendente"
descendingOrder: "Descendente"
scratchpad: "Scratch pad"
scratchpadDescription: "Scratchpad proporciona un entorno experimental para AiScript. Puede escribir, ejecutar y verificar los resultados que interactúan con Misskey."
output: "Salida"
script: "Script"
disablePagesScript: "Deshabilitar AiScript en Páginas"
updateRemoteUser: "Actualizar información de usuario remoto"
deleteAllFiles: "Borrar todos los archivos"
deleteAllFilesConfirm: "¿Desea borrar todos los archivos?"
removeAllFollowing: "Retener todos los siguientes"
removeAllFollowingDescription: "Cancelar todos los siguientes del servidor {host}. Ejecutar en caso de que esta instancia haya dejado de existir"
userSuspended: "Este usuario ha sido suspendido."
userSilenced: "Este usuario ha sido silenciado."
_theme:
explore: "Explorar temas"
install: "Instalar tema"
manage: "Gestor de temas"
code: "Código del tema"
installed: "{name} ha sido instalado"
alreadyInstalled: "Este tema ya está instalado"
invalid: "El formato del tema no es válido"
_sfx:
note: "Notas"
noteMy: "Nota (a mí mismo)"
@@ -550,7 +584,11 @@ _permissions:
"write:user-groups": "Administrar grupos de usuarios"
_auth:
shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?"
shareAccessAsk: "¿Está seguro de que desea autorizar esta aplicación para acceder a su cuenta?"
permissionAsk: "Esta aplicación requiere los siguientes permisos"
pleaseGoBack: "Por favor, vuelve a la aplicación"
callback: "Volviendo a la aplicación"
denied: "Acceso denegado"
_antennaSources:
all: "Todas las notas"
homeTimeline: "Notas de los usuarios que sigues"
@@ -654,15 +692,15 @@ _charts:
_instanceCharts:
requests: "Pedidos"
users: "Variación de usuarios"
usersTotal: "Total de usuarios"
usersTotal: "Total acumulado de usuarios"
notes: "Variación de la cantidad de notas"
notesTotal: "Estimación de notas"
notesTotal: "Total acumulado de la cantidad de notas"
ff: "Variación de cantidad de seguidos/seguidores"
ffTotal: "Total de seguidos/seguidores"
ffTotal: "Total acumulado de cantidad de seguidos/seguidores"
cacheSize: "Variación del tamaño de la caché"
cacheSizeTotal: "Total del tamaño de la caché"
cacheSizeTotal: "Total acumulado del tamaño de la caché"
files: "Variación de cantidad de archivos"
filesTotal: "Total de archivos"
filesTotal: "Total acumulado de cantidad de archivos"
_timelines:
home: "Inicio"
local: "Local"
@@ -717,6 +755,8 @@ _pages:
post: "Formulario"
_post:
text: "Contenido"
attachCanvasImage: "Nota con lienzo como imagen"
canvasId: "Lienzo ID"
textInput: "Entrada de texto"
_textInput:
name: "Nombre de variable"
@@ -732,6 +772,11 @@ _pages:
name: "Nombre de variable"
text: "Título"
default: "Valor predeterminado"
canvas: "Lienzo"
_canvas:
id: "Lienzo ID"
width: "Ancho"
height: "Altura"
switch: "Interruptor"
_switch:
name: "Nombre de variable"
@@ -757,6 +802,9 @@ _pages:
message: "Mensaje mostrado al apretar"
variable: "Variable a enviar"
no-variable: "Ninguna"
callAiScript: "Invocar AiScript"
_callAiScript:
functionName: "Nombre de la función"
radioButton: "Botón de opción"
_radioButton:
name: "Nombre de variable"
@@ -917,6 +965,7 @@ _pages:
_splitStrByLine:
arg1: "Texto"
ref: "Variables"
aiScriptVar: "Variable de AiScript"
fn: "funciones"
_fn:
slots: "Slots"

View File

@@ -42,8 +42,8 @@ sendMessage: "Envoyer un message"
copyUsername: "Copier le nom d'utilisateur"
reply: "Répondre"
loadMore: "Voir plus"
youGotNewFollower: "Vous a abonnés"
receiveFollowRequest: "Demande de abonnés reçue"
youGotNewFollower: "Vous suit"
receiveFollowRequest: "Demande de suivi reçue"
followRequestAccepted: "L'abonne la demande acceptée"
mentions: "Mentions"
directNotes: "Messages directs"
@@ -53,7 +53,7 @@ export: "Exporter"
files: "Fichier·s"
download: "Télécharger"
driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier \"{name}\" ? Les notes avec ce fichier joint seront aussi supprimées."
unfollowConfirm: "Êtes-vous sûr·de ne plus vouloir abonne {name} ?"
unfollowConfirm: "Se désabonner de {name} ?"
exportRequested: "Vous avez demandé une exportation. Cela pourrait prendre un peu de temps. Une fois l'exportation terminée, le fichier résultant sera ajouté dans le Drive."
importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps."
lists: "Listes"
@@ -62,17 +62,17 @@ note: "Note"
notes: "Notes"
following: "Abonnements"
followers: "Abonné·e·s"
followsYou: "Votre abonné"
followsYou: "Vous suit"
createList: "Créer une liste"
manageLists: "Gérer les listes"
error: "Une erreur est survenue"
retry: "Réessayer"
enterListName: "Nom de la liste"
privacy: "Vie privée"
makeFollowManuallyApprove: "Demandes dabonnements requiert lapprobation"
makeFollowManuallyApprove: "Demandes dsuivi requiert l'approbation"
defaultNoteVisibility: "Visibilité par défaut"
follow: "Abonnement"
followRequest: "Demande dabonnement"
follow: "Suivre"
followRequest: "Demande dsuivre"
followRequests: "Demandes dabonnement"
unfollow: "Se désabonner"
followRequestPending: "En attente dapprobation"
@@ -121,7 +121,7 @@ setWallpaper: "Définir le fond d'écran"
removeWallpaper: "Supprimer l'arrière plan"
searchWith: "Recherche : {q}"
youHaveNoLists: "Vous n'avez aucune liste"
followConfirm: "Désirez-vous abonne {name} ?"
followConfirm: "Désirez-vous suivre {name} ?"
proxyAccount: "Compte proxy"
proxyAccountDescription: "Un compte proxy se comporte, dans certaines conditions, comme un·e abonné·e distant pour les utilisateurs d'autres instances.\nExemple : quand un·e utilisateur·rice distant·e est ajouté·e à une liste, ses notes ne serait pas visibles sur l'instance si personne ne le·la abonné. Le compte proxy va donc le·la abonne pour que ses notes soient acheminées."
host: "Hôte"
@@ -192,7 +192,7 @@ newPassword: "Nouveau mot de passe"
newPasswordRetype: "Nouveau mot de passe (répéter)"
attachFile: "Joindre un fichier"
more: "Plus !"
featured: "Surlignage"
featured: "Tendances"
usernameOrUserId: "Nom d'utilisateur ou ID utilisateur"
noSuchUser: "Utilisateur non trouvé"
lookup: "Recherche"
@@ -214,7 +214,7 @@ explore: "Découvrir"
games: "Jeux de Misskey"
messageRead: "Lus"
noMoreHistory: "Plus d'histoire passée"
startMessaging: "Commencer à écrire un discutez"
startMessaging: "Commencer à discuter"
nUsersRead: "{n} personnes ont lu"
agreeTo: "D'accord {0}"
tos: "Conditions d'utilisation"
@@ -239,6 +239,8 @@ drive: "Drive"
fileName: "Nom du fichier"
selectFile: "Choisir le fichier"
selectFiles: "Choisir le fichiers"
selectFolder: "Sélectionnez un dossier"
selectFolders: "Sélectionnez dossiers"
renameFile: "Renommer le ficher"
folderName: "Nom du dossier"
createFolder: "Créer un dossier"
@@ -265,6 +267,7 @@ watch: "Surveiller"
unwatch: "Ne plus surveiller"
accept: "Autoriser"
reject: "Refuser"
normal: "Normal"
instanceName: "Nom de linstance"
instanceDescription: "Description de linstance"
maintainerName: "Nom d'administrateur"
@@ -319,6 +322,7 @@ notesAndReplies: "Notes et Répondres"
withFiles: "Avec fichiers joints"
silence: "Mettre en masquer"
silenceConfirm: "Mettre l'utilisateur sous masquer ?"
unsilence: "Annuler la masquer"
unsilenceConfirm: "Voulez-vous annuler le masquer ?"
popularUsers: "Utilisateur·rice·s populaires"
recentlyUpdatedUsers: "Utilisateur·rice·s actif·ve·s récemment"
@@ -350,8 +354,6 @@ unregister: "Se désinscrire"
passwordLessLogin: "Connectez-vous sans mot de passe"
resetPassword: "Réinitialiser mot de passe"
newPasswordIs: "Votre nouveau mot de passe est \"{password}\""
post: "Publier"
posted: "Publié !"
autoReloadWhenDisconnected: "Rechargement automatique lorsque le serveur se déconnecte"
autoNoteWatch: "Surveiller automatique pour les notes"
autoNoteWatchDescription: "Soyez informé des notes auxquelles vous avez réagi ou répondu."
@@ -454,6 +456,8 @@ objectStorageRegion: "Region"
objectStorageRegionDesc: "Spécifiez une région comme 'xx-east-1'. Si votre service ne fait pas de distinction entre les régions, laissez-le vide ou remplissez 'us-east-1'."
objectStorageUseSSL: "Utiliser SSL"
objectStorageUseSSLDesc: "Désactivez-le si vous n'utilisez pas HTTPS pour la connexion API"
objectStorageUseProxy: "Se connecter via proxy"
objectStorageUseProxyDesc: "Désactivez-le si vous n'utilisez pas Proxy pour la connexion de stockage d'objets"
serverLogs: "Journaux serveur"
deleteAll: "Supprimer tout"
showFixedPostForm: "Afficher le formulaire en haut du fil d'actualité"
@@ -466,6 +470,36 @@ details: "Détails"
chooseEmoji: "Choisissez des emojis"
unableToProcess: "L'opération n'a pas pu être complétée"
recentUsed: "Récemment utilisé"
install: "Installation"
uninstall: "Désinstaller"
installedApps: "Applications Autorisées"
nothing: "Il n'y a rien à voir ici"
installedDate: "Autorisé"
lastUsedDate: "Dernière utilisation"
state: "État"
sort: "Trier"
ascendingOrder: "Ascendant"
descendingOrder: "Descendant"
scratchpad: "Scratch pad"
scratchpadDescription: "Scratchpad fournit un environnement expérimental pour AiScript. Vous pouvez écrire, exécuter et vérifier les résultats qui interagissent avec Misskey."
output: "Sortie"
script: "Script"
disablePagesScript: "Désactiver AiScript sur les Pages"
updateRemoteUser: "Mettre à jour les informations de lutilisateur·rice distant·e"
deleteAllFiles: "Supprimer tous les fichiers"
deleteAllFilesConfirm: "Êtes vous surs de vouloir supprimer tous les fichiers ?"
removeAllFollowing: "Retenir tous les abonnements"
removeAllFollowingDescription: "Se désabonner de tous les comptes de {host}. Exécutez cette commande si l'instance n'existe plus."
userSuspended: "Cette utilisateur·trice a été suspendue."
userSilenced: "Cette utilisateur·trice a été masquer."
_theme:
explore: "Explorer les thèmes"
install: "Installer un thème"
manage: "Gestion des thèmes"
code: "Code du thème"
installed: "{name} a été installé"
alreadyInstalled: "Ce thème est déjà installé"
invalid: "Le format du thème n'est pas valide"
_sfx:
note: "Nouvelle note"
noteMy: "Ma note"
@@ -514,7 +548,7 @@ _tutorial:
step7_3: "Alors, profitez de Misskey 🚀"
_2fa:
alreadyRegistered: "Cette étape à déjà été complétée"
registerDevice: "Sinscrire l'appareil"
registerDevice: "Ajouter un appareil"
registerKey: "Sinscrire la clé"
step1: "Tout d'abord, installez une application d'authentification, telle que {a} ou {b}, sur votre appareil."
step2: "Ensuite, scannez le code QR affiché avec l'application."
@@ -550,7 +584,11 @@ _permissions:
"write:user-groups": "Éditer les groupes des utilisateur·rice·s"
_auth:
shareAccess: "Autoriser \"{name}\" à accéder à votre compte ?"
shareAccessAsk: "Voulez-vous vraiment autoriser cette application à accéder à votre compte?"
permissionAsk: "Cette application nécessite les autorisations suivantes "
pleaseGoBack: "Veillez retourner à l'application"
callback: "Retour vers lapplication"
denied: "Accès refusé"
_antennaSources:
all: "Toutes les notes"
homeTimeline: "Notes de l'utilisateur auquel je m'abonne"
@@ -641,7 +679,7 @@ _charts:
federationInstancesIncDec: "Variation du nombre d'instances"
federationInstancesTotal: "Nombre d'instances au total"
usersIncDec: "Variation du nombre d'utilisateur·rice·s"
usersTotal: "Nombre d'utilsateur·rice·s au total"
usersTotal: "Nombre d'utilisateur·rice·s au total"
activeUsers: "Utilisateur·rice·s actif·ve·s"
notesIncDec: "Variation du nombre d'notes"
localNotesIncDec: "Variation du nombre de notes local"
@@ -654,15 +692,15 @@ _charts:
_instanceCharts:
requests: "Requêtes"
users: "Variation du nombre d'utilisateur·rice·s"
usersTotal: "Somme du nombre d'utilisateur·rice·s accumulés"
usersTotal: "Nombre d'utilisateur·rice·s au total cumulé"
notes: "Variation du nombre d'notes"
notesTotal: "Somme du nombre dnotes accumulés"
notesTotal: "Nombre d'notes au total cumulé"
ff: "Variation des abonné·e·s"
ffTotal: "Somme du nombre d'abonnements accumulés"
ffTotal: "Nombre d'abonné·e·s au total cumulé"
cacheSize: "Variation de la taille du cache"
cacheSizeTotal: "Somme de la taille du cache accumulé"
cacheSizeTotal: "La taille du cache au total cumulé"
files: "Variation du nombre de fichiers"
filesTotal: "Somme du nombre de fichiers accumulés"
filesTotal: "Nombre de fichiers au total cumulé"
_timelines:
home: "Principal"
local: "Local"
@@ -717,6 +755,8 @@ _pages:
post: "Formulaire à publier"
_post:
text: "Contenu"
attachCanvasImage: "Publier avec Toile comme image"
canvasId: "Toile ID"
textInput: "Entrée de textuelle"
_textInput:
name: "Nom de la variable"
@@ -732,6 +772,11 @@ _pages:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
canvas: "Toile"
_canvas:
id: "Toile ID"
width: "Largeur"
height: "Hauteur"
switch: "Basculer"
_switch:
name: "Nom de la variable"
@@ -757,6 +802,9 @@ _pages:
message: "Message à afficher lorsque appuyé"
variable: "Variable à envoyer"
no-variable: "Rien"
callAiScript: "Appeler AiScript"
_callAiScript:
functionName: "Nom de la fonction"
radioButton: "Choix"
_radioButton:
name: "Nom de la variable"
@@ -917,6 +965,7 @@ _pages:
_splitStrByLine:
arg1: "Texte"
ref: "Variables"
aiScriptVar: "Variable d'AiScript"
fn: "Fonction"
_fn:
slots: "Slots"

View File

@@ -16,7 +16,7 @@ const merge = (...args) => args.reduce((a, c) => ({
const languages = [
//'cs-CZ',
//'da-DK',
//'de-DE',
'de-DE',
'en-US',
'es-ES',
'fr-FR',
@@ -26,7 +26,7 @@ const languages = [
//'nl-NL',
//'pl-PL',
'zh-CN',
//'zh-TW',
'zh-TW',
];
const primaries = {

View File

@@ -239,6 +239,8 @@ drive: "ドライブ"
fileName: "ファイル名"
selectFile: "ファイルを選択"
selectFiles: "ファイルを選択"
selectFolder: "フォルダーを選択"
selectFolders: "フォルダーを選択"
renameFile: "ファイル名を変更"
folderName: "フォルダー名"
createFolder: "フォルダーを作成"
@@ -265,6 +267,7 @@ watch: "ウォッチ"
unwatch: "ウォッチ解除"
accept: "許可"
reject: "拒否"
normal: "正常"
instanceName: "インスタンス名"
instanceDescription: "インスタンスの紹介"
maintainerName: "管理者の名前"
@@ -319,6 +322,7 @@ notesAndReplies: "投稿と返信"
withFiles: "ファイル付き"
silence: "サイレンス"
silenceConfirm: "サイレンスしますか?"
unsilence: "サイレンス解除"
unsilenceConfirm: "サイレンス解除しますか?"
popularUsers: "人気のユーザー"
recentlyUpdatedUsers: "最近投稿したユーザー"
@@ -350,8 +354,6 @@ unregister: "登録を解除"
passwordLessLogin: "パスワード無しログイン"
resetPassword: "パスワードをリセット"
newPasswordIs: "新しいパスワードは「{password}」です"
post: "投稿"
posted: "投稿しました"
autoReloadWhenDisconnected: "サーバー切断時に自動リロード"
autoNoteWatch: "ノートの自動ウォッチ"
autoNoteWatchDescription: "あなたがリアクションしたり返信したりした他のユーザーのノートに関する通知を受け取るようにします。"
@@ -454,6 +456,8 @@ objectStorageRegion: "Region"
objectStorageRegionDesc: "'xx-east-1'のようなregionを指定してください。使用サービスにregionの概念がない場合は、空または'us-east-1'にしてください。"
objectStorageUseSSL: "SSLを使用する"
objectStorageUseSSLDesc: "API接続にhttpsを使用しない場合はオフにしてください"
objectStorageUseProxy: "Proxyを利用する"
objectStorageUseProxyDesc: "API接続にproxyを利用しない場合はオフにしてください"
serverLogs: "サーバーログ"
deleteAll: "全て削除"
showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
@@ -466,6 +470,37 @@ details: "詳細"
chooseEmoji: "絵文字を選択"
unableToProcess: "操作を完了できません"
recentUsed: "最近使用"
install: "インストール"
uninstall: "アンインストール"
installedApps: "インストールされたアプリ"
nothing: "ありません"
installedDate: "インストール日時"
lastUsedDate: "最終使用日時"
state: "状態"
sort: "ソート"
ascendingOrder: "昇順"
descendingOrder: "降順"
scratchpad: "スクラッチパッド"
scratchpadDescription: "スクラッチパッドは、AiScriptの実験環境を提供します。Misskeyと対話するコードの記述、実行、結果の確認ができます。"
output: "出力"
script: "スクリプト"
disablePagesScript: "Pagesのスクリプトを無効にする"
updateRemoteUser: "リモートユーザー情報の更新"
deleteAllFiles: "すべてのファイルを削除"
deleteAllFilesConfirm: "すべてのファイルを削除しますか?"
removeAllFollowing: "フォローを全解除"
removeAllFollowingDescription: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
userSuspended: "このユーザーは凍結されています。"
userSilenced: "このユーザーはサイレンスされています。"
_theme:
explore: "テーマを探す"
install: "テーマのインストール"
manage: "テーマの管理"
code: "テーマコード"
installed: "{name}をインストールしました"
alreadyInstalled: "そのテーマは既にインストールされています"
invalid: "テーマの形式が間違っています"
_sfx:
note: "ノート"
@@ -557,7 +592,11 @@ _permissions:
_auth:
shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?"
shareAccessAsk: "アカウントへのアクセスを許可しますか?"
permissionAsk: "このアプリは次の権限を要求しています"
pleaseGoBack: "アプリケーションに戻ってやっていってください"
callback: "アプリケーションに戻っています"
denied: "アクセスを拒否しました"
_antennaSources:
all: "全てのノート"
@@ -672,15 +711,15 @@ _charts:
_instanceCharts:
requests: "リクエスト"
users: "ユーザーの増減"
usersTotal: "ユーザーの積"
usersTotal: "ユーザーの積"
notes: "ノートの増減"
notesTotal: "ノートの積"
notesTotal: "ノートの積"
ff: "フォロー/フォロワーの増減"
ffTotal: "フォロー/フォロワーの積"
ffTotal: "フォロー/フォロワーの積"
cacheSize: "キャッシュサイズの増減"
cacheSizeTotal: "キャッシュサイズの積"
cacheSizeTotal: "キャッシュサイズの積"
files: "ファイル数の増減"
filesTotal: "ファイル数の積"
filesTotal: "ファイル数の積"
_timelines:
home: "ホーム"
@@ -739,6 +778,8 @@ _pages:
post: "投稿フォーム"
_post:
text: "内容"
attachCanvasImage: "キャンバスの画像を添付する"
canvasId: "キャンバスID"
textInput: "テキスト入力"
_textInput:
@@ -758,6 +799,12 @@ _pages:
text: "タイトル"
default: "デフォルト値"
canvas: "キャンバス"
_canvas:
id: "キャンバスID"
width: "幅"
height: "高さ"
switch: "スイッチ"
_switch:
name: "変数名"
@@ -785,6 +832,9 @@ _pages:
message: "押したときに表示するメッセージ"
variable: "送信する変数"
no-variable: "なし"
callAiScript: "AiScript呼び出し"
_callAiScript:
functionName: "関数名"
radioButton: "選択肢"
_radioButton:
@@ -947,6 +997,7 @@ _pages:
_splitStrByLine:
arg1: "テキスト"
ref: "変数"
aiScriptVar: "AiScript変数"
fn: "関数"
_fn:
slots: "スロット"

View File

@@ -239,6 +239,8 @@ drive: "드라이브"
fileName: "파일명"
selectFile: "파일 선택"
selectFiles: "파일 선택"
selectFolder: "폴더 선택"
selectFolders: "폴더 선택"
renameFile: "파일 이름 변경"
folderName: "폴더명"
createFolder: "폴더 만들기"
@@ -265,6 +267,7 @@ watch: "지켜보기"
unwatch: "지켜보기 해제"
accept: "허가"
reject: "거부"
normal: "정상"
instanceName: "인스턴스 이름"
instanceDescription: "인스턴스 소개"
maintainerName: "관리자 이름"
@@ -319,6 +322,7 @@ notesAndReplies: "글과 답글"
withFiles: "미디어"
silence: "사일런스"
silenceConfirm: "이 계정을 사일런스로 설정하시겠습니까?"
unsilence: "사일런스 해제"
unsilenceConfirm: "이 계정의 사일런스를 해제하시겠습니까?"
popularUsers: "인기 유저"
recentlyUpdatedUsers: "최근 활동한 유저"
@@ -350,8 +354,6 @@ unregister: "등록 해제"
passwordLessLogin: "비밀번호 없이 로그인"
resetPassword: "비밀번호 재설정"
newPasswordIs: "새로운 비밀번호는 \"{password}\" 입니다"
post: "작성"
posted: "게시하였습니다"
autoReloadWhenDisconnected: "서버와의 연결이 끊기면 자동 새로고침"
autoNoteWatch: "노트를 자동으로 지켜보기"
autoNoteWatchDescription: "리액션하거나 답글을 남긴 다른 유저의 노트에 대한 알림을 받습니다."
@@ -454,6 +456,8 @@ objectStorageRegion: "Region"
objectStorageRegionDesc: "'xx-east-1'와 같이 region을 지정해주세요. 사용하는 서비스에 region 개념이 없는 경우, 비워 두거나 'us-east-1'으로 설정해 주세요."
objectStorageUseSSL: "SSL 사용"
objectStorageUseSSLDesc: "API 호출시 HTTPS 를 사용하지 않는 경우 OFF 로 설정해 주세요"
objectStorageUseProxy: "연결에 프록시를 사용"
objectStorageUseProxyDesc: "오브젝트 스토리지 API 호출시 프록시를 사용하지 않는 경우 OFF 로 설정해 주세요"
serverLogs: "서버 로그"
deleteAll: "모두 삭제"
showFixedPostForm: "타임라인 상단에 글 작성란을 표시"
@@ -466,6 +470,36 @@ details: "자세히"
chooseEmoji: "이모지 선택"
unableToProcess: "작업을 완료할 수 없습니다"
recentUsed: "최근 사용"
install: "설치"
uninstall: "삭제"
installedApps: "인증된 애플리케이션"
nothing: "아무것도 없습니다"
installedDate: "승인한 날짜"
lastUsedDate: "마지막 사용"
state: "상태"
sort: "정렬"
ascendingOrder: "오름차순"
descendingOrder: "내림차순"
scratchpad: "스크래치 패드"
scratchpadDescription: "스크래치 패드는 AiScript 의 테스트 환경을 제공합니다. Misskey 와 상호 작용하는 코드를 작성, 실행 및 결과를 확인할 수 있습니다."
output: "출력"
script: "스크립트"
disablePagesScript: "Pages 에서 AiScript 를 사용하지 않음"
updateRemoteUser: "리모트 유저 정보 갱신"
deleteAllFiles: "모든 파일 삭제"
deleteAllFilesConfirm: "모든 파일을 삭제하시겠습니까?"
removeAllFollowing: "모든 팔로잉 해제"
removeAllFollowingDescription: "{host}(으)로부터 모든 팔로잉을 해제합니다. 해당 인스턴스가 더 이상 존재하지 않게 된 경우 등에 실행해 주세요."
userSuspended: "이 계정은 정지된 상태입니다."
userSilenced: "이 계정은 사일런스된 상태입니다."
_theme:
explore: "테마 찾아보기"
install: "테마 설치"
manage: "테마 관리"
code: "테마 코드"
installed: "{name} 테마가 설치되었습니다"
alreadyInstalled: "이미 설치된 테마입니다"
invalid: "테마 형식이 올바르지 않습니다"
_sfx:
note: "새 노트"
noteMy: "내 노트"
@@ -550,7 +584,11 @@ _permissions:
"write:user-groups": "유저 그룹을 만들거나, 초대하거나, 이름을 변경하거나, 양도하거나, 삭제합니다"
_auth:
shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
shareAccessAsk: "이 애플리케이션이 계정에 접근하는 것을 허용하시겠습니까?"
permissionAsk: "이 앱은 다음의 권한을 요청합니다"
pleaseGoBack: "앱으로 돌아가서 시도해 주세요"
callback: "앱으로 돌아갑니다"
denied: "접근이 거부되었습니다"
_antennaSources:
all: "모든 노트"
homeTimeline: "팔로우중인 유저의 노트"
@@ -639,26 +677,26 @@ _exportOrImport:
userLists: "리스트"
_charts:
federationInstancesIncDec: "연합 인스턴스 수 증감"
federationInstancesTotal: "연합 인스턴스 수"
federationInstancesTotal: "연합 인스턴스 수 합계"
usersIncDec: "유저 수 증감"
usersTotal: "유저 수 합계"
activeUsers: "활성 유저 수"
notesIncDec: "노트 수 증감"
localNotesIncDec: "로컬 노트 수 증감"
remoteNotesIncDec: "리모트 노트 수 증감"
notesTotal: "노트 수"
notesTotal: "노트 수 합계"
filesIncDec: "파일 수 증감"
filesTotal: "파일 수"
filesTotal: "파일 수 합계"
storageUsageIncDec: "스토리지 사용량 증감"
storageUsageTotal: "스토리지 사용량"
storageUsageTotal: "스토리지 사용량 합계"
_instanceCharts:
requests: "요청"
users: "유저 수 증감"
usersTotal: "누적 유저 수"
notes: "노트 수 증감"
notesTotal: " 노트 수"
notesTotal: "누적 노트 수"
ff: "팔로잉/팔로워 증감"
ffTotal: "팔로잉/팔로워 누적"
ffTotal: "누적 팔로잉/팔로워 "
cacheSize: "캐시 용량 증감"
cacheSizeTotal: "누적 캐시 용량"
files: "파일 수 증감"
@@ -717,6 +755,8 @@ _pages:
post: "글 입력란"
_post:
text: "내용"
attachCanvasImage: "캔버스의 이미지와 함께 게시하기"
canvasId: "캔버스 ID"
textInput: "텍스트 입력"
_textInput:
name: "변수명"
@@ -732,6 +772,11 @@ _pages:
name: "변수명"
text: "제목"
default: "기본값"
canvas: "캔버스"
_canvas:
id: "캔버스 ID"
width: "폭"
height: "높이"
switch: "스위치"
_switch:
name: "변수명"
@@ -757,6 +802,9 @@ _pages:
message: "눌렀을 때 표시할 페이지"
variable: "보낼 변수"
no-variable: "없음"
callAiScript: "AiScript 호출"
_callAiScript:
functionName: "함수명"
radioButton: "선택지"
_radioButton:
name: "변수명"
@@ -917,6 +965,7 @@ _pages:
_splitStrByLine:
arg1: "텍스트"
ref: "변수"
aiScriptVar: "AiScript 변수"
fn: "함수"
_fn:
slots: "슬롯"

View File

@@ -230,8 +230,8 @@ location: "位置"
theme: "主题"
themeForLightMode: "在轻便模式下使用的主题"
themeForDarkMode: "在黑暗模式下使用的主题"
light: "轻便"
dark: "黑暗"
light: "浅色"
dark: "深色"
lightThemes: "亮色主题"
darkThemes: "暗色主题"
syncDeviceDarkMode: "将黑暗模式与设备设置同步"
@@ -265,6 +265,7 @@ watch: "关注"
unwatch: "取消关注"
accept: "允许"
reject: "拒绝"
normal: "正常"
instanceName: "实例名称"
instanceDescription: "实例介绍"
maintainerName: "管理员名称"
@@ -319,6 +320,7 @@ notesAndReplies: "帖子与回复"
withFiles: "附件"
silence: "禁言"
silenceConfirm: "确认要禁言吗?"
unsilence: "解除禁言"
unsilenceConfirm: "要解除禁言吗?"
popularUsers: "热门用户"
recentlyUpdatedUsers: "最近投稿用户"
@@ -350,8 +352,6 @@ unregister: "删除账户"
passwordLessLogin: "无密码登录"
resetPassword: "重置密码"
newPasswordIs: "新的密码是「{password}」"
post: "投稿"
posted: "已投稿"
autoReloadWhenDisconnected: "断开连接时自动重新加载"
autoNoteWatch: "自动关注帖子"
autoNoteWatchDescription: "让您能够收到关于「反应」和回复其他用户的帖子的通知。"
@@ -454,6 +454,8 @@ objectStorageRegion: "可用区"
objectStorageRegionDesc: "指定一个可用区例如“xx-east-1”。 如果您的对象存储服务没有可用区概念请将其留空或填写“us-east-1”。"
objectStorageUseSSL: "使用SSL"
objectStorageUseSSLDesc: "如果不使用https进行API连接请关闭。"
objectStorageUseProxy: "使用代理"
objectStorageUseProxyDesc: "如果您不使用代理进行API连接请将其关闭。"
serverLogs: "服务器日志"
deleteAll: "删除全部"
showFixedPostForm: "在时间线顶部显示帖子表单"
@@ -466,9 +468,39 @@ details: "详情"
chooseEmoji: "选择表情符号"
unableToProcess: "操作无法完成"
recentUsed: "最近使用"
install: "安装"
uninstall: "卸载"
installedApps: "已授权的应用"
nothing: "没什么"
installedDate: "授权日期"
lastUsedDate: "最近使用"
state: "状态"
sort: "排序"
ascendingOrder: "升序"
descendingOrder: "降序"
scratchpad: "暂存器"
scratchpadDescription: "暂存器为AiScript提供了实验环境。您可以编写代码以与Misskey交互运行它并查看结果。"
output: "输出"
script: "脚本"
disablePagesScript: "禁用页面脚本"
updateRemoteUser: "更新远程用户信息"
deleteAllFiles: "删除所有文件"
deleteAllFilesConfirm: "要删除所有文件吗?"
removeAllFollowing: "取消所有关注"
removeAllFollowingDescription: "取消{host}的所有关注者。当实例不存在时执行。"
userSuspended: "该用户已被冻结。"
userSilenced: "该用户已被禁言。"
_theme:
explore: "寻找主题"
install: "安装主题"
manage: "主题管理"
code: "主题代码"
installed: "{name} 已安装"
alreadyInstalled: "此主题已经安装"
invalid: "主题格式错误"
_sfx:
note: "帖子"
noteMy: "我的笔记"
noteMy: "我的帖子"
notification: "通知"
chat: "聊天"
chatBg: "聊天背景"
@@ -550,7 +582,11 @@ _permissions:
"write:user-groups": "操作用户组"
_auth:
shareAccess: "您要授权允许“{name}”访问您的帐户吗?"
shareAccessAsk: "您确定要授权此应用程序访问您的帐户吗?"
permissionAsk: "这个应用程序需要以下权限"
pleaseGoBack: "请返回到应用程序"
callback: "回到应用程序"
denied: "拒绝访问"
_antennaSources:
all: "所有帖子"
homeTimeline: "已关注用户的帖子"
@@ -654,15 +690,15 @@ _charts:
_instanceCharts:
requests: "请求"
users: "用户数量:增加/减少"
usersTotal: "用户总"
usersTotal: "用户总"
notes: "帖子:增加/减少"
notesTotal: "帖子:总数"
notesTotal: "帖子总计"
ff: "关注/被关注:数量变化"
ffTotal: "关注/被关注:总数"
ffTotal: "关注/被关注者总计"
cacheSize: "缓存大小:增加/减少"
cacheSizeTotal: "合计缓存大小"
cacheSizeTotal: "缓存大小总计"
files: "文件总数增减"
filesTotal: "合计文件总数"
filesTotal: "文件数总计"
_timelines:
home: "首页"
local: "本地"
@@ -717,6 +753,8 @@ _pages:
post: "投稿窗口"
_post:
text: "内容"
attachCanvasImage: "附加画布图像"
canvasId: "画布ID"
textInput: "文本输入"
_textInput:
name: "变量名"
@@ -732,6 +770,11 @@ _pages:
name: "变量名"
text: "标题"
default: "默认值"
canvas: "画布"
_canvas:
id: "画布ID"
width: "宽度"
height: "高度"
switch: "开关"
_switch:
name: "变量名"
@@ -757,6 +800,9 @@ _pages:
message: "按下时显示的消息"
variable: "发送的变量"
no-variable: "空"
callAiScript: "调用AiScript"
_callAiScript:
functionName: "函数名"
radioButton: "选择项"
_radioButton:
name: "变量名"
@@ -917,6 +963,7 @@ _pages:
_splitStrByLine:
arg1: "文本"
ref: "变量"
aiScriptVar: "AiScript变量"
fn: "函数"
_fn:
slots: "槽函数"

View File

@@ -256,8 +256,6 @@ userList: "清單"
passwordLessLogin: "設置無密碼登入"
resetPassword: "重置密碼"
newPasswordIs: "新密碼為「{password}」"
post: "投稿"
posted: "投稿完成"
autoReloadWhenDisconnected: "和伺服器斷線時自動重新載入"
autoNoteWatch: "自動關注筆記"
autoNoteWatchDescription: "收到反應或回覆過的筆記的通知"

View File

@@ -0,0 +1,36 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class miauth1585361548360 implements MigrationInterface {
name = 'miauth1585361548360'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "access_token" ADD "lastUsedAt" TIMESTAMP WITH TIME ZONE DEFAULT null`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ADD "session" character varying(128) DEFAULT null`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ADD "name" character varying(128) DEFAULT null`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ADD "description" character varying(512) DEFAULT null`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ADD "iconUrl" character varying(512) DEFAULT null`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ADD "permission" character varying(64) array NOT NULL DEFAULT '{}'::varchar[]`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ADD "fetched" boolean NOT NULL DEFAULT false`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" DROP CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560"`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "appId" DROP NOT NULL`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "appId" SET DEFAULT null`, undefined);
await queryRunner.query(`CREATE INDEX "IDX_bf3a053c07d9fb5d87317c56ee" ON "access_token" ("session") `, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ADD CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "access_token" DROP CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_bf3a053c07d9fb5d87317c56ee"`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "appId" DROP DEFAULT`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "appId" SET NOT NULL`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" ADD CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "fetched"`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "permission"`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "iconUrl"`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "description"`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "name"`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "session"`, undefined);
await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "lastUsedAt"`, undefined);
}
}

View File

@@ -0,0 +1,48 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class customNotification1585385921215 implements MigrationInterface {
name = 'customNotification1585385921215'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "notification" ADD "customBody" character varying(2048)`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ADD "customHeader" character varying(256)`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ADD "customIcon" character varying(1024)`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ADD "appAccessTokenId" character varying(32)`, undefined);
await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710"`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "notifierId" DROP NOT NULL`, undefined);
await queryRunner.query(`COMMENT ON COLUMN "notification"."notifierId" IS 'The ID of sender user of the Notification.'`, undefined);
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`, undefined);
await queryRunner.query(`CREATE TYPE "notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum" USING "type"::"text"::"notification_type_enum"`, undefined);
await queryRunner.query(`DROP TYPE "notification_type_enum_old"`, undefined);
await queryRunner.query(`COMMENT ON COLUMN "notification"."type" IS 'The type of the Notification.'`, undefined);
await queryRunner.query(`CREATE INDEX "IDX_3b4e96eec8d36a8bbb9d02aa71" ON "notification" ("notifierId") `, undefined);
await queryRunner.query(`CREATE INDEX "IDX_33f33cc8ef29d805a97ff4628b" ON "notification" ("type") `, undefined);
await queryRunner.query(`CREATE INDEX "IDX_080ab397c379af09b9d2169e5b" ON "notification" ("isRead") `, undefined);
await queryRunner.query(`CREATE INDEX "IDX_e22bf6bda77b6adc1fd9e75c8c" ON "notification" ("appAccessTokenId") `, undefined);
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710" FOREIGN KEY ("notifierId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_e22bf6bda77b6adc1fd9e75c8c9" FOREIGN KEY ("appAccessTokenId") REFERENCES "access_token"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_e22bf6bda77b6adc1fd9e75c8c9"`, undefined);
await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_e22bf6bda77b6adc1fd9e75c8c"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_080ab397c379af09b9d2169e5b"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_33f33cc8ef29d805a97ff4628b"`, undefined);
await queryRunner.query(`DROP INDEX "IDX_3b4e96eec8d36a8bbb9d02aa71"`, undefined);
await queryRunner.query(`COMMENT ON COLUMN "notification"."type" IS ''`, undefined);
await queryRunner.query(`CREATE TYPE "notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited')`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum_old" USING "type"::"text"::"notification_type_enum_old"`, undefined);
await queryRunner.query(`DROP TYPE "notification_type_enum"`, undefined);
await queryRunner.query(`ALTER TYPE "notification_type_enum_old" RENAME TO "notification_type_enum"`, undefined);
await queryRunner.query(`COMMENT ON COLUMN "notification"."notifierId" IS ''`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "notifierId" SET NOT NULL`, undefined);
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710" FOREIGN KEY ("notifierId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "appAccessTokenId"`, undefined);
await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "customIcon"`, undefined);
await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "customHeader"`, undefined);
await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "customBody"`, undefined);
}
}

View File

@@ -0,0 +1,15 @@
/* tslint:disable:quotemark class-name indent */
import {MigrationInterface, QueryRunner} from "typeorm";
export class apUrl1585772678853 implements MigrationInterface {
name = 'apUrl1585772678853'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "note" ADD "url" character varying(512)`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "url"`, undefined);
}
}

View File

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

View File

@@ -0,0 +1,12 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class remoteReaction1586641139527 implements MigrationInterface {
name = 'remoteReaction1586641139527'
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(260)`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(130)`, undefined);
}
}

View File

@@ -0,0 +1,14 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class pageAiScript1586708940386 implements MigrationInterface {
name = 'pageAiScript1586708940386'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "page" ADD "script" character varying(16384) NOT NULL DEFAULT ''`, undefined);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "script"`, undefined);
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.24.2",
"version": "12.36.0",
"codename": "indigo",
"repository": {
"type": "git",
@@ -11,11 +11,13 @@
"private": true,
"scripts": {
"start": "node ./index.js",
"start-product": "cross-env NODE_ENV=production node ./index.js",
"init": "npm run migrate",
"ormconfig": "node ./built/ormconfig.js",
"migrate": "ts-node ./node_modules/typeorm/cli.js migration:run",
"migrateandstart": "npm run migrate && npm run start",
"build": "webpack && gulp build",
"build-product": "cross-env NODE_ENV=production webpack && gulp build",
"webpack": "webpack",
"watch": "webpack --watch",
"gulp": "gulp build",
@@ -30,18 +32,19 @@
"lodash": "^4.17.13"
},
"dependencies": {
"@elastic/elasticsearch": "7.6.0",
"@fortawesome/fontawesome-svg-core": "1.2.27",
"@fortawesome/free-brands-svg-icons": "5.12.1",
"@fortawesome/free-regular-svg-icons": "5.12.1",
"@fortawesome/free-solid-svg-icons": "5.12.1",
"@elastic/elasticsearch": "7.6.1",
"@fortawesome/fontawesome-svg-core": "1.2.28",
"@fortawesome/free-brands-svg-icons": "5.13.0",
"@fortawesome/free-regular-svg-icons": "5.13.0",
"@fortawesome/free-solid-svg-icons": "5.13.0",
"@fortawesome/vue-fontawesome": "0.1.9",
"@juggle/resize-observer": "3.0.2",
"@juggle/resize-observer": "3.1.3",
"@koa/cors": "3.0.0",
"@koa/multer": "2.0.2",
"@koa/router": "8.0.8",
"@syuilo/aiscript": "0.5.0",
"@types/bcryptjs": "2.4.2",
"@types/bull": "3.12.0",
"@types/bull": "3.12.2",
"@types/cbor": "5.0.0",
"@types/dateformat": "3.0.1",
"@types/double-ended-queue": "2.1.1",
@@ -51,10 +54,10 @@
"@types/gulp-rename": "0.0.33",
"@types/gulp-replace": "0.0.31",
"@types/is-url": "1.2.28",
"@types/js-yaml": "3.12.2",
"@types/jsdom": "12.2.4",
"@types/js-yaml": "3.12.3",
"@types/jsdom": "16.2.1",
"@types/katex": "0.11.0",
"@types/koa": "2.11.1",
"@types/koa": "2.11.3",
"@types/koa-bodyparser": "4.3.0",
"@types/koa-compress": "2.0.9",
"@types/koa-cors": "0.0.0",
@@ -68,8 +71,9 @@
"@types/koa__router": "8.0.2",
"@types/lolex": "5.1.0",
"@types/markdown-it": "0.0.9",
"@types/mocha": "7.0.1",
"@types/node": "13.7.1",
"@types/mocha": "7.0.2",
"@types/node": "13.13.0",
"@types/node-fetch": "2.5.6",
"@types/nodemailer": "6.4.0",
"@types/nprogress": "0.2.0",
"@types/oauth": "0.9.1",
@@ -80,10 +84,8 @@
"@types/qrcode": "1.3.4",
"@types/random-seed": "0.3.3",
"@types/ratelimiter": "2.1.28",
"@types/redis": "2.8.15",
"@types/redis": "2.8.18",
"@types/rename": "1.0.1",
"@types/request": "2.48.4",
"@types/request-promise-native": "1.0.17",
"@types/request-stats": "3.0.0",
"@types/rimraf": "2.0.3",
"@types/seedrandom": "2.4.28",
@@ -93,47 +95,46 @@
"@types/systeminformation": "3.54.1",
"@types/tinycolor2": "1.4.2",
"@types/tmp": "0.1.0",
"@types/uuid": "3.4.7",
"@types/uuid": "7.0.2",
"@types/web-push": "3.3.0",
"@types/webpack": "4.41.6",
"@types/webpack": "4.41.10",
"@types/webpack-stream": "3.2.10",
"@types/websocket": "1.0.0",
"@types/ws": "7.2.1",
"@typescript-eslint/parser": "2.19.2",
"agentkeepalive": "4.1.0",
"animejs": "3.1.0",
"apexcharts": "3.15.6",
"@types/ws": "7.2.4",
"@typescript-eslint/parser": "2.28.0",
"abort-controller": "3.0.0",
"apexcharts": "3.18.1",
"autobind-decorator": "2.4.0",
"autosize": "4.0.2",
"autwh": "0.1.0",
"aws-sdk": "2.617.0",
"aws-sdk": "2.658.0",
"bcryptjs": "2.4.3",
"bull": "3.12.1",
"bull": "3.13.0",
"cafy": "15.2.1",
"cbor": "5.0.1",
"cbor": "5.0.2",
"chai": "4.2.0",
"chalk": "3.0.0",
"chalk": "4.0.0",
"chart.js": "2.9.3",
"cli-highlight": "2.1.4",
"commander": "4.1.1",
"content-disposition": "0.5.3",
"crc-32": "1.2.0",
"css-loader": "3.4.2",
"css-loader": "3.5.2",
"cssnano": "4.1.10",
"dateformat": "3.0.3",
"diskusage": "1.1.3",
"double-ended-queue": "2.1.0-0",
"eslint": "6.8.0",
"eslint-plugin-vue": "6.1.2",
"eslint-plugin-vue": "6.2.2",
"eventemitter3": "4.0.0",
"feed": "4.1.0",
"fibers": "4.0.2",
"file-type": "14.1.2",
"file-type": "14.1.4",
"fluent-ffmpeg": "2.1.2",
"glob": "7.1.6",
"gulp": "4.0.2",
"gulp-clean-css": "4.2.0",
"gulp-dart-sass": "0.9.1",
"gulp-clean-css": "4.3.0",
"gulp-dart-sass": "1.0.1",
"gulp-mocha": "7.0.2",
"gulp-rename": "2.0.0",
"gulp-replace": "1.0.0",
@@ -143,21 +144,22 @@
"gulp-typescript": "5.0.1",
"hard-source-webpack-plugin": "0.13.1",
"html-minifier": "4.0.0",
"http-signature": "1.3.1",
"http-proxy-agent": "4.0.1",
"http-signature": "1.3.4",
"https-proxy-agent": "5.0.0",
"insert-text-at-cursor": "0.3.0",
"is-root": "2.1.0",
"is-svg": "4.2.1",
"js-yaml": "3.13.1",
"jsdom": "16.1.0",
"json5": "2.1.1",
"jsdom": "16.2.2",
"json5": "2.1.3",
"json5-loader": "3.0.0",
"jsrsasign": "8.0.12",
"jsrsasign": "8.0.15",
"katex": "0.11.1",
"koa": "2.11.0",
"koa-bodyparser": "4.2.1",
"koa-compress": "3.0.0",
"koa-favicon": "2.0.1",
"koa-bodyparser": "4.3.0",
"koa-compress": "3.1.0",
"koa-favicon": "2.1.0",
"koa-json-body": "5.3.0",
"koa-logger": "3.2.1",
"koa-mount": "4.0.0",
@@ -165,102 +167,99 @@
"koa-slow": "2.1.0",
"koa-views": "6.2.1",
"langmap": "0.0.16",
"loader-utils": "1.2.3",
"lolex": "5.1.2",
"lookup-dns-cache": "2.1.0",
"markdown-it": "10.0.0",
"mocha": "7.0.1",
"markdown-it-anchor": "5.2.7",
"mocha": "7.1.1",
"moji": "0.5.1",
"ms": "2.1.2",
"multer": "1.4.2",
"nested-property": "1.0.4",
"node-fetch": "2.6.0",
"nodemailer": "6.4.2",
"nodemailer": "6.4.6",
"nprogress": "0.2.0",
"object-assign-deep": "0.4.0",
"os-utils": "0.0.14",
"parse5": "5.1.1",
"parsimmon": "1.13.0",
"pg": "7.18.1",
"pg": "8.0.2",
"portal-vue": "2.1.7",
"portscanner": "2.2.0",
"postcss-loader": "3.0.0",
"prismjs": "1.19.0",
"prismjs": "1.20.0",
"probe-image-size": "5.0.0",
"progress-bar-webpack-plugin": "2.1.0",
"promise-limit": "2.7.0",
"promise-sequential": "1.1.1",
"pug": "2.0.4",
"punycode": "2.1.1",
"pureimage": "0.1.6",
"pureimage": "0.2.1",
"qrcode": "1.4.4",
"random-seed": "0.3.0",
"randomcolor": "0.5.4",
"ratelimiter": "3.4.0",
"ratelimiter": "3.4.1",
"recaptcha-promise": "0.1.3",
"reconnecting-websocket": "4.4.0",
"redis": "3.0.2",
"redis-lock": "0.1.4",
"reflect-metadata": "0.1.13",
"rename": "1.0.4",
"request": "2.88.2",
"request-promise-native": "1.0.8",
"request-stats": "3.0.0",
"require-all": "3.0.0",
"rimraf": "3.0.2",
"rndstr": "1.0.0",
"s-age": "1.1.2",
"sass": "1.25.0",
"sass": "1.26.3",
"sass-loader": "8.0.2",
"seedrandom": "3.0.5",
"sharp": "0.24.0",
"sharp": "0.25.2",
"showdown": "1.9.1",
"showdown-highlightjs-extension": "0.1.2",
"speakeasy": "2.0.0",
"stringz": "2.0.0",
"style-loader": "1.1.3",
"stringz": "2.1.0",
"style-loader": "1.1.4",
"summaly": "2.3.1",
"syslog-pro": "1.0.0",
"systeminformation": "4.21.2",
"systeminformation": "4.23.3",
"syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "2.3.4",
"textarea-caret": "3.1.0",
"three": "0.113.2",
"three": "0.115.0",
"tinycolor2": "1.4.1",
"tmp": "0.1.0",
"ts-loader": "6.2.1",
"ts-node": "8.6.2",
"tslint": "6.0.0",
"ts-loader": "6.2.2",
"ts-node": "8.8.2",
"tslint": "6.1.1",
"tslint-sonarts": "1.9.0",
"typeorm": "0.2.22",
"typescript": "3.7.5",
"typeorm": "0.2.24",
"typescript": "3.8.3",
"ulid": "2.3.0",
"url-loader": "3.0.0",
"uuid": "3.4.0",
"uuid": "7.0.3",
"v-animate-css": "0.0.3",
"v-debounce": "0.1.2",
"vue": "2.6.11",
"vue-color": "2.7.0",
"vue-color": "2.7.1",
"vue-content-loading": "1.6.0",
"vue-cropperjs": "4.0.1",
"vue-i18n": "8.15.3",
"vue-cropperjs": "4.1.0",
"vue-i18n": "8.17.2",
"vue-json-pretty": "1.6.3",
"vue-loader": "15.9.0",
"vue-loader": "15.9.1",
"vue-marquee-text-component": "1.1.1",
"vue-meta": "2.3.2",
"vue-prism-component": "1.1.1",
"vue-router": "3.1.5",
"vue-meta": "2.3.3",
"vue-prism-component": "1.2.0",
"vue-prism-editor": "0.5.1",
"vue-router": "3.1.6",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.4.5",
"vue-svg-inline-loader": "1.5.0",
"vue-template-compiler": "2.6.11",
"vuedraggable": "2.23.2",
"vuex": "3.1.2",
"vuex-persistedstate": "2.7.1",
"vuex": "3.1.3",
"vuex-persistedstate": "3.0.1",
"web-push": "3.4.3",
"webpack": "4.41.6",
"webpack": "5.0.0-beta.15",
"webpack-cli": "3.3.11",
"websocket": "1.0.31",
"ws": "7.2.1",
"ws": "7.2.3",
"xev": "2.0.1"
},
"devDependencies": {

View File

@@ -99,7 +99,7 @@ async function isPortAvailable(port: number): Promise<boolean> {
function showEnvironment(): void {
const env = process.env.NODE_ENV;
const logger = bootLogger.createSubLogger('env');
logger.info(typeof env == 'undefined' ? 'NODE_ENV is not set' : `NODE_ENV: ${env}`);
logger.info(typeof env === 'undefined' ? 'NODE_ENV is not set' : `NODE_ENV: ${env}`);
if (env !== 'production') {
logger.warn('The environment is not in production mode.');

View File

@@ -156,14 +156,13 @@
<script lang="ts">
import Vue from 'vue';
import { faGripVertical, faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faGamepad, faServer, faFileAlt, faSatellite, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { faTerminal, faGripVertical, faChevronLeft, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faListUl, faPlus, faUserClock, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faGamepad, faServer, faFileAlt, faSatellite, faInfoCircle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
import { faBell, faEnvelope, faLaugh, faComments } from '@fortawesome/free-regular-svg-icons';
import { ResizeObserver } from '@juggle/resize-observer';
import { v4 as uuid } from 'uuid';
import i18n from './i18n';
import { host, instanceName } from './config';
import { search } from './scripts/search';
import MkToast from './components/toast.vue';
const DESKTOP_THRESHOLD = 1100;
@@ -310,7 +309,7 @@ export default Vue.extend({
title: this.$t('search'),
input: true
}).then(async ({ canceled, result: query }) => {
if (canceled || query == null || query == '') return;
if (canceled || query == null || query === '') return;
this.searching = true;
search(this, query).finally(() => {
@@ -320,7 +319,7 @@ export default Vue.extend({
},
searchKeypress(e) {
if (e.keyCode == 13) {
if (e.keyCode === 13) {
this.searchWait = true;
search(this, this.searchQuery).finally(() => {
this.searchWait = false;
@@ -470,6 +469,11 @@ export default Vue.extend({
to: '/games',
icon: faGamepad,
}, null] : []), {
type: 'link',
text: this.$t('scratchpad'),
to: '/scratchpad',
icon: faTerminal,
}, null, {
type: 'link',
text: this.$t('help'),
to: '/docs',
@@ -530,14 +534,14 @@ export default Vue.extend({
});
},
onNotification(notification) {
async onNotification(notification) {
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
if (true) {
this.$root.stream.send('readNotification', {
id: notification.id
});
this.$root.new(MkToast, {
this.$root.new(await import('./components/toast.vue').then(m => m.default), {
notification
});
}
@@ -895,24 +899,25 @@ export default Vue.extend({
color: var(--navActive);
}
&:first-child {
&:first-child, &:last-child {
position: sticky;
z-index: 1;
top: 0;
padding-top: 8px;
padding-bottom: 8px;
background: var(--wboyroyc);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
&:first-child {
top: 0;
margin-bottom: 16px;
background: var(--navBg);
border-bottom: solid 1px var(--divider);
}
&:last-child {
position: sticky;
bottom: 0;
padding-top: 8px;
padding-bottom: 8px;
margin-top: 16px;
background: var(--navBg);
border-top: solid 1px var(--divider);
}
@@ -974,6 +979,10 @@ export default Vue.extend({
&:not(.naked) {
background: var(--pageBg);
}
&.naked {
background: var(--bg);
}
}
}

View File

@@ -18,7 +18,7 @@
</style>
</head>
<body>
<redoc spec-url='/api.json'></redoc>
<redoc spec-url="/api.json" expand-responses="200" expand-single-schema-field="true"></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
</body>
</html>

View File

@@ -1,8 +1,8 @@
<template>
<component :is="$store.state.device.animation ? 'transition-group' : 'div'" class="sqadhkmv" name="list" tag="div" :data-direction="direction" :data-reversed="reversed ? 'true' : 'false'">
<template v-for="(item, i) in items">
<slot :item="item" :i="i"></slot>
<div class="separator" :key="item.id + '_date'" v-if="showDate(i, item)">
<slot :item="item"></slot>
<div class="separator" v-if="showDate(i, item)" :key="item.id + '_date'">
<p class="date">
<span><fa class="icon" :icon="faAngleUp"/>{{ getDateText(item.createdAt) }}</span>
<span>{{ getDateText(items[i + 1].createdAt) }}<fa class="icon" :icon="faAngleDown"/></span>

View File

@@ -1,8 +1,11 @@
<template>
<x-window ref="window" @closed="() => { $emit('closed'); destroyDom(); }" :with-ok-button="true" :ok-button-disabled="selected.length === 0" @ok="ok()">
<template #header>{{ multiple ? $t('selectFiles') : $t('selectFile') }}<span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ selected.length | number }})</span></template>
<x-window ref="window" :width="800" :height="500" @closed="() => { $emit('closed'); destroyDom(); }" :with-ok-button="true" :ok-button-disabled="(type === 'file') && (selected.length === 0)" @ok="ok()">
<template #header>
{{ multiple ? ((type === 'file') ? $t('selectFiles') : $t('selectFolders')) : ((type === 'file') ? $t('selectFile') : $t('selectFolder')) }}
<span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ selected.length | number }})</span>
</template>
<div>
<x-drive :multiple="multiple" @change-selection="onChangeSelection" :select-mode="true"/>
<x-drive :multiple="multiple" @change-selection="onChangeSelection" :select="type"/>
</div>
</x-window>
</template>
@@ -25,7 +28,7 @@ export default Vue.extend({
type: {
type: String,
required: false,
default: undefined
default: 'file'
},
multiple: {
type: Boolean,
@@ -45,8 +48,8 @@ export default Vue.extend({
this.$refs.window.close();
},
onChangeSelection(files) {
this.selected = files;
onChangeSelection(xs) {
this.selected = xs;
}
}
});

View File

@@ -42,11 +42,20 @@ import { faDownload, faLink, faICursor, faTrashAlt } from '@fortawesome/free-sol
export default Vue.extend({
i18n,
components: {
XFileThumbnail
},
props: {
file: {
type: Object,
required: true,
},
isSelected: {
type: Boolean,
required: false,
default: false,
},
selectMode: {
type: Boolean,
required: false,
@@ -54,10 +63,6 @@ export default Vue.extend({
}
},
components: {
XFileThumbnail
},
data() {
return {
isDragging: false
@@ -65,12 +70,10 @@ export default Vue.extend({
},
computed: {
// TODO: parentへの参照を無くす
browser(): any {
return this.$parent;
},
isSelected(): boolean {
return this.browser.selectedFiles.some(f => f.id == this.file.id);
},
title(): string {
return `${this.file.name}\n${this.file.type} ${Vue.filter('bytes')(this.file.size)}`;
}
@@ -79,7 +82,7 @@ export default Vue.extend({
methods: {
onClick(ev) {
if (this.selectMode) {
this.browser.chooseFile(this.file);
this.$emit('chosen', this.file);
} else {
this.$root.menu({
items: [{

View File

@@ -21,6 +21,7 @@
<p class="upload" v-if="$store.state.settings.uploadFolder == folder.id">
{{ $t('uploadFolder') }}
</p>
<button v-if="selectMode" class="checkbox _button" :class="{ checked: isSelected }" @click.prevent.stop="checkboxClicked"></button>
</div>
</template>
@@ -36,6 +37,16 @@ export default Vue.extend({
folder: {
type: Object,
required: true,
},
isSelected: {
type: Boolean,
required: false,
default: false,
},
selectMode: {
type: Boolean,
required: false,
default: false,
}
},
@@ -56,7 +67,12 @@ export default Vue.extend({
return this.folder.name;
}
},
methods: {
checkboxClicked(e) {
this.$emit('chosen', this.folder);
},
onClick() {
this.browser.move(this.folder);
},
@@ -241,10 +257,24 @@ export default Vue.extend({
cursor: pointer;
}
* {
*:not(.checkbox) {
pointer-events: none;
}
> .checkbox {
position: absolute;
bottom: 8px;
right: 8px;
width: 16px;
height: 16px;
background: #fff;
border: solid 1px #000;
&.checked {
background: var(--accent);
}
}
&[data-draghover] {
&:after {
content: "";

View File

@@ -3,9 +3,9 @@
<nav>
<div class="path" @contextmenu.prevent.stop="() => {}">
<x-nav-folder :class="{ current: folder == null }"/>
<template v-for="folder in hierarchyFolders">
<span class="separator"><fa :icon="faAngleRight"/></span>
<x-nav-folder :folder="folder" :key="folder.id"/>
<template v-for="f in hierarchyFolders">
<span class="separator" :key="f.id + ':separator'"><fa :icon="faAngleRight"/></span>
<x-nav-folder :folder="f" :key="f.id"/>
</template>
<span class="separator" v-if="folder != null"><fa :icon="faAngleRight"/></span>
<span class="folder current" v-if="folder != null">{{ folder.name }}</span>
@@ -20,15 +20,15 @@
>
<div class="contents" ref="contents">
<div class="folders" ref="foldersContainer" v-if="folders.length > 0">
<x-folder v-for="folder in folders" :key="folder.id" class="folder" :folder="folder"/>
<x-folder v-for="f in folders" :key="f.id" class="folder" :folder="f" :select-mode="select === 'folder'" :is-selected="selectedFolders.some(x => x.id === f.id)" @chosen="chooseFolder"/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div class="padding" v-for="n in 16"></div>
<div class="padding" v-for="(n, i) in 16" :key="i"></div>
<mk-button v-if="moreFolders">{{ $t('loadMore') }}</mk-button>
</div>
<div class="files" ref="filesContainer" v-if="files.length > 0">
<x-file v-for="file in files" :key="file.id" class="file" :file="file" :select-mode="selectMode"/>
<x-file v-for="file in files" :key="file.id" class="file" :file="file" :select-mode="select === 'file'" :is-selected="selectedFiles.some(x => x.id === file.id)" @chosen="chooseFile"/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div class="padding" v-for="n in 16"></div>
<div class="padding" v-for="(n, i) in 16" :key="i"></div>
<mk-button v-if="moreFiles" @click="fetchMoreFiles">{{ $t('loadMore') }}</mk-button>
</div>
<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching">
@@ -81,10 +81,10 @@ export default Vue.extend({
required: false,
default: false
},
selectMode: {
type: Boolean,
select: {
type: String,
required: false,
default: false
default: null
}
},
@@ -102,6 +102,7 @@ export default Vue.extend({
moreFolders: false,
hierarchyFolders: [],
selectedFiles: [],
selectedFolders: [],
uploadings: [],
connection: null,
@@ -392,6 +393,25 @@ export default Vue.extend({
}
},
chooseFolder(folder) {
const isAlreadySelected = this.selectedFolders.some(f => f.id == folder.id);
if (this.multiple) {
if (isAlreadySelected) {
this.selectedFolders = this.selectedFolders.filter(f => f.id != folder.id);
} else {
this.selectedFolders.push(folder);
}
this.$emit('change-selection', this.selectedFolders);
} else {
if (isAlreadySelected) {
this.$emit('selected', folder);
} else {
this.selectedFolders = [folder];
this.$emit('change-selection', [folder]);
}
}
},
move(target) {
if (target == null) {
this.goRoot();

View File

@@ -1,6 +1,6 @@
<template>
<div class="mjndxjcg _panel">
<img src="https://xn--931a.moe/assets/error.png" class="_ghost"/>
<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
<p><fa :icon="faExclamationTriangle"/> {{ $t('error') }}</p>
<mk-button @click="() => $emit('retry')" class="button">{{ $t('retry') }}</mk-button>
</div>

View File

@@ -14,6 +14,7 @@ import Vue from 'vue';
import { faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { url as local } from '../config';
import MkUrlPreview from './url-preview-popup.vue';
import { isDeviceTouch } from '../scripts/is-device-touch';
export default Vue.extend({
props: {
@@ -61,11 +62,13 @@ export default Vue.extend({
}
},
onMouseover() {
if (isDeviceTouch()) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.showTimer = setTimeout(this.showPreview, 500);
},
onMouseleave() {
if (isDeviceTouch()) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = setTimeout(this.closePreview, 500);

View File

@@ -1,23 +1,26 @@
<template>
<div class="qjewsnkgzzxlxtzncydssfbgjibiehcy" v-if="image.isSensitive && hide && !$store.state.device.alwaysShowNsfw" @click="hide = false">
<div class="qjewsnkgzzxlxtzncydssfbgjibiehcy" v-if="hide" @click="hide = false">
<div>
<b><fa :icon="faExclamationTriangle"/> {{ $t('sensitive') }}</b>
<span>{{ $t('clickToShow') }}</span>
</div>
</div>
<a class="gqnyydlzavusgskkfvwvjiattxdzsqlf" v-else
:href="image.url"
:style="style"
:title="image.name"
@click.prevent="onClick"
>
<div v-if="image.type === 'image/gif'">GIF</div>
</a>
<div class="gqnyydlzavusgskkfvwvjiattxdzsqlf" v-else>
<i><fa :icon="faEyeSlash" @click="hide = true"/></i>
<a
:href="image.url"
:style="style"
:title="image.name"
@click.prevent="onClick"
>
<div v-if="image.type === 'image/gif'">GIF</div>
</a>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { faExclamationTriangle, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
import { getStaticImageUrl } from '../scripts/get-static-image-url';
import ImageViewer from './image-viewer.vue';
@@ -36,7 +39,8 @@ export default Vue.extend({
data() {
return {
hide: true,
faExclamationTriangle
faExclamationTriangle,
faEyeSlash
};
},
computed: {
@@ -59,6 +63,9 @@ export default Vue.extend({
};
}
},
created() {
this.hide = this.image.isSensitive && !this.$store.state.device.alwaysShowNsfw;
},
methods: {
onClick() {
if (this.$store.state.device.imageNewTab) {
@@ -78,28 +85,47 @@ export default Vue.extend({
<style lang="scss" scoped>
.gqnyydlzavusgskkfvwvjiattxdzsqlf {
display: block;
cursor: zoom-in;
overflow: hidden;
width: 100%;
height: 100%;
background-position: center;
background-size: contain;
background-repeat: no-repeat;
position: relative;
> div {
background-color: var(--fg);
> i {
display: block;
position: absolute;
border-radius: 6px;
background-color: var(--fg);
color: var(--accentLighten);
display: inline-block;
font-size: 14px;
font-weight: bold;
left: 12px;
opacity: .5;
padding: 0 6px;
padding: 3px 6px;
text-align: center;
cursor: pointer;
top: 12px;
pointer-events: none;
right: 12px;
}
> a {
display: block;
cursor: zoom-in;
overflow: hidden;
width: 100%;
height: 100%;
background-position: center;
background-size: contain;
background-repeat: no-repeat;
> div {
background-color: var(--fg);
border-radius: 6px;
color: var(--accentLighten);
display: inline-block;
font-size: 14px;
font-weight: bold;
left: 12px;
opacity: .5;
padding: 0 6px;
text-align: center;
top: 12px;
pointer-events: none;
}
}
}

View File

@@ -34,9 +34,7 @@ export default Vue.extend({
default: false
},
// specify the parent element
parentElement: {
type: Object
}
parentElement: {}
},
data() {
return {
@@ -69,7 +67,7 @@ export default Vue.extend({
if (this.$refs.gridOuter) {
let height = 287;
const parent = this.$props.parentElement || this.$parent.$el;
const parent = this.parentElement || this.$parent.$el;
if (this.$refs.gridOuter.clientHeight) {
height = this.$refs.gridOuter.clientHeight;
@@ -83,6 +81,11 @@ export default Vue.extend({
}
});
}
},
watch: {
parentElement() {
this.size();
}
}
});
</script>

View File

@@ -1,24 +1,28 @@
<template>
<div class="icozogqfvdetwohsdglrbswgrejoxbdj" v-if="video.isSensitive && hide && !$store.state.device.alwaysShowNsfw" @click="hide = false">
<div class="icozogqfvdetwohsdglrbswgrejoxbdj" v-if="hide" @click="hide = false">
<div>
<b><fa icon="exclamation-triangle"/> {{ $t('sensitive') }}</b>
<b><fa :icon="faExclamationTriangle"/> {{ $t('sensitive') }}</b>
<span>{{ $t('clickToShow') }}</span>
</div>
</div>
<a class="kkjnbbplepmiyuadieoenjgutgcmtsvu" v-else
:href="video.url"
rel="nofollow noopener"
target="_blank"
:style="imageStyle"
:title="video.name"
>
<fa :icon="faPlayCircle"/>
</a>
<div class="kkjnbbplepmiyuadieoenjgutgcmtsvu" v-else>
<i><fa :icon="faEyeSlash" @click="hide = true"/></i>
<a
:href="video.url"
rel="nofollow noopener"
target="_blank"
:style="imageStyle"
:title="video.name"
>
<fa :icon="faPlayCircle"/>
</a>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faPlayCircle } from '@fortawesome/free-regular-svg-icons';
import { faExclamationTriangle, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
import i18n from '../i18n';
export default Vue.extend({
@@ -32,7 +36,9 @@ export default Vue.extend({
data() {
return {
hide: true,
faPlayCircle
faPlayCircle,
faExclamationTriangle,
faEyeSlash
};
},
computed: {
@@ -41,22 +47,44 @@ export default Vue.extend({
'background-image': `url(${this.video.thumbnailUrl})`
};
}
}
},
created() {
this.hide = this.video.isSensitive && !this.$store.state.device.alwaysShowNsfw;
},
});
</script>
<style lang="scss" scoped>
.kkjnbbplepmiyuadieoenjgutgcmtsvu {
display: flex;
justify-content: center;
align-items: center;
position: relative;
font-size: 3.5em;
overflow: hidden;
background-position: center;
background-size: cover;
width: 100%;
height: 100%;
> i {
display: block;
position: absolute;
border-radius: 6px;
background-color: var(--fg);
color: var(--accentLighten);
font-size: 14px;
opacity: .5;
padding: 3px 6px;
text-align: center;
cursor: pointer;
top: 12px;
right: 12px;
}
> a {
display: flex;
justify-content: center;
align-items: center;
font-size: 3.5em;
overflow: hidden;
background-position: center;
background-size: cover;
width: 100%;
height: 100%;
}
}
.icozogqfvdetwohsdglrbswgrejoxbdj {

View File

@@ -33,7 +33,7 @@
<mk-avatar class="avatar" :user="appearNote.user"/>
<div class="main">
<x-note-header class="header" :note="appearNote" :mini="true"/>
<div class="body" v-if="appearNote.deletedAt == null">
<div class="body" v-if="appearNote.deletedAt == null" ref="noteBody">
<p v-if="appearNote.cw != null" class="cw">
<mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$store.state.i" :custom-emojis="appearNote.emojis" />
<x-cw-button v-model="showContent" :note="appearNote"/>
@@ -46,7 +46,7 @@
<a class="rp" v-if="appearNote.renote != null">RN:</a>
</div>
<div class="files" v-if="appearNote.files.length > 0">
<x-media-list :media-list="appearNote.files"/>
<x-media-list :media-list="appearNote.files" :parent-element="noteBody"/>
</div>
<x-poll v-if="appearNote.poll" :note="appearNote" ref="pollViewer"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url" :compact="true" class="url-preview"/>
@@ -142,6 +142,7 @@ export default Vue.extend({
replies: [],
showContent: false,
hideThisNote: false,
noteBody: this.$refs.noteBody,
faEdit, faBolt, faTimes, faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan
};
},
@@ -254,6 +255,8 @@ export default Vue.extend({
if (this.$store.getters.isSignedIn) {
this.connection.on('_connected_', this.onStreamConnected);
}
this.noteBody = this.$refs.noteBody
},
beforeDestroy() {
@@ -301,6 +304,14 @@ export default Vue.extend({
case 'reacted': {
const reaction = body.reaction;
if (body.emoji) {
const emojis = this.appearNote.emojis || [];
if (!emojis.includes(body.emoji)) {
emojis.push(body.emoji);
Vue.set(this.appearNote, 'emojis', emojis);
}
}
if (this.appearNote.reactions == null) {
Vue.set(this.appearNote, 'reactions', {});
}
@@ -517,11 +528,11 @@ export default Vue.extend({
icon: faLink,
text: this.$t('copyLink'),
action: this.copyLink
}, this.appearNote.uri ? {
}, (this.appearNote.url || this.appearNote.uri) ? {
icon: faExternalLinkSquareAlt,
text: this.$t('showOnRemote'),
action: () => {
window.open(this.appearNote.uri, '_blank');
window.open(this.appearNote.url || this.appearNote.uri, '_blank');
}
} : undefined,
null,
@@ -561,13 +572,13 @@ export default Vue.extend({
}]
: []
),
...(this.appearNote.userId == this.$store.state.i.id ? [
...(this.appearNote.userId == this.$store.state.i.id || this.$store.state.i.isModerator || this.$store.state.i.isAdmin ? [
null,
{
this.appearNote.userId == this.$store.state.i.id ? {
icon: faEdit,
text: this.$t('deleteAndEdit'),
action: this.delEdit
},
} : undefined,
{
icon: faTrashAlt,
text: this.$t('delete'),
@@ -585,11 +596,11 @@ export default Vue.extend({
icon: faLink,
text: this.$t('copyLink'),
action: this.copyLink
}, this.appearNote.uri ? {
}, (this.appearNote.url || this.appearNote.uri) ? {
icon: faExternalLinkSquareAlt,
text: this.$t('showOnRemote'),
action: () => {
window.open(this.appearNote.uri, '_blank');
window.open(this.appearNote.url || this.appearNote.uri, '_blank');
}
} : undefined]
.filter(x => x !== undefined);

View File

@@ -1,7 +1,7 @@
<template>
<div class="mk-notes" v-size="[{ max: 500 }]">
<div class="empty" v-if="empty">
<img src="https://xn--931a.moe/assets/info.png" class="_ghost"/>
<div class="_fullinfo" v-if="empty">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $t('noNotes') }}</div>
</div>
@@ -90,18 +90,6 @@ export default Vue.extend({
<style lang="scss" scoped>
.mk-notes {
> .empty {
padding: 32px;
text-align: center;
> img {
vertical-align: bottom;
height: 128px;
margin-bottom: 16px;
border-radius: 16px;
}
}
> .notes {
> ::v-deep *:not(:last-child) {
margin-bottom: var(--marginFull);

View File

@@ -1,22 +1,24 @@
<template>
<div class="mk-notification" :class="notification.type" v-size="[{ max: 500 }, { max: 600 }]">
<div class="head">
<mk-avatar class="avatar" :user="notification.user"/>
<div class="icon" :class="notification.type">
<mk-avatar v-if="notification.user" class="icon" :user="notification.user"/>
<img v-else class="icon" :src="notification.icon" alt=""/>
<div class="sub-icon" :class="notification.type">
<fa :icon="faPlus" v-if="notification.type === 'follow'"/>
<fa :icon="faClock" v-if="notification.type === 'receiveFollowRequest'"/>
<fa :icon="faCheck" v-if="notification.type === 'followRequestAccepted'"/>
<fa :icon="faIdCardAlt" v-if="notification.type === 'groupInvited'"/>
<fa :icon="faRetweet" v-if="notification.type === 'renote'"/>
<fa :icon="faReply" v-if="notification.type === 'reply'"/>
<fa :icon="faAt" v-if="notification.type === 'mention'"/>
<fa :icon="faQuoteLeft" v-if="notification.type === 'quote'"/>
<x-reaction-icon v-if="notification.type === 'reaction'" :reaction="notification.reaction" :no-style="true"/>
<fa :icon="faClock" v-else-if="notification.type === 'receiveFollowRequest'"/>
<fa :icon="faCheck" v-else-if="notification.type === 'followRequestAccepted'"/>
<fa :icon="faIdCardAlt" v-else-if="notification.type === 'groupInvited'"/>
<fa :icon="faRetweet" v-else-if="notification.type === 'renote'"/>
<fa :icon="faReply" v-else-if="notification.type === 'reply'"/>
<fa :icon="faAt" v-else-if="notification.type === 'mention'"/>
<fa :icon="faQuoteLeft" v-else-if="notification.type === 'quote'"/>
<x-reaction-icon v-else-if="notification.type === 'reaction'" :reaction="notification.reaction" :customEmojis="notification.note.emojis" :no-style="true"/>
</div>
</div>
<div class="tail">
<header>
<router-link class="name" :to="notification.user | userPage" v-user-preview="notification.user.id"><mk-user-name :user="notification.user"/></router-link>
<router-link v-if="notification.user" class="name" :to="notification.user | userPage" v-user-preview="notification.user.id"><mk-user-name :user="notification.user"/></router-link>
<span v-else>{{ notification.header }}</span>
<mk-time :time="notification.createdAt" v-if="withTime"/>
</header>
<router-link v-if="notification.type === 'reaction'" class="text" :to="notification.note | notePage" :title="getNoteSummary(notification.note)">
@@ -42,6 +44,9 @@
<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ $t('followRequestAccepted') }}</span>
<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ $t('receiveFollowRequest') }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ $t('accept') }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ $t('reject') }}</button></div></span>
<span v-if="notification.type === 'groupInvited'" class="text" style="opacity: 0.6;">{{ $t('groupInvited') }}: <b>{{ notification.invitation.group.name }}</b><div v-if="full && !groupInviteDone"><button class="_textButton" @click="acceptGroupInvitation()">{{ $t('accept') }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ $t('reject') }}</button></div></span>
<span v-if="notification.type === 'app'" class="text">
<mfm :text="notification.body" :nowrap="!full"/>
</span>
</div>
</div>
</template>
@@ -142,14 +147,14 @@ export default Vue.extend({
height: 42px;
margin-right: 8px;
> .avatar {
> .icon {
display: block;
width: 100%;
height: 100%;
border-radius: 6px;
}
> .icon {
> .sub-icon {
position: absolute;
z-index: 1;
bottom: -2px;
@@ -163,6 +168,10 @@ export default Vue.extend({
font-size: 12px;
pointer-events: none;
&:empty {
display: none;
}
> * {
color: #fff;
width: 100%;

View File

@@ -35,6 +35,7 @@ export default Vue.extend({
border: solid var(--lineWidth) var(--urlPreviewBorder);
border-radius: 4px;
overflow: hidden;
border: 1px solid var(--divider);
&:hover {
text-decoration: none;
@@ -42,9 +43,8 @@ export default Vue.extend({
}
> .thumbnail {
position: absolute;
width: 100px;
height: 100%;
width: 100%;
height: 200px;
background-position: center;
background-size: cover;
display: flex;

View File

@@ -1,5 +1,5 @@
<template>
<component :is="'x-' + value.type" :value="value" :page="page" :script="script" :key="value.id" :h="h"/>
<component :is="'x-' + value.type" :value="value" :page="page" :hpml="hpml" :key="value.id" :h="h"/>
</template>
<script lang="ts">
@@ -17,16 +17,17 @@ import XTextarea from './page.textarea.vue';
import XPost from './page.post.vue';
import XCounter from './page.counter.vue';
import XRadioButton from './page.radio-button.vue';
import XCanvas from './page.canvas.vue';
export default Vue.extend({
components: {
XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton
XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton, XCanvas
},
props: {
value: {
required: true
},
script: {
hpml: {
required: true
},
page: {

View File

@@ -1,6 +1,6 @@
<template>
<div>
<mk-button class="kudkigyw" @click="click()" :primary="value.primary">{{ script.interpolate(value.text) }}</mk-button>
<mk-button class="kudkigyw" @click="click()" :primary="value.primary">{{ hpml.interpolate(value.text) }}</mk-button>
</div>
</template>
@@ -16,33 +16,35 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
}
},
methods: {
click() {
if (this.value.action === 'dialog') {
this.script.eval();
this.hpml.eval();
this.$root.dialog({
text: this.script.interpolate(this.value.content)
text: this.hpml.interpolate(this.value.content)
});
} else if (this.value.action === 'resetRandom') {
this.script.aiScript.updateRandomSeed(Math.random());
this.script.eval();
this.hpml.updateRandomSeed(Math.random());
this.hpml.eval();
} else if (this.value.action === 'pushEvent') {
this.$root.api('page-push', {
pageId: this.script.page.id,
pageId: this.hpml.page.id,
event: this.value.event,
...(this.value.var ? {
var: this.script.vars[this.value.var]
var: this.hpml.vars[this.value.var]
} : {})
});
this.$root.dialog({
type: 'success',
text: this.script.interpolate(this.value.message)
text: this.hpml.interpolate(this.value.message)
});
} else if (this.value.action === 'callAiScript') {
this.hpml.callAiScript(this.value.fn);
}
}
}

View File

@@ -0,0 +1,36 @@
<template>
<div class="ysrxegms">
<canvas ref="canvas" :width="value.width" :height="value.height"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
value: {
required: true
},
hpml: {
required: true
}
},
mounted() {
this.hpml.registerCanvas(this.value.name, this.$refs.canvas);
}
});
</script>
<style lang="scss" scoped>
.ysrxegms {
display: inline-block;
vertical-align: bottom;
overflow: auto;
max-width: 100%;
> canvas {
display: block;
}
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<div>
<mk-button class="llumlmnx" @click="click()">{{ script.interpolate(value.text) }}</mk-button>
<mk-button class="llumlmnx" @click="click()">{{ hpml.interpolate(value.text) }}</mk-button>
</div>
</template>
@@ -16,7 +16,7 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
}
},
@@ -27,8 +27,8 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
this.hpml.updatePageVar(this.value.name, this.v);
this.hpml.eval();
}
},
methods: {

View File

@@ -1,6 +1,6 @@
<template>
<div v-show="script.vars[value.var]">
<x-block v-for="child in value.children" :value="child" :page="page" :script="script" :key="child.id" :h="h"/>
<x-block v-for="child in value.children" :value="child" :page="page" :hpml="hpml" :key="child.id" :h="h"/>
</div>
</template>
@@ -12,7 +12,7 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
},
page: {

View File

@@ -1,6 +1,6 @@
<template>
<div>
<mk-input class="kudkigyw" v-model="v" type="number">{{ script.interpolate(value.text) }}</mk-input>
<mk-input class="kudkigyw" v-model="v" type="number">{{ hpml.interpolate(value.text) }}</mk-input>
</div>
</template>
@@ -16,7 +16,7 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
}
},
@@ -27,8 +27,8 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
this.hpml.updatePageVar(this.value.name, this.v);
this.hpml.eval();
}
}
});

View File

@@ -1,15 +1,17 @@
<template>
<div class="ngbfujlo">
<mk-textarea :value="text" readonly style="margin: 0;"></mk-textarea>
<mk-button class="button" primary @click="post()" :disabled="posting || posted">{{ posted ? $t('posted') : $t('post') }}</mk-button>
<mk-button class="button" primary @click="post()" :disabled="posting || posted"><fa v-if="posted" :icon="faCheck"/><fa v-else :icon="faPaperPlane"/></mk-button>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faCheck, faPaperPlane } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../i18n';
import MkTextarea from '../ui/textarea.vue';
import MkButton from '../ui/button.vue';
import { apiUrl } from '../../config';
export default Vue.extend({
i18n,
@@ -21,30 +23,63 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
}
},
data() {
return {
text: this.script.interpolate(this.value.text),
text: this.hpml.interpolate(this.value.text),
posted: false,
posting: false,
faCheck, faPaperPlane
};
},
watch: {
'script.vars': {
'hpml.vars': {
handler() {
this.text = this.script.interpolate(this.value.text);
this.text = this.hpml.interpolate(this.value.text);
},
deep: true
}
},
methods: {
post() {
upload() {
return new Promise((ok) => {
const dialog = this.$root.dialog({
type: 'waiting',
text: this.$t('uploading') + '...',
showOkButton: false,
showCancelButton: false,
cancelableByBgClick: false
});
const canvas = this.hpml.canvases[this.value.canvasId];
canvas.toBlob(blob => {
const data = new FormData();
data.append('file', blob);
data.append('i', this.$store.state.i.token);
if (this.$store.state.settings.uploadFolder) {
data.append('folderId', this.$store.state.settings.uploadFolder);
}
fetch(apiUrl + '/drive/files/create', {
method: 'POST',
body: data
})
.then(response => response.json())
.then(f => {
dialog.close();
ok(f);
})
});
});
},
async post() {
this.posting = true;
const file = this.value.attachCanvasImage ? await this.upload() : null;
this.$root.api('notes/create', {
text: this.text,
text: this.text === '' ? null : this.text,
fileIds: file ? [file.id] : undefined,
}).then(() => {
this.posted = true;
this.$root.dialog({
@@ -59,9 +94,11 @@ export default Vue.extend({
<style lang="scss" scoped>
.ngbfujlo {
position: relative;
padding: 32px;
border-radius: 6px;
box-shadow: 0 2px 8px var(--shadow);
z-index: 1;
> .button {
margin-top: 32px;

View File

@@ -1,6 +1,6 @@
<template>
<div>
<div>{{ script.interpolate(value.title) }}</div>
<div>{{ hpml.interpolate(value.title) }}</div>
<mk-radio v-for="x in value.values" v-model="v" :value="x" :key="x">{{ x }}</mk-radio>
</div>
</template>
@@ -17,7 +17,7 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
}
},
@@ -28,8 +28,8 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
this.hpml.updatePageVar(this.value.name, this.v);
this.hpml.eval();
}
}
});

View File

@@ -3,7 +3,7 @@
<component :is="'h' + h">{{ value.title }}</component>
<div class="children">
<x-block v-for="child in value.children" :value="child" :page="page" :script="script" :key="child.id" :h="h + 1"/>
<x-block v-for="child in value.children" :value="child" :page="page" :hpml="hpml" :key="child.id" :h="h + 1"/>
</div>
</section>
</template>
@@ -16,7 +16,7 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
},
page: {

View File

@@ -1,6 +1,6 @@
<template>
<div class="hkcxmtwj">
<mk-switch v-model="v">{{ script.interpolate(value.text) }}</mk-switch>
<mk-switch v-model="v">{{ hpml.interpolate(value.text) }}</mk-switch>
</div>
</template>
@@ -16,7 +16,7 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
}
},
@@ -27,8 +27,8 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
this.hpml.updatePageVar(this.value.name, this.v);
this.hpml.eval();
}
}
});

View File

@@ -1,6 +1,6 @@
<template>
<div>
<mk-input class="kudkigyw" v-model="v" type="text">{{ script.interpolate(value.text) }}</mk-input>
<mk-input class="kudkigyw" v-model="v" type="text">{{ hpml.interpolate(value.text) }}</mk-input>
</div>
</template>
@@ -16,7 +16,7 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
}
},
@@ -27,8 +27,8 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
this.hpml.updatePageVar(this.value.name, this.v);
this.hpml.eval();
}
}
});

View File

@@ -15,13 +15,13 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
}
},
data() {
return {
text: this.script.interpolate(this.value.text),
text: this.hpml.interpolate(this.value.text),
};
},
computed: {
@@ -38,9 +38,9 @@ export default Vue.extend({
}
},
watch: {
'script.vars': {
'hpml.vars': {
handler() {
this.text = this.script.interpolate(this.value.text);
this.text = this.hpml.interpolate(this.value.text);
},
deep: true
}

View File

@@ -1,6 +1,6 @@
<template>
<div>
<mk-textarea v-model="v">{{ script.interpolate(value.text) }}</mk-textarea>
<mk-textarea v-model="v">{{ hpml.interpolate(value.text) }}</mk-textarea>
</div>
</template>
@@ -16,7 +16,7 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
}
},
@@ -27,8 +27,8 @@ export default Vue.extend({
},
watch: {
v() {
this.script.aiScript.updatePageVar(this.value.name, this.v);
this.script.eval();
this.hpml.updatePageVar(this.value.name, this.v);
this.hpml.eval();
}
}
});

View File

@@ -14,19 +14,19 @@ export default Vue.extend({
value: {
required: true
},
script: {
hpml: {
required: true
}
},
data() {
return {
text: this.script.interpolate(this.value.text),
text: this.hpml.interpolate(this.value.text),
};
},
watch: {
'script.vars': {
'hpml.vars': {
handler() {
this.text = this.script.interpolate(this.value.text);
this.text = this.hpml.interpolate(this.value.text);
},
deep: true
}

View File

@@ -1,49 +1,19 @@
<template>
<div class="iroscrza" :class="{ center: page.alignCenter, serif: page.font === 'serif' }" v-if="script">
<x-block v-for="child in page.content" :value="child" @input="v => updateBlock(v)" :page="page" :script="script" :key="child.id" :h="2"/>
<div class="iroscrza" :class="{ center: page.alignCenter, serif: page.font === 'serif' }" v-if="hpml">
<x-block v-for="child in page.content" :value="child" @input="v => updateBlock(v)" :page="page" :hpml="hpml" :key="child.id" :h="2"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import { parse } from '@syuilo/aiscript';
import { faHeart as faHeartS } from '@fortawesome/free-solid-svg-icons';
import { faHeart } from '@fortawesome/free-regular-svg-icons';
import i18n from '../../i18n';
import XBlock from './page.block.vue';
import { ASEvaluator } from '../../scripts/aiscript/evaluator';
import { collectPageVars } from '../../scripts/collect-page-vars';
import { Hpml } from '../../scripts/hpml/evaluator';
import { url } from '../../config';
class Script {
public aiScript: ASEvaluator;
private onError: any;
public vars: Record<string, any>;
public page: Record<string, any>;
constructor(page, aiScript, onError) {
this.page = page;
this.aiScript = aiScript;
this.onError = onError;
this.eval();
}
public eval() {
try {
this.vars = this.aiScript.evaluateVars();
} catch (e) {
this.onError(e);
}
}
public interpolate(str: string) {
if (str == null) return null;
return str.replace(/{(.+?)}/g, match => {
const v = this.vars[match.slice(1, -1).trim()];
return v == null ? 'NULL' : v.toString();
});
}
}
export default Vue.extend({
i18n,
@@ -60,28 +30,52 @@ export default Vue.extend({
data() {
return {
script: null,
hpml: null,
faHeartS, faHeart
};
},
created() {
const pageVars = this.getPageVars();
this.script = new Script(this.page, new ASEvaluator(this.page.variables, pageVars, {
this.hpml = new Hpml(this, this.page, {
randomSeed: Math.random(),
visitor: this.$store.state.i,
page: this.page,
url: url
}), e => {
console.dir(e);
url: url,
enableAiScript: !this.$store.state.device.disablePagesScript
});
},
methods: {
getPageVars() {
return collectPageVars(this.page.content);
},
}
mounted() {
this.$nextTick(() => {
if (this.page.script && this.hpml.aiscript) {
let ast;
try {
ast = parse(this.page.script);
} catch (e) {
console.error(e);
/*this.$root.dialog({
type: 'error',
text: 'Syntax error :('
});*/
return;
}
this.hpml.aiscript.exec(ast).then(() => {
this.hpml.eval();
}).catch(e => {
console.error(e);
/*this.$root.dialog({
type: 'error',
text: e
});*/
});
} else {
this.hpml.eval();
}
});
},
beforeDestroy() {
if (this.hpml.aiscript) this.hpml.aiscript.abort();
},
});
</script>

View File

@@ -1,5 +1,5 @@
<template>
<mk-emoji :emoji="reaction.startsWith(':') ? null : reaction" :name="reaction.startsWith(':') ? reaction.substr(1, reaction.length - 2) : null" :is-reaction="true" :normal="true" :no-style="noStyle"/>
<mk-emoji :emoji="reaction.startsWith(':') ? null : reaction" :name="reaction.startsWith(':') ? reaction.substr(1, reaction.length - 2) : null" :customEmojis="customEmojis" :is-reaction="true" :normal="true" :no-style="noStyle"/>
</template>
<script lang="ts">
@@ -12,6 +12,10 @@ export default Vue.extend({
type: String,
required: true
},
customEmojis: {
required: false,
default: () => []
},
noStyle: {
type: Boolean,
required: false,

View File

@@ -1,7 +1,7 @@
<template>
<button
class="hkzvhatu _button"
:class="{ reacted: note.myReaction == reaction }"
:class="{ reacted: note.myReaction == reaction, canToggle }"
@click="toggleReaction(reaction)"
v-if="count > 0"
@mouseover="onMouseover"
@@ -9,7 +9,7 @@
ref="reaction"
v-particle
>
<x-reaction-icon :reaction="reaction" ref="icon"/>
<x-reaction-icon :reaction="reaction" :customEmojis="note.emojis" ref="icon"/>
<span>{{ count }}</span>
</button>
</template>
@@ -40,11 +40,6 @@ export default Vue.extend({
type: Object,
required: true,
},
canToggle: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
@@ -57,6 +52,9 @@ export default Vue.extend({
isMe(): boolean {
return this.$store.getters.isSignedIn && this.$store.state.i.id === this.note.userId;
},
canToggle(): boolean {
return !this.reaction.match(/@\w/);
},
},
mounted() {
if (!this.isInitial) this.anime();
@@ -144,15 +142,7 @@ export default Vue.extend({
padding: 0 6px;
border-radius: 4px;
&.reacted {
background: var(--accent);
> span {
color: #fff;
}
}
&:not(.reacted) {
&.canToggle {
background: rgba(0, 0, 0, 0.05);
&:hover {
@@ -160,6 +150,22 @@ export default Vue.extend({
}
}
&:not(.canToggle) {
cursor: default;
}
&.reacted {
background: var(--accent);
&:hover {
background: var(--accent);
}
> span {
color: #fff;
}
}
> span {
font-size: 0.9em;
line-height: 32px;

View File

@@ -12,9 +12,9 @@
<template #prefix><fa :icon="faLock"/></template>
</mk-input>
<mk-button type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</mk-button>
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`"><fa :icon="faTwitter"/> {{ $t('signinWith', { x: 'Twitter' }) }}</a></p>
<p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`"><fa :icon="faGithub"/> {{ $t('signinWith', { x: 'GitHub' }) }}</a></p>
<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`"><fa :icon="faDiscord"/> {{ $t('signinWith', { x: 'Discord' }) }}</a></p>
<a class="_panel _button" style="margin: 8px auto;" v-if="meta && meta.enableTwitterIntegration" :href="`${apiUrl}/signin/twitter`"><fa :icon="faTwitter" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Twitter' }) }}</a>
<a class="_panel _button" style="margin: 8px auto;" v-if="meta && meta.enableGithubIntegration" :href="`${apiUrl}/signin/github`"><fa :icon="faGithub" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'GitHub' }) }}</a>
<a class="_panel _button" style="margin: 8px auto;" v-if="meta && meta.enableDiscordIntegration" :href="`${apiUrl}/signin/discord`"><fa :icon="faDiscord" style="margin-right: 4px;"/>{{ $t('signinWith', { x: 'Discord' }) }}</a>
</div>
<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
<div v-if="user && user.securityKeys" class="twofa-group tap-group">

View File

@@ -38,7 +38,7 @@
</mk-input>
<mk-switch v-model="ToSAgreement" v-if="meta.tosUrl">
<i18n path="agreeTo">
<a :href="meta.tosUrl" target="_blank">{{ $t('tos') }}</a>
<a :href="meta.tosUrl" class="_link" target="_blank">{{ $t('tos') }}</a>
</i18n>
</mk-switch>
<div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div>

View File

@@ -50,7 +50,8 @@ export default Vue.extend({
});
const prepend = note => {
(this.$refs.tl as any).prepend(note);
const _note = JSON.parse(JSON.stringify(note)); // deepcopy
(this.$refs.tl as any).prepend(_note);
if (this.sound) {
this.$root.sound(note.userId === this.$store.state.i.id ? 'noteMy' : 'note');

View File

@@ -36,7 +36,7 @@ export default Vue.extend({
mounted() {
const rect = this.source.getBoundingClientRect();
const x = ((rect.left + (this.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset;
const x = Math.max((rect.left + (this.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset;
const y = rect.top + this.source.offsetHeight + window.pageYOffset;
this.top = y;
@@ -50,6 +50,7 @@ export default Vue.extend({
position: absolute;
z-index: 11000;
width: 500px;
max-width: calc(90vw - 12px);
overflow: hidden;
pointer-events: none;
}

View File

@@ -24,6 +24,7 @@ import { faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { toUnicode as decodePunycode } from 'punycode';
import { url as local } from '../config';
import MkUrlPreview from './url-preview-popup.vue';
import { isDeviceTouch } from '../scripts/is-device-touch';
export default Vue.extend({
props: {
@@ -92,11 +93,13 @@ export default Vue.extend({
}
},
onMouseover() {
if (isDeviceTouch()) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.showTimer = setTimeout(this.showPreview, 500);
},
onMouseleave() {
if (isDeviceTouch()) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = setTimeout(this.closePreview, 500);

View File

@@ -6,7 +6,7 @@
<div class="efvhhmdq">
<div class="no-users" v-if="empty">
<p>{{ $t('no-users') }}</p>
<p>{{ $t('noUsers') }}</p>
</div>
<div class="user" v-for="user in users" :key="user.id">
<mk-avatar class="avatar" :user="user"/>

View File

@@ -4,7 +4,7 @@
<script lang="ts">
import Vue from 'vue';
import { faAt, faListUl, faEye, faEyeSlash, faBan, faPencilAlt, faComments, faUsers } from '@fortawesome/free-solid-svg-icons';
import { faAt, faListUl, faEye, faEyeSlash, faBan, faPencilAlt, faComments, faUsers, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
import { faSnowflake, faEnvelope } from '@fortawesome/free-regular-svg-icons';
import i18n from '../i18n';
import XMenu from './menu.vue';
@@ -60,8 +60,12 @@ export default Vue.extend({
action: this.toggleBlock
}]);
if (this.$store.state.i.isAdmin) {
if (this.$store.getters.isSignedIn && (this.$store.state.i.isAdmin || this.$store.state.i.isModerator)) {
menu = menu.concat([null, {
icon: faMicrophoneSlash,
text: this.user.isSilenced ? this.$t('unsilence') : this.$t('silence'),
action: this.toggleSilence
}, {
icon: faSnowflake,
text: this.user.isSuspended ? this.$t('unsuspend') : this.$t('suspend'),
action: this.toggleSuspend
@@ -194,6 +198,25 @@ export default Vue.extend({
});
},
async toggleSilence() {
if (!await this.getConfirmed(this.$t(this.user.isSilenced ? 'unsilenceConfirm' : 'silenceConfirm'))) return;
this.$root.api(this.user.isSilenced ? 'admin/unsilence-user' : 'admin/silence-user', {
userId: this.user.id
}).then(() => {
this.user.isSilenced = !this.user.isSilenced;
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
}, e => {
this.$root.dialog({
type: 'error',
text: e
});
});
},
async toggleSuspend() {
if (!await this.getConfirmed(this.$t(this.user.isSuspended ? 'unsuspendConfirm' : 'suspendConfirm'))) return;

View File

@@ -1,105 +0,0 @@
<template>
<x-window @closed="() => { $emit('closed'); destroyDom(); }" :avatar="user">
<template #header><mk-user-name :user="user"/></template>
<div class="vrcsvlkm">
<mk-button @click="resetPassword()" primary>{{ $t('resetPassword') }}</mk-button>
<mk-switch v-if="$store.state.i.isAdmin && (this.moderator || !user.isAdmin)" @change="toggleModerator()" v-model="moderator">{{ $t('moderator') }}</mk-switch>
<mk-switch @change="toggleSilence()" v-model="silenced">{{ $t('silence') }}</mk-switch>
<mk-switch @change="toggleSuspend()" v-model="suspended">{{ $t('suspend') }}</mk-switch>
</div>
</x-window>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import MkButton from './ui/button.vue';
import MkSwitch from './ui/switch.vue';
import XWindow from './window.vue';
export default Vue.extend({
i18n,
components: {
MkButton,
MkSwitch,
XWindow,
},
props: {
user: {
type: Object,
required: true
}
},
data() {
return {
moderator: this.user.isModerator,
silenced: this.user.isSilenced,
suspended: this.user.isSuspended,
};
},
methods: {
async resetPassword() {
const dialog = this.$root.dialog({
type: 'waiting',
iconOnly: true
});
this.$root.api('admin/reset-password', {
userId: this.user.id,
}).then(({ password }) => {
this.$root.dialog({
type: 'success',
text: this.$t('newPasswordIs', { password })
});
}).catch(e => {
this.$root.dialog({
type: 'error',
text: e
});
}).finally(() => {
dialog.close();
});
},
async toggleSilence() {
const confirm = await this.$root.dialog({
type: 'warning',
showCancelButton: true,
text: this.silenced ? this.$t('silenceConfirm') : this.$t('unsilenceConfirm'),
});
if (confirm.canceled) {
this.silenced = !this.silenced;
} else {
this.$root.api(this.silenced ? 'admin/silence-user' : 'admin/unsilence-user', { userId: this.user.id });
}
},
async toggleSuspend() {
const confirm = await this.$root.dialog({
type: 'warning',
showCancelButton: true,
text: this.suspended ? this.$t('suspendConfirm') : this.$t('unsuspendConfirm'),
});
if (confirm.canceled) {
this.suspended = !this.suspended;
} else {
this.$root.api(this.suspended ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: this.user.id });
}
},
async toggleModerator() {
this.$root.api(this.moderator ? 'admin/moderators/add' : 'admin/moderators/remove', { userId: this.user.id });
}
}
});
</script>
<style lang="scss" scoped>
.vrcsvlkm {
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<x-modal ref="modal" @closed="() => { $emit('closed'); destroyDom(); }">
<div class="ebkgoccj" :class="{ noPadding }" @keydown="onKeydown">
<div class="ebkgoccj" :class="{ noPadding }" @keydown="onKeydown" :style="{ width: `${width}px`, height: `${height}px` }">
<div class="header">
<button class="_button" v-if="withOkButton" @click="close()"><fa :icon="faTimes"/></button>
<span class="title">
@@ -49,7 +49,17 @@ export default Vue.extend({
type: Boolean,
required: false,
default: false
}
},
width: {
type: Number,
required: false,
default: 400
},
height: {
type: Number,
required: false,
default: 400
},
},
data() {
@@ -76,19 +86,12 @@ export default Vue.extend({
<style lang="scss" scoped>
.ebkgoccj {
width: 400px;
height: 400px;
background: var(--panel);
border-radius: var(--radius);
overflow: hidden;
display: flex;
flex-direction: column;
@media (max-width: 500px) {
width: 350px;
height: 350px;
}
> .header {
$height: 58px;
$height-narrow: 42px;

View File

@@ -18,8 +18,9 @@ import PostFormDialog from './components/post-form-dialog.vue';
import Dialog from './components/dialog.vue';
import Menu from './components/menu.vue';
import { router } from './router';
import { applyTheme, lightTheme, builtinThemes } from './theme';
import { applyTheme, lightTheme } from './theme';
import { isDeviceDarkmode } from './scripts/is-device-darkmode';
import createStore from './store';
Vue.use(Vuex);
Vue.use(VueHotkey);
@@ -134,30 +135,39 @@ document.body.setAttribute('ontouchstart', '');
// アプリ基底要素マウント
document.body.innerHTML = '<div id="app"></div>';
const os = new MiOS();
const store = createStore();
const os = new MiOS(store);
os.init(async () => {
window.addEventListener('storage', e => {
if (e.key === 'vuex') {
os.store.replaceState(JSON.parse(localStorage['vuex']));
store.replaceState(JSON.parse(localStorage['vuex']));
} else if (e.key === 'i') {
location.reload();
}
}, false)
store.watch(state => state.device.darkMode, darkMode => {
import('./theme').then(({ builtinThemes }) => {
const themes = builtinThemes.concat(store.state.device.themes);
applyTheme(themes.find(x => x.id === (darkMode ? store.state.device.darkTheme : store.state.device.lightTheme)));
});
});
//#region Sync dark mode
if (os.store.state.device.syncDeviceDarkMode) {
os.store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
if (store.state.device.syncDeviceDarkMode) {
store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
}
window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => {
if (os.store.state.device.syncDeviceDarkMode) {
os.store.commit('device/set', { key: 'darkMode', value: mql.matches });
if (store.state.device.syncDeviceDarkMode) {
store.commit('device/set', { key: 'darkMode', value: mql.matches });
}
});
//#endregion
if ('Notification' in window && os.store.getters.isSignedIn) {
if ('Notification' in window && store.getters.isSignedIn) {
// 許可を得ていなかったらリクエスト
if (Notification.permission === 'default') {
Notification.requestPermission();
@@ -165,7 +175,7 @@ os.init(async () => {
}
const app = new Vue({
store: os.store,
store: store,
metaInfo: {
title: null,
titleTemplate: title => title ? `${title} | ${(instanceName || 'Misskey')}` : (instanceName || 'Misskey')
@@ -176,14 +186,8 @@ os.init(async () => {
isMobile: isMobile
};
},
watch: {
'$store.state.device.darkMode'() {
const themes = builtinThemes.concat(this.$store.state.device.themes);
applyTheme(themes.find(x => x.id === (this.$store.state.device.darkMode ? this.$store.state.device.darkTheme : this.$store.state.device.lightTheme)));
}
},
methods: {
api: os.api,
api: (endpoint: string, data: { [x: string]: any } = {}, token?) => store.dispatch('api', { endpoint, data, token }),
signout: os.signout,
new(vm, props) {
const x = new vm({
@@ -234,58 +238,63 @@ os.init(async () => {
// マウント
app.$mount('#app');
if (app.$store.getters.isSignedIn) {
os.stream.on('emojiAdded', data => {
// TODO
//store.commit('instance/set', );
});
if (store.getters.isSignedIn) {
const main = os.stream.useSharedConnection('main');
// 自分の情報が更新されたとき
main.on('meUpdated', i => {
app.$store.dispatch('mergeMe', i);
store.dispatch('mergeMe', i);
});
main.on('readAllNotifications', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadNotification: false
});
});
main.on('unreadNotification', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadNotification: true
});
});
main.on('unreadMention', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadMentions: true
});
});
main.on('readAllUnreadMentions', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadMentions: false
});
});
main.on('unreadSpecifiedNote', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: true
});
});
main.on('readAllUnreadSpecifiedNotes', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: false
});
});
main.on('readAllMessagingMessages', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadMessagingMessage: false
});
});
main.on('unreadMessagingMessage', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadMessagingMessage: true
});
@@ -293,13 +302,13 @@ os.init(async () => {
});
main.on('readAllAntennas', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadAntenna: false
});
});
main.on('unreadAntenna', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadAntenna: true
});
@@ -307,13 +316,13 @@ os.init(async () => {
});
main.on('readAllAnnouncements', () => {
app.$store.dispatch('mergeMe', {
store.dispatch('mergeMe', {
hasUnreadAnnouncement: false
});
});
main.on('clientSettingUpdated', x => {
app.$store.commit('settings/set', {
store.commit('settings/set', {
key: x.key,
value: x.value
});

View File

@@ -2,16 +2,11 @@ import autobind from 'autobind-decorator';
import Vue from 'vue';
import { EventEmitter } from 'eventemitter3';
import initStore from './store';
import { apiUrl, version } from './config';
import Progress from './scripts/loading';
import Stream from './scripts/stream';
//#region api requests
let spinner = null;
let pending = 0;
//#endregion
import store from './store';
/**
* Misskey Operating System
@@ -19,7 +14,7 @@ let pending = 0;
export default class MiOS extends EventEmitter {
public app: Vue;
public store: ReturnType<typeof initStore>;
public store: ReturnType<typeof store>;
/**
* A connection manager of home stream
@@ -31,6 +26,11 @@ export default class MiOS extends EventEmitter {
*/
private swRegistration: ServiceWorkerRegistration = null;
constructor(vuex: MiOS['store']) {
super();
this.store = vuex;
}
@autobind
public signout() {
this.store.dispatch('logout');
@@ -52,8 +52,6 @@ export default class MiOS extends EventEmitter {
});
};
this.store = initStore(this);
// ユーザーをフェッチしてコールバックする
const fetchme = (token, cb) => {
let me = null;
@@ -187,16 +185,19 @@ export default class MiOS extends EventEmitter {
}
// Register
this.api('sw/register', {
endpoint: subscription.endpoint,
auth: encode(subscription.getKey('auth')),
publickey: encode(subscription.getKey('p256dh'))
this.store.dispatch('api', {
endpoint: 'sw/register',
data: {
endpoint: subscription.endpoint,
auth: encode(subscription.getKey('auth')),
publickey: encode(subscription.getKey('p256dh'))
}
});
})
// When subscribe failed
.catch(async (err: Error) => {
// 通知が許可されていなかったとき
if (err.name == 'NotAllowedError') {
if (err.name === 'NotAllowedError') {
return;
}
@@ -214,52 +215,6 @@ export default class MiOS extends EventEmitter {
// Register service worker
navigator.serviceWorker.register(sw);
}
/**
* Misskey APIにリクエストします
* @param endpoint エンドポイント名
* @param data パラメータ
*/
@autobind
public api(endpoint: string, data: { [x: string]: any } = {}, token?): Promise<{ [x: string]: any }> {
if (++pending === 1) {
spinner = document.createElement('div');
spinner.setAttribute('id', 'wait');
document.body.appendChild(spinner);
}
const onFinally = () => {
if (--pending === 0) spinner.parentNode.removeChild(spinner);
};
const promise = new Promise((resolve, reject) => {
// Append a credential
if (this.store.getters.isSignedIn) (data as any).i = this.store.state.i.token;
if (token) (data as any).i = token;
// Send request
fetch(endpoint.indexOf('://') > -1 ? endpoint : `${apiUrl}/${endpoint}`, {
method: 'POST',
body: JSON.stringify(data),
credentials: 'omit',
cache: 'no-cache'
}).then(async (res) => {
const body = res.status === 204 ? null : await res.json();
if (res.status === 200) {
resolve(body);
} else if (res.status === 204) {
resolve();
} else {
reject(body.error);
}
}).catch(reject);
});
promise.then(onFinally, onFinally);
return promise;
}
}
/**

View File

@@ -1,9 +1,14 @@
<template>
<div class="znqjceqz">
<portal to="title">🍀 {{ $t('aboutMisskey') }}</portal>
<portal to="title">{{ $t('aboutMisskey') }}</portal>
<section class="_card">
<div class="_title">🍀 {{ $t('aboutMisskey') }}</div>
<div class="_title">{{ $t('aboutMisskey') }}</div>
<div class="_content" style="text-align: center;">
<img src="/assets/icons/512.png" alt="" style="display: block; width: 100px; margin: 0 auto; border-radius: 16px;"/>
<div style="margin-top: 0.75em;">Misskey</div>
<div style="opacity: 0.5;">v{{ version }}</div>
</div>
<div class="_content">
<div style="margin-bottom: 1em;">{{ $t('aboutMisskeyText') }}</div>
<div>🛠 {{ $t('misskeyMembers') }}</div>
@@ -44,6 +49,9 @@
<li>wara</li>
<li>Takashi Shibuya</li>
<li>Noizeman</li>
<li>mydarkstar</li>
<li>nenohi</li>
<li>Eduardo Quiros</li>
</ul>
<span>{{ $t('morePatrons') }}</span>
</div>

107
src/client/pages/apps.vue Normal file
View File

@@ -0,0 +1,107 @@
<template>
<div>
<portal to="icon"><fa :icon="faPlug"/></portal>
<portal to="title">{{ $t('installedApps') }}</portal>
<mk-pagination :pagination="pagination" class="bfomjevm" ref="list">
<template #empty>
<div class="_fullinfo">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $t('nothing') }}</div>
</div>
</template>
<template #default="{items}">
<div class="token _panel" v-for="token in items" :key="token.id">
<img class="icon" :src="token.iconUrl" alt=""/>
<div class="body">
<div class="name">{{ token.name }}</div>
<div class="description">{{ token.description }}</div>
<div class="_keyValue">
<div>{{ $t('installedDate') }}:</div>
<div><mk-time :time="token.createdAt"/></div>
</div>
<div class="_keyValue">
<div>{{ $t('lastUsedDate') }}:</div>
<div><mk-time :time="token.lastUsedAt"/></div>
</div>
<div class="actions">
<button class="_button" @click="revoke(token)"><fa :icon="faTrashAlt"/></button>
</div>
<details>
<summary>{{ $t('details') }}</summary>
<ul>
<li v-for="p in token.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
</ul>
</details>
</div>
</div>
</template>
</mk-pagination>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faTrashAlt, faPlug } from '@fortawesome/free-solid-svg-icons';
import MkPagination from '../components/ui/pagination.vue';
export default Vue.extend({
metaInfo() {
return {
title: this.$t('installedApps') as string
};
},
components: {
MkPagination
},
data() {
return {
pagination: {
endpoint: 'i/apps',
limit: 100,
params: {
sort: '+lastUsedAt'
}
},
faTrashAlt, faPlug
};
},
methods: {
revoke(token) {
this.$root.api('i/revoke-token', { tokenId: token.id }).then(() => {
this.$refs.list.reload();
});
}
}
});
</script>
<style lang="scss" scoped>
.bfomjevm {
> .token {
display: flex;
padding: 16px;
> .icon {
display: block;
flex-shrink: 0;
margin: 0 12px 0 0;
width: 50px;
height: 50px;
border-radius: 8px;
}
> .body {
width: calc(100% - 62px);
position: relative;
> .name {
font-weight: bold;
}
}
}
}
</style>

View File

@@ -12,20 +12,18 @@
@accepted="accepted"
/>
<div class="denied _panel" v-if="state == 'denied'">
<h1>{{ $t('denied') }}</h1>
<p>{{ $t('denied-paragraph') }}</p>
<h1>{{ $t('_auth.denied') }}</h1>
</div>
<div class="accepted _panel" v-if="state == 'accepted'">
<h1>{{ session.app.isAuthorized ? this.$t('already-authorized') : this.$t('allowed') }}</h1>
<p v-if="session.app.callbackUrl">{{ $t('callback-url') }}<mk-ellipsis/></p>
<p v-if="!session.app.callbackUrl">{{ $t('please-go-back') }}</p>
<p v-if="session.app.callbackUrl">{{ $t('_auth.callback') }}<mk-ellipsis/></p>
<p v-if="!session.app.callbackUrl">{{ $t('_auth.pleaseGoBack') }}</p>
</div>
<div class="error _panel" v-if="state == 'fetch-session-error'">
<p>{{ $t('error') }}</p>
</div>
</div>
<div class="signin" v-else>
<h1>{{ $t('sign-in') }}</h1>
<mk-signin @login="onLogin"/>
</div>
</template>

View File

@@ -18,6 +18,7 @@
import Vue from 'vue';
import { faFileAlt } from '@fortawesome/free-solid-svg-icons'
import MarkdownIt from 'markdown-it';
import MarkdownItAnchor from 'markdown-it-anchor';
import i18n from '../i18n';
import { url, lang } from '../config';
import MkLink from '../components/link.vue';
@@ -26,6 +27,10 @@ const markdown = MarkdownIt({
html: true
});
markdown.use(MarkdownItAnchor, {
slugify: (s) => encodeURIComponent(String(s).trim().replace(/\s+/g, '-'))
});
export default Vue.extend({
i18n,
@@ -72,6 +77,9 @@ export default Vue.extend({
},
parse(md: string) {
// 変数置換
md = md.replace(/\{_URL_\}/g, url);
// markdown の全容をパースする
const parsed = markdown.parse(md, {});
if (parsed.length === 0) return;
@@ -115,6 +123,23 @@ export default Vue.extend({
margin-bottom: 0;
}
::v-deep a {
color: var(--link);
}
::v-deep blockquote {
display: block;
margin: 8px;
padding: 6px 0 6px 12px;
color: var(--fg);
border-left: solid 3px var(--fg);
opacity: 0.7;
p {
margin: 0;
}
}
::v-deep h2 {
font-size: 1.25em;
padding: 0 0 0.5em 0;

View File

@@ -5,8 +5,8 @@
<mk-pagination :pagination="pagination" class="mk-follow-requests" ref="list">
<template #empty>
<div class="tkdrhpxr">
<img src="https://xn--931a.moe/assets/info.png" class="_ghost"/>
<div class="_fullinfo">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $t('noFollowRequests') }}</div>
</div>
</template>
@@ -75,18 +75,6 @@ export default Vue.extend({
<style lang="scss" scoped>
.mk-follow-requests {
.tkdrhpxr {
padding: 32px;
text-align: center;
> img {
vertical-align: bottom;
height: 128px;
margin-bottom: 16px;
border-radius: 16px;
}
}
> .user {
display: flex;
padding: 16px;

View File

@@ -29,7 +29,6 @@ import { faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faListUl, faSatell
import { faComments } from '@fortawesome/free-regular-svg-icons';
import Progress from '../scripts/loading';
import XTimeline from '../components/timeline.vue';
import XTutorial from './index.home.tutorial.vue';
import XPostForm from '../components/post-form.vue';
export default Vue.extend({
@@ -41,7 +40,7 @@ export default Vue.extend({
components: {
XTimeline,
XTutorial,
XTutorial: () => import('./index.home.tutorial.vue').then(m => m.default),
XPostForm,
},

View File

@@ -99,10 +99,19 @@
<span class="label">{{ $t('operations') }}</span>
<mk-switch v-model="isSuspended" class="switch">{{ $t('stopActivityDelivery') }}</mk-switch>
<mk-switch :value="isBlocked" class="switch" @change="changeBlock">{{ $t('blockThisInstance') }}</mk-switch>
<details>
<summary>{{ $t('deleteAllFiles') }}</summary>
<mk-button @click="deleteAllFiles()" style="margin: 0.5em 0 0.5em 0;"><fa :icon="faTrashAlt"/> {{ $t('deleteAllFiles') }}</mk-button>
</details>
<details>
<summary>{{ $t('removeAllFollowing') }}</summary>
<mk-button @click="removeAllFollowing()" style="margin: 0.5em 0 0.5em 0;"><fa :icon="faMinusCircle"/> {{ $t('removeAllFollowing') }}</mk-button>
<mk-info warn>{{ $t('removeAllFollowingDescription', { host: instance.host }) }}</mk-info>
</details>
</div>
<details class="metadata">
<summary class="label">{{ $t('metadata') }}</summary>
<pre><code>{{ JSON.stringify(instance.metadata, null, 2) }}</code></pre>
<pre><code>{{ JSON.stringify(instance, null, 2) }}</code></pre>
</details>
</div>
</x-window>
@@ -112,11 +121,13 @@
import Vue from 'vue';
import Chart from 'chart.js';
import i18n from '../../i18n';
import { faTimes, faCrosshairs, faCloudDownloadAlt, faCloudUploadAlt, faUsers, faPencilAlt, faFileImage, faDatabase, faTrafficLight, faLongArrowAltUp, faLongArrowAltDown } from '@fortawesome/free-solid-svg-icons';
import { faTimes, faCrosshairs, faCloudDownloadAlt, faCloudUploadAlt, faUsers, faPencilAlt, faFileImage, faDatabase, faTrafficLight, faLongArrowAltUp, faLongArrowAltDown, faMinusCircle, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import XWindow from '../../components/window.vue';
import MkUsersDialog from '../../components/users-dialog.vue';
import MkSelect from '../../components/ui/select.vue';
import MkButton from '../../components/ui/button.vue';
import MkSwitch from '../../components/ui/switch.vue';
import MkInfo from '../../components/ui/info.vue';
const chartLimit = 90;
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
@@ -135,7 +146,9 @@ export default Vue.extend({
components: {
XWindow,
MkSelect,
MkButton,
MkSwitch,
MkInfo,
},
props: {
@@ -153,7 +166,7 @@ export default Vue.extend({
chartInstance: null,
chartSrc: 'requests',
chartSpan: 'hour',
faTimes, faCrosshairs, faCloudDownloadAlt, faCloudUploadAlt, faUsers, faPencilAlt, faFileImage, faDatabase, faTrafficLight, faLongArrowAltUp, faLongArrowAltDown
faTimes, faCrosshairs, faCloudDownloadAlt, faCloudUploadAlt, faUsers, faPencilAlt, faFileImage, faDatabase, faTrafficLight, faLongArrowAltUp, faLongArrowAltDown, faMinusCircle, faTrashAlt
};
},
@@ -239,6 +252,28 @@ export default Vue.extend({
this.chartSrc = src;
},
removeAllFollowing() {
this.$root.api('admin/federation/remove-all-following', {
host: this.instance.host
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
});
},
deleteAllFiles() {
this.$root.api('admin/federation/delete-all-files', {
host: this.instance.host
}).then(() => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
});
},
renderChart() {
if (this.chartInstance) {
this.chartInstance.destroy();

View File

@@ -1,11 +1,14 @@
<template>
<div class="mk-federation">
<portal to="icon"><fa :icon="faGlobe"/></portal>
<portal to="title">{{ $t('federation') }}</portal>
<section class="_card instances">
<div class="_title"><fa :icon="faGlobe"/> {{ $t('instances') }}</div>
<div class="_content">
<mk-input v-model="host" :debounce="true"><span>{{ $t('host') }}</span></mk-input>
<div class="inputs" style="display: flex;">
<mk-input v-model="host" :debounce="true" style="margin: 0; flex: 1;"><span>{{ $t('host') }}</span></mk-input>
<mk-select v-model="state" style="margin: 0;">
<mk-select v-model="state" style="margin: 0; flex: 1;">
<template #label>{{ $t('state') }}</template>
<option value="all">{{ $t('all') }}</option>
<option value="federating">{{ $t('federating') }}</option>
<option value="subscribing">{{ $t('subscribing') }}</option>
@@ -14,11 +17,32 @@
<option value="blocked">{{ $t('blocked') }}</option>
<option value="notResponding">{{ $t('notResponding') }}</option>
</mk-select>
<mk-select v-model="sort" style="margin: 0; flex: 1;">
<template #label>{{ $t('sort') }}</template>
<option value="+pubSub">{{ $t('pubSub') }} ({{ $t('descendingOrder') }})</option>
<option value="-pubSub">{{ $t('pubSub') }} ({{ $t('ascendingOrder') }})</option>
<option value="+notes">{{ $t('notes') }} ({{ $t('descendingOrder') }})</option>
<option value="-notes">{{ $t('notes') }} ({{ $t('ascendingOrder') }})</option>
<option value="+users">{{ $t('users') }} ({{ $t('descendingOrder') }})</option>
<option value="-users">{{ $t('users') }} ({{ $t('ascendingOrder') }})</option>
<option value="+following">{{ $t('following') }} ({{ $t('descendingOrder') }})</option>
<option value="-following">{{ $t('following') }} ({{ $t('ascendingOrder') }})</option>
<option value="+followers">{{ $t('followers') }} ({{ $t('descendingOrder') }})</option>
<option value="-followers">{{ $t('followers') }} ({{ $t('ascendingOrder') }})</option>
<option value="+caughtAt">{{ $t('caughtAt') }} ({{ $t('descendingOrder') }})</option>
<option value="-caughtAt">{{ $t('caughtAt') }} ({{ $t('ascendingOrder') }})</option>
<option value="+lastCommunicatedAt">{{ $t('lastCommunicatedAt') }} ({{ $t('descendingOrder') }})</option>
<option value="-lastCommunicatedAt">{{ $t('lastCommunicatedAt') }} ({{ $t('ascendingOrder') }})</option>
<option value="+driveUsage">{{ $t('driveUsage') }} ({{ $t('descendingOrder') }})</option>
<option value="-driveUsage">{{ $t('driveUsage') }} ({{ $t('ascendingOrder') }})</option>
<option value="+driveFiles">{{ $t('driveFiles') }} ({{ $t('descendingOrder') }})</option>
<option value="-driveFiles">{{ $t('driveFiles') }} ({{ $t('ascendingOrder') }})</option>
</mk-select>
</div>
</div>
<div class="_content">
<mk-pagination :pagination="pagination" #default="{items}" class="instances" ref="instances" :key="host + state">
<div class="instance" v-for="(instance, i) in items" :key="instance.id" @click="info(instance)">
<div class="instance" v-for="instance in items" :key="instance.id" @click="info(instance)">
<div class="host"><fa :icon="faCircle" class="indicator" :class="getStatus(instance)"/><b>{{ instance.host }}</b></div>
<div class="status">
<span class="sub" v-if="instance.followersCount > 0"><fa :icon="faCaretDown" class="icon"/>Sub</span>

View File

@@ -1,5 +1,8 @@
<template>
<div>
<portal to="icon"><fa :icon="faExchangeAlt"/></portal>
<portal to="title">{{ $t('jobQueue') }}</portal>
<x-queue :connection="connection" domain="inbox">
<template #title><fa :icon="faExchangeAlt"/> In</template>
</x-queue>

View File

@@ -116,6 +116,7 @@
<mk-input v-model="objectStorageSecretKey" :disabled="!useObjectStorage"><template #icon><fa :icon="faKey"/></template>Secret key</mk-input>
</div>
<mk-switch v-model="objectStorageUseSSL" :disabled="!useObjectStorage">{{ $t('objectStorageUseSSL') }}<template #desc>{{ $t('objectStorageUseSSLDesc') }}</template></mk-switch>
<mk-switch v-model="objectStorageUseProxy" :disabled="!useObjectStorage">{{ $t('objectStorageUseProxy') }}<template #desc>{{ $t('objectStorageUseProxyDesc') }}</template></mk-switch>
</template>
</div>
<div class="_footer">
@@ -249,6 +250,7 @@ export default Vue.extend({
objectStorageAccessKey: null,
objectStorageSecretKey: null,
objectStorageUseSSL: false,
objectStorageUseProxy: false,
enableTwitterIntegration: false,
twitterConsumerKey: null,
twitterConsumerSecret: null,
@@ -303,6 +305,7 @@ export default Vue.extend({
this.objectStorageAccessKey = this.meta.objectStorageAccessKey;
this.objectStorageSecretKey = this.meta.objectStorageSecretKey;
this.objectStorageUseSSL = this.meta.objectStorageUseSSL;
this.objectStorageUseProxy = this.meta.objectStorageUseProxy;
this.enableTwitterIntegration = this.meta.enableTwitterIntegration;
this.twitterConsumerKey = this.meta.twitterConsumerKey;
this.twitterConsumerSecret = this.meta.twitterConsumerSecret;
@@ -411,6 +414,7 @@ export default Vue.extend({
objectStorageAccessKey: this.objectStorageAccessKey ? this.objectStorageAccessKey : null,
objectStorageSecretKey: this.objectStorageSecretKey ? this.objectStorageSecretKey : null,
objectStorageUseSSL: this.objectStorageUseSSL,
objectStorageUseProxy: this.objectStorageUseProxy,
enableTwitterIntegration: this.enableTwitterIntegration,
twitterConsumerKey: this.twitterConsumerKey,
twitterConsumerSecret: this.twitterConsumerSecret,

View File

@@ -0,0 +1,209 @@
<template>
<div class="vrcsvlkm" v-if="user && info">
<portal to="title" v-if="user"><mk-user-name :user="user" :nowrap="false" class="name"/></portal>
<portal to="avatar" v-if="user"><mk-avatar class="avatar" :user="user" :disable-preview="true"/></portal>
<section class="_card">
<div class="_title">
<mk-avatar class="avatar" :user="user"/>
<mk-user-name class="name" :user="user"/>
<span class="acct">@{{ user | acct }}</span>
<span class="staff" v-if="user.isAdmin"><fa :icon="faBookmark"/></span>
<span class="staff" v-if="user.isModerator"><fa :icon="farBookmark"/></span>
<span class="punished" v-if="user.isSilenced"><fa :icon="faMicrophoneSlash"/></span>
<span class="punished" v-if="user.isSuspended"><fa :icon="faSnowflake"/></span>
</div>
<div class="_content actions">
<div style="flex: 1; padding-left: 1em;">
<mk-switch v-if="user.host == null && $store.state.i.isAdmin && (this.moderator || !user.isAdmin)" @change="toggleModerator()" v-model="moderator">{{ $t('moderator') }}</mk-switch>
<mk-switch @change="toggleSilence()" v-model="silenced">{{ $t('silence') }}</mk-switch>
<mk-switch @change="toggleSuspend()" v-model="suspended">{{ $t('suspend') }}</mk-switch>
</div>
<div style="flex: 1; padding-left: 1em;">
<mk-button @click="openProfile"><fa :icon="faExternalLinkSquareAlt"/> {{ $t('profile')}}</mk-button>
<mk-button v-if="user.host != null" @click="updateRemoteUser"><fa :icon="faSync"/> {{ $t('updateRemoteUser') }}</mk-button>
<mk-button @click="resetPassword"><fa :icon="faKey"/> {{ $t('resetPassword') }}</mk-button>
<mk-button @click="deleteAllFiles"><fa :icon="faTrashAlt"/> {{ $t('deleteAllFiles') }}</mk-button>
</div>
</div>
<div class="_content rawdata">
<pre><code>{{ JSON.stringify(info, null, 2) }}</code></pre>
</div>
</section>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faTimes, faBookmark, faKey, faSync, faMicrophoneSlash, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { faSnowflake, faTrashAlt, faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons';
import MkButton from '../../components/ui/button.vue';
import MkSwitch from '../../components/ui/switch.vue';
import i18n from '../../i18n';
import Progress from '../../scripts/loading';
export default Vue.extend({
i18n,
components: {
MkButton,
MkSwitch,
},
data() {
return {
user: null,
info: null,
moderator: false,
silenced: false,
suspended: false,
faTimes, faBookmark, farBookmark, faKey, faSync, faMicrophoneSlash, faSnowflake, faTrashAlt, faExternalLinkSquareAlt
};
},
watch: {
$route: 'fetch'
},
created() {
this.fetch();
},
methods: {
async fetch() {
Progress.start();
this.user = await this.$root.api('users/show', { userId: this.$route.params.user });
this.info = await this.$root.api('admin/show-user', { userId: this.$route.params.user });
this.moderator = this.info.isModerator;
this.silenced = this.info.isSilenced;
this.suspended = this.info.isSuspended;
Progress.done();
},
/** 処理対象ユーザーの情報を更新する */
async refreshUser() {
this.user = await this.$root.api('users/show', { userId: this.user.id });
this.info = await this.$root.api('admin/show-user', { userId: this.user.id });
},
openProfile() {
window.open(Vue.filter('userPage')(this.user, null, true), '_blank');
},
async updateRemoteUser() {
await this.$root.api('admin/update-remote-user', { userId: this.user.id }).then(res => {
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
});
await this.refreshUser();
},
async resetPassword() {
const dialog = this.$root.dialog({
type: 'waiting',
iconOnly: true
});
this.$root.api('admin/reset-password', {
userId: this.user.id,
}).then(({ password }) => {
this.$root.dialog({
type: 'success',
text: this.$t('newPasswordIs', { password })
});
}).catch(e => {
this.$root.dialog({
type: 'error',
text: e
});
}).finally(() => {
dialog.close();
});
},
async toggleSilence() {
const confirm = await this.$root.dialog({
type: 'warning',
showCancelButton: true,
text: this.silenced ? this.$t('silenceConfirm') : this.$t('unsilenceConfirm'),
});
if (confirm.canceled) {
this.silenced = !this.silenced;
} else {
await this.$root.api(this.silenced ? 'admin/silence-user' : 'admin/unsilence-user', { userId: this.user.id });
await this.refreshUser();
}
},
async toggleSuspend() {
const confirm = await this.$root.dialog({
type: 'warning',
showCancelButton: true,
text: this.suspended ? this.$t('suspendConfirm') : this.$t('unsuspendConfirm'),
});
if (confirm.canceled) {
this.suspended = !this.suspended;
} else {
await this.$root.api(this.suspended ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: this.user.id });
await this.refreshUser();
}
},
async toggleModerator() {
await this.$root.api(this.moderator ? 'admin/moderators/add' : 'admin/moderators/remove', { userId: this.user.id });
await this.refreshUser();
},
async deleteAllFiles() {
const confirm = await this.$root.dialog({
type: 'warning',
showCancelButton: true,
text: this.$t('deleteAllFilesConfirm'),
});
if (confirm.canceled) return;
const process = async () => {
await this.$root.api('admin/delete-all-files-of-a-user', { userId: this.user.id });
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
};
await process().catch(e => {
this.$root.dialog({
type: 'error',
text: e.toString()
});
});
await this.refreshUser();
},
}
});
</script>
<style lang="scss" scoped>
.vrcsvlkm {
display: flex;
flex-direction: column;
> ._card {
> .actions {
display: flex;
box-sizing: border-box;
text-align: left;
align-items: center;
margin-top: 16px;
margin-bottom: 16px;
}
> .rawdata {
> pre > code {
display: block;
width: 100%;
height: 100%;
}
}
}
}
</style>

View File

@@ -12,19 +12,65 @@
<mk-button @click="showUser()" primary><fa :icon="faSearch"/> {{ $t('lookup') }}</mk-button>
</div>
<div class="_footer">
<mk-button inline primary @click="search()"><fa :icon="faSearch"/> {{ $t('search') }}</mk-button>
<mk-button inline primary @click="searchUser()"><fa :icon="faSearch"/> {{ $t('search') }}</mk-button>
</div>
</section>
<section class="_card users">
<div class="_title"><fa :icon="faUsers"/> {{ $t('users') }}</div>
<div class="_content">
<div class="inputs" style="display: flex;">
<mk-select v-model="sort" style="margin: 0; flex: 1;">
<template #label>{{ $t('sort') }}</template>
<option value="-createdAt">{{ $t('registeredDate') }} ({{ $t('ascendingOrder') }})</option>
<option value="+createdAt">{{ $t('registeredDate') }} ({{ $t('descendingOrder') }})</option>
<option value="-updatedAt">{{ $t('lastUsed') }} ({{ $t('ascendingOrder') }})</option>
<option value="+updatedAt">{{ $t('lastUsed') }} ({{ $t('descendingOrder') }})</option>
</mk-select>
<mk-select v-model="state" style="margin: 0; flex: 1;">
<template #label>{{ $t('state') }}</template>
<option value="all">{{ $t('all') }}</option>
<option value="available">{{ $t('normal') }}</option>
<option value="admin">{{ $t('administrator') }}</option>
<option value="moderator">{{ $t('moderator') }}</option>
<option value="silenced">{{ $t('silence') }}</option>
<option value="suspended">{{ $t('suspend') }}</option>
</mk-select>
<mk-select v-model="origin" style="margin: 0; flex: 1;">
<template #label>{{ $t('instance') }}</template>
<option value="combined">{{ $t('all') }}</option>
<option value="local">{{ $t('local') }}</option>
<option value="remote">{{ $t('remote') }}</option>
</mk-select>
</div>
<div class="inputs" style="display: flex; padding-top: 1.2em;">
<mk-input v-model="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @input="$refs.users.reload()">
<span>{{ $t('username') }}</span>
</mk-input>
<mk-input v-model="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @input="$refs.users.reload()" :disabled="pagination.params().origin === 'local'">
<span>{{ $t('host') }}</span>
</mk-input>
</div>
</div>
<div class="_content _list">
<mk-pagination :pagination="pagination" #default="{items}" class="users" ref="users" :auto-margin="false">
<button class="user _button _listItem" v-for="(user, i) in items" :key="user.id" @click="show(user)">
<mk-avatar :user="user" class="avatar"/>
<mk-avatar class="avatar" :user="user" :disable-link="true"/>
<div class="body">
<mk-user-name :user="user" class="name"/>
<mk-acct :user="user" class="acct"/>
<header>
<mk-user-name class="name" :user="user"/>
<span class="acct">@{{ user | acct }}</span>
<span class="staff" v-if="user.isAdmin"><fa :icon="faBookmark"/></span>
<span class="staff" v-if="user.isModerator"><fa :icon="farBookmark"/></span>
<span class="punished" v-if="user.isSilenced"><fa :icon="faMicrophoneSlash"/></span>
<span class="punished" v-if="user.isSuspended"><fa :icon="faSnowflake"/></span>
</header>
<div>
<span>{{ $t('lastUsed') }}: <mk-time :time="user.updatedAt" mode="detail"/></span>
</div>
<div>
<span>{{ $t('registeredDate') }}: <mk-time :time="user.createdAt" mode="detail"/></span>
</div>
</div>
</button>
</mk-pagination>
@@ -38,12 +84,13 @@
<script lang="ts">
import Vue from 'vue';
import { faPlus, faUsers, faSearch } from '@fortawesome/free-solid-svg-icons';
import { faPlus, faUsers, faSearch, faBookmark, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
import { faSnowflake, faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons';
import parseAcct from '../../../misc/acct/parse';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
import MkSelect from '../../components/ui/select.vue';
import MkPagination from '../../components/ui/pagination.vue';
import MkUserModerateDialog from '../../components/user-moderate-dialog.vue';
import MkUserSelect from '../../components/user-select.vue';
export default Vue.extend({
@@ -56,21 +103,46 @@ export default Vue.extend({
components: {
MkButton,
MkInput,
MkSelect,
MkPagination,
},
data() {
return {
target: '',
sort: '+createdAt',
state: 'all',
origin: 'local',
searchUsername: '',
searchHost: '',
pagination: {
endpoint: 'admin/show-users',
limit: 10,
params: () => ({
sort: this.sort,
state: this.state,
origin: this.origin,
username: this.searchUsername,
hostname: this.searchHost,
}),
offsetMode: true
},
target: '',
faPlus, faUsers, faSearch
faPlus, faUsers, faSearch, faBookmark, farBookmark, faMicrophoneSlash, faSnowflake
}
},
watch: {
sort() {
this.$refs.users.reload();
},
state() {
this.$refs.users.reload();
},
origin() {
this.$refs.users.reload();
},
},
methods: {
/** テキストエリアのユーザーを解決する */
fetchUser() {
@@ -102,12 +174,16 @@ export default Vue.extend({
/** テキストエリアから処理対象ユーザーを設定する */
async showUser() {
const user = await this.fetchUser();
this.$root.api('admin/show-user', { userId: user.id }).then(info => {
this.show(user, info);
});
this.show(user);
this.target = '';
},
searchUser() {
this.$root.new(MkUserSelect, {}).$once('selected', user => {
this.show(user);
});
},
async addUser() {
const { canceled: canceled1, result: username } = await this.$root.dialog({
title: this.$t('username'),
@@ -145,19 +221,8 @@ export default Vue.extend({
});
},
async show(user, info) {
if (info == null) info = await this.$root.api('admin/show-user', { userId: user.id });
this.$root.new(MkUserModerateDialog, {
user: { ...user, ...info }
});
},
search() {
this.$root.new(MkUserSelect, {}).$once('selected', user => {
this.$root.api('admin/show-user', { userId: user.id }).then(info => {
this.show(user, info);
});
});
async show(user) {
this.$router.push('./users/' + user.id);
}
}
});
@@ -179,20 +244,38 @@ export default Vue.extend({
align-items: center;
> .avatar {
width: 50px;
height: 50px;
width: 64px;
height: 64px;
}
> .body {
margin-left: 0.3em;
padding: 8px;
flex: 1;
> .name {
display: block;
font-weight: bold;
@media (max-width 500px) {
font-size: 14px;
}
> .acct {
opacity: 0.5;
> header {
> .name {
font-weight: bold;
}
> .acct {
margin-left: 8px;
opacity: 0.7;
}
> .staff {
margin-left: 0.5em;
color: var(--badge);
}
> .punished {
margin-left: 0.5em;
color: #4dabf7;
}
}
}
}

View File

@@ -31,8 +31,8 @@
</div>
</router-link>
</div>
<div class="no-history" v-if="!fetching && messages.length == 0">
<img src="https://xn--931a.moe/assets/info.png" class="_ghost"/>
<div class="_fullinfo" v-if="!fetching && messages.length == 0">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $t('noHistory') }}</div>
</div>
<mk-loading v-if="fetching"/>
@@ -287,18 +287,6 @@ export default Vue.extend({
}
}
> .no-history {
padding: 32px;
text-align: center;
> img {
vertical-align: bottom;
height: 128px;
margin-bottom: 16px;
border-radius: 16px;
}
}
@media (max-width: 400px) {
> .history {
> .message {

View File

@@ -214,6 +214,7 @@ export default Vue.extend({
width: 100%;
max-height: 512px;
object-fit: contain;
box-sizing: border-box;
}
> p {

View File

@@ -19,7 +19,7 @@
<button class="more _button" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
<template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('loading') : $t('loadMore') }}
</button>
<x-list class="messages" :items="messages" v-slot="{ item: message, i }" direction="up" reversed>
<x-list class="messages" :items="messages" v-slot="{ item: message }" direction="up" reversed>
<x-message :message="message" :is-group="group != null" :key="message.id"/>
</x-list>
</div>

103
src/client/pages/miauth.vue Normal file
View File

@@ -0,0 +1,103 @@
<template>
<div v-if="$store.getters.isSignedIn">
<div class="waiting _card" v-if="state == 'waiting'">
<div class="_content">
<mk-loading/>
</div>
</div>
<div class="denied _card" v-if="state == 'denied'">
<div class="_content">
<p>{{ $t('_auth.denied') }}</p>
</div>
</div>
<div class="accepted _card" v-else-if="state == 'accepted'">
<div class="_content">
<p v-if="callback">{{ $t('_auth.callback') }}<mk-ellipsis/></p>
<p v-else>{{ $t('_auth.pleaseGoBack') }}</p>
</div>
</div>
<div class="_card" v-else>
<div class="_title" v-if="name">{{ $t('_auth.shareAccess', { name: name }) }}</div>
<div class="_title" v-else>{{ $t('_auth.shareAccessAsk') }}</div>
<div class="_content">
<p>{{ $t('_auth.permissionAsk') }}</p>
<ul>
<template v-for="p in permission">
<li :key="p">{{ $t(`_permissions.${p}`) }}</li>
</template>
</ul>
</div>
<div class="_footer">
<mk-button @click="deny" inline>{{ $t('cancel') }}</mk-button>
<mk-button @click="accept" inline primary>{{ $t('accept') }}</mk-button>
</div>
</div>
</div>
<div class="signin" v-else>
<mk-signin @login="onLogin"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../i18n';
import MkSignin from '../components/signin.vue';
import MkButton from '../components/ui/button.vue';
export default Vue.extend({
i18n,
components: {
MkSignin,
MkButton,
},
data() {
return {
state: null
};
},
computed: {
session(): string {
return this.$route.params.session;
},
callback(): string {
return this.$route.query.callback;
},
name(): string {
return this.$route.query.name;
},
icon(): string {
return this.$route.query.icon;
},
permission(): string[] {
return this.$route.query.permission ? this.$route.query.permission.split(',') : [];
},
},
methods: {
async accept() {
this.state = 'waiting';
await this.$root.api('miauth/gen-token', {
session: this.session,
name: this.name,
iconUrl: this.icon,
permission: this.permission,
});
this.state = 'accepted';
if (this.callback) {
location.href = `${this.callback}?session=${this.session}`;
}
},
deny() {
this.state = 'denied';
},
onLogin(res) {
localStorage.setItem('i', res.i);
location.reload();
}
}
});
</script>
<style lang="scss" scoped>
</style>

View File

@@ -2,107 +2,57 @@
<section class="uawsfosz _card">
<div class="_title"><fa :icon="faCloud"/> {{ $t('drive') }}</div>
<div class="_content">
<mk-pagination :pagination="drivePagination" #default="{items}" class="drive" ref="drive">
<div class="file" v-for="(file, i) in items" :key="file.id" @click="selected = file" :class="{ selected: selected && (selected.id === file.id) }">
<x-file-thumbnail class="thumbnail" :file="file" fit="cover"/>
<div class="body">
<p class="name">
<span>{{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }}</span>
<span class="ext" v-if="file.name.lastIndexOf('.') != -1">{{ file.name.substr(file.name.lastIndexOf('.')) }}</span>
</p>
<footer>
<span class="type"><x-file-type-icon :type="file.type" class="icon"/>{{ file.type }}</span>
<span class="separator"></span>
<span class="data-size">{{ file.size | bytes }}</span>
<span class="separator"></span>
<span class="created-at"><fa :icon="faClock"/><mk-time :time="file.createdAt"/></span>
<template v-if="file.isSensitive">
<span class="separator"></span>
<span class="nsfw"><fa :icon="faEyeSlash"/> {{ $t('nsfw') }}</span>
</template>
</footer>
</div>
</div>
</mk-pagination>
</div>
<div class="_footer">
<mk-button primary inline :disabled="selected == null" @click="download()"><fa :icon="faDownload"/> {{ $t('download') }}</mk-button>
<mk-button inline :disabled="selected == null" @click="del()"><fa :icon="faTrashAlt"/> {{ $t('delete') }}</mk-button>
<span>{{ $t('uploadFolder') }}: {{ uploadFolder ? uploadFolder.name : '-' }}</span>
<mk-button primary @click="chooseUploadFolder()"><fa :icon="faFolderOpen"/> {{ $t('selectFolder') }}</mk-button>
</div>
</section>
</template>
<script lang="ts">
import Vue from 'vue';
import { faCloud, faDownload } from '@fortawesome/free-solid-svg-icons';
import { faCloud, faFolderOpen } from '@fortawesome/free-solid-svg-icons';
import { faClock, faEyeSlash, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import XFileTypeIcon from '../../components/file-type-icon.vue';
import XFileThumbnail from '../../components/drive-file-thumbnail.vue';
import MkButton from '../../components/ui/button.vue';
import MkPagination from '../../components/ui/pagination.vue';
import i18n from '../../i18n';
import { selectDriveFolder } from '../../scripts/select-drive-folder';
export default Vue.extend({
i18n,
components: {
XFileTypeIcon,
XFileThumbnail,
MkPagination,
MkButton,
},
data() {
return {
selected: null,
connection: null,
drivePagination: {
endpoint: 'drive/files',
limit: 10,
},
faCloud, faClock, faEyeSlash, faDownload, faTrashAlt
uploadFolder: null,
faCloud, faClock, faEyeSlash, faFolderOpen, faTrashAlt
}
},
created() {
this.connection = this.$root.stream.useSharedConnection('drive');
this.connection.on('fileCreated', this.onStreamDriveFileCreated);
this.connection.on('fileUpdated', this.onStreamDriveFileUpdated);
this.connection.on('fileDeleted', this.onStreamDriveFileDeleted);
},
beforeDestroy() {
this.connection.dispose();
async created() {
if (this.$store.state.settings.uploadFolder) {
this.uploadFolder = await this.$root.api('drive/folders/show', {
folderId: this.$store.state.settings.uploadFolder
});
}
},
methods: {
onStreamDriveFileCreated(file) {
this.$refs.drive.prepend(file);
},
onStreamDriveFileUpdated(file) {
// TODO
},
onStreamDriveFileDeleted(fileId) {
this.$refs.drive.remove(x => x.id === fileId);
},
download() {
window.open(this.selected.url, '_blank');
},
async del() {
const { canceled } = await this.$root.dialog({
type: 'warning',
text: this.$t('driveFileDeleteConfirm', { name: this.selected.name }),
showCancelButton: true
});
if (canceled) return;
this.$root.api('drive/files/delete', {
fileId: this.selected.id
chooseUploadFolder() {
selectDriveFolder(this.$root, false).then(async folder => {
await this.$store.dispatch('settings/set', { key: 'uploadFolder', value: folder ? folder.id : null });
this.$root.dialog({
type: 'success',
iconOnly: true, autoClose: true
});
if (this.$store.state.settings.uploadFolder) {
this.uploadFolder = await this.$root.api('drive/folders/show', {
folderId: this.$store.state.settings.uploadFolder
});
} else {
this.uploadFolder = null;
}
});
}
}
@@ -111,102 +61,6 @@ export default Vue.extend({
<style lang="scss" scoped>
.uawsfosz {
> ._content {
max-height: 350px;
overflow: auto;
> .drive {
> .file {
display: grid;
margin: 0 auto;
grid-template-columns: 64px 1fr;
grid-column-gap: 10px;
cursor: pointer;
&.selected {
background: var(--accent);
box-shadow: 0 0 0 8px var(--accent);
color: #fff;
}
&:not(:last-child) {
margin-bottom: 16px;
}
> .thumbnail {
width: 64px;
height: 64px;
}
> .body {
display: block;
word-break: break-all;
padding-top: 4px;
> .name {
display: block;
margin: 0;
padding: 0;
font-size: 0.9em;
font-weight: bold;
word-break: break-word;
> .ext {
opacity: 0.5;
}
}
> .tags {
display: block;
margin: 4px 0 0 0;
padding: 0;
list-style: none;
font-size: 0.5em;
> .tag {
display: inline-block;
margin: 0 5px 0 0;
padding: 1px 5px;
border-radius: 2px;
}
}
> footer {
display: block;
margin: 4px 0 0 0;
font-size: 0.7em;
> .separator {
padding: 0 4px;
}
> .type {
opacity: 0.7;
> .icon {
margin-right: 4px;
}
}
> .data-size {
opacity: 0.7;
}
> .created-at {
opacity: 0.7;
> [data-icon] {
margin-right: 2px;
}
}
> .nsfw {
color: #bf4633;
}
}
}
}
}
}
}
</style>

View File

@@ -32,7 +32,9 @@
<x-integration/>
<x-api/>
<mk-button @click="$root.signout()" primary style="margin: var(--margin) auto;">{{ $t('logout') }}</mk-button>
<router-link class="_panel _buttonPrimary" to="/my/apps" style="margin: var(--margin) auto;">{{ $t('installedApps') }}</router-link>
<button class="_panel _buttonPrimary" @click="$root.signout()" style="margin: var(--margin) auto;">{{ $t('logout') }}</button>
</div>
</template>

View File

@@ -5,7 +5,7 @@
<section class="_card">
<div class="_content">
<img src="https://xn--931a.moe/assets/not-found.png" class="_ghost"/>
<img src="https://xn--931a.moe/assets/not-found.jpg" class="_ghost"/>
<div>{{ $t('notFoundDescription') }}</div>
</div>
</section>

View File

@@ -13,7 +13,7 @@
<x-notes v-if="showNext" ref="next" :pagination="next"/>
<hr v-if="showNext"/>
<mk-remote-caution v-if="note.user.host != null" :href="note.uri" style="margin-bottom: var(--margin)"/>
<mk-remote-caution v-if="note.user.host != null" :href="note.url || note.uri" style="margin-bottom: var(--margin)"/>
<x-note :note="note" :key="note.id" :detail="true"/>
<div v-if="error">
<mk-error @retry="fetch()"/>

View File

@@ -10,6 +10,7 @@
<option value="dialog">{{ $t('_pages.blocks._button._action.dialog') }}</option>
<option value="resetRandom">{{ $t('_pages.blocks._button._action.resetRandom') }}</option>
<option value="pushEvent">{{ $t('_pages.blocks._button._action.pushEvent') }}</option>
<option value="callAiScript">{{ $t('_pages.blocks._button._action.callAiScript') }}</option>
</mk-select>
<template v-if="value.action === 'dialog'">
<mk-input v-model="value.content"><span>{{ $t('_pages.blocks._button._action._dialog.content') }}</span></mk-input>
@@ -20,15 +21,18 @@
<mk-select v-model="value.var">
<template #label>{{ $t('_pages.blocks._button._action._pushEvent.variable') }}</template>
<option :value="null">{{ $t('_pages.blocks._button._action._pushEvent.no-variable') }}</option>
<option v-for="v in aiScript.getVarsByType()" :value="v.name">{{ v.name }}</option>
<option v-for="v in hpml.getVarsByType()" :value="v.name">{{ v.name }}</option>
<optgroup :label="$t('_pages.script.pageVariables')">
<option v-for="v in aiScript.getPageVarsByType()" :value="v">{{ v }}</option>
<option v-for="v in hpml.getPageVarsByType()" :value="v">{{ v }}</option>
</optgroup>
<optgroup :label="$t('_pages.script.enviromentVariables')">
<option v-for="v in aiScript.getEnvVarsByType()" :value="v">{{ v }}</option>
<option v-for="v in hpml.getEnvVarsByType()" :value="v">{{ v }}</option>
</optgroup>
</mk-select>
</template>
<template v-else-if="value.action === 'callAiScript'">
<mk-input v-model="value.fn"><span>{{ $t('_pages.blocks._button._action._callAiScript.functionName') }}</span></mk-input>
</template>
</section>
</x-container>
</template>
@@ -53,7 +57,7 @@ export default Vue.extend({
value: {
required: true
},
aiScript: {
hpml: {
required: true,
},
},
@@ -72,6 +76,7 @@ export default Vue.extend({
if (this.value.message == null) Vue.set(this.value, 'message', null);
if (this.value.primary == null) Vue.set(this.value, 'primary', false);
if (this.value.var == null) Vue.set(this.value, 'var', null);
if (this.value.fn == null) Vue.set(this.value, 'fn', null);
},
});
</script>

View File

@@ -0,0 +1,45 @@
<template>
<x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faPaintBrush"/> {{ $t('_pages.blocks.canvas') }}</template>
<section style="padding: 0 16px 0 16px;">
<mk-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('_pages.blocks._canvas.id') }}</span></mk-input>
<mk-input v-model="value.width" type="number"><span>{{ $t('_pages.blocks._canvas.width') }}</span><template #suffix>px</template></mk-input>
<mk-input v-model="value.height" type="number"><span>{{ $t('_pages.blocks._canvas.height') }}</span><template #suffix>px</template></mk-input>
</section>
</x-container>
</template>
<script lang="ts">
import Vue from 'vue';
import { faPaintBrush, faMagic } from '@fortawesome/free-solid-svg-icons';
import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkInput from '../../../components/ui/input.vue';
export default Vue.extend({
i18n,
components: {
XContainer, MkInput
},
props: {
value: {
required: true
},
},
data() {
return {
faPaintBrush, faMagic
};
},
created() {
if (this.value.name == null) Vue.set(this.value, 'name', '');
if (this.value.width == null) Vue.set(this.value, 'width', 300);
if (this.value.height == null) Vue.set(this.value, 'height', 200);
},
});
</script>

View File

@@ -2,7 +2,7 @@
<x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faQuestion"/> {{ $t('_pages.blocks.if') }}</template>
<template #func>
<button @click="add()">
<button @click="add()" class="_button">
<fa :icon="faPlus"/>
</button>
</template>
@@ -10,16 +10,16 @@
<section class="romcojzs">
<mk-select v-model="value.var">
<template #label>{{ $t('_pages.blocks._if.variable') }}</template>
<option v-for="v in aiScript.getVarsByType('boolean')" :value="v.name">{{ v.name }}</option>
<option v-for="v in hpml.getVarsByType('boolean')" :value="v.name">{{ v.name }}</option>
<optgroup :label="$t('_pages.script.pageVariables')">
<option v-for="v in aiScript.getPageVarsByType('boolean')" :value="v">{{ v }}</option>
<option v-for="v in hpml.getPageVarsByType('boolean')" :value="v">{{ v }}</option>
</optgroup>
<optgroup :label="$t('_pages.script.enviromentVariables')">
<option v-for="v in aiScript.getEnvVarsByType('boolean')" :value="v">{{ v }}</option>
<option v-for="v in hpml.getEnvVarsByType('boolean')" :value="v">{{ v }}</option>
</optgroup>
</mk-select>
<x-blocks class="children" v-model="value.children" :ai-script="aiScript"/>
<x-blocks class="children" v-model="value.children" :hpml="hpml"/>
</section>
</x-container>
</template>
@@ -45,7 +45,7 @@ export default Vue.extend({
value: {
required: true
},
aiScript: {
hpml: {
required: true,
},
},

View File

@@ -2,8 +2,10 @@
<x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faPaperPlane"/> {{ $t('_pages.blocks.post') }}</template>
<section style="padding: 0 16px 16px 16px;">
<section style="padding: 16px;">
<mk-textarea v-model="value.text">{{ $t('_pages.blocks._post.text') }}</mk-textarea>
<mk-switch v-model="value.attachCanvasImage"><span>{{ $t('_pages.blocks._post.attachCanvasImage') }}</span></mk-switch>
<mk-input v-if="value.attachCanvasImage" v-model="value.canvasId"><span>{{ $t('_pages.blocks._post.canvasId') }}</span></mk-input>
</section>
</x-container>
</template>
@@ -14,12 +16,14 @@ import { faPaperPlane } from '@fortawesome/free-regular-svg-icons';
import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkTextarea from '../../../components/ui/textarea.vue';
import MkInput from '../../../components/ui/input.vue';
import MkSwitch from '../../../components/ui/switch.vue';
export default Vue.extend({
i18n,
components: {
XContainer, MkTextarea
XContainer, MkTextarea, MkInput, MkSwitch
},
props: {
@@ -36,6 +40,8 @@ export default Vue.extend({
created() {
if (this.value.text == null) Vue.set(this.value, 'text', '');
if (this.value.attachCanvasImage == null) Vue.set(this.value, 'attachCanvasImage', false);
if (this.value.canvasId == null) Vue.set(this.value, 'canvasId', '');
},
});
</script>

View File

@@ -2,16 +2,16 @@
<x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faStickyNote"/> {{ value.title }}</template>
<template #func>
<button @click="rename()">
<button @click="rename()" class="_button">
<fa :icon="faPencilAlt"/>
</button>
<button @click="add()">
<button @click="add()" class="_button">
<fa :icon="faPlus"/>
</button>
</template>
<section class="ilrvjyvi">
<x-blocks class="children" v-model="value.children" :ai-script="aiScript"/>
<x-blocks class="children" v-model="value.children" :hpml="hpml"/>
</section>
</x-container>
</template>
@@ -37,7 +37,7 @@ export default Vue.extend({
value: {
required: true
},
aiScript: {
hpml: {
required: true,
},
},

View File

@@ -2,7 +2,7 @@
<x-container @remove="() => $emit('remove')" :draggable="true">
<template #header><fa :icon="faAlignLeft"/> {{ $t('_pages.blocks.text') }}</template>
<section class="ihymsbbe">
<section class="vckmsadr">
<textarea v-model="value.text"></textarea>
</section>
</x-container>
@@ -40,7 +40,7 @@ export default Vue.extend({
</script>
<style lang="scss" scoped>
.ihymsbbe {
.vckmsadr {
> textarea {
display: block;
-webkit-appearance: none;
@@ -55,6 +55,7 @@ export default Vue.extend({
background: transparent;
color: var(--fg);
font-size: 14px;
box-sizing: border-box;
}
}
</style>

View File

@@ -55,6 +55,7 @@ export default Vue.extend({
background: transparent;
color: var(--fg);
font-size: 14px;
box-sizing: border-box;
}
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<x-draggable tag="div" :list="blocks" handle=".drag-handle" :group="{ name: 'blocks' }" animation="150" swap-threshold="0.5">
<component v-for="block in blocks" :is="'x-' + block.type" :value="block" @input="updateItem" @remove="() => removeItem(block)" :key="block.id" :ai-script="aiScript"/>
<component v-for="block in blocks" :is="'x-' + block.type" :value="block" @input="updateItem" @remove="() => removeItem(block)" :key="block.id" :hpml="hpml"/>
</x-draggable>
</template>
@@ -20,10 +20,11 @@ import XIf from './els/page-editor.el.if.vue';
import XPost from './els/page-editor.el.post.vue';
import XCounter from './els/page-editor.el.counter.vue';
import XRadioButton from './els/page-editor.el.radio-button.vue';
import XCanvas from './els/page-editor.el.canvas.vue';
export default Vue.extend({
components: {
XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter, XRadioButton
XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter, XRadioButton, XCanvas
},
props: {
@@ -31,7 +32,7 @@ export default Vue.extend({
type: Array,
required: true
},
aiScript: {
hpml: {
required: true,
},
},

View File

@@ -18,7 +18,7 @@
</header>
<p v-show="showBody" class="error" v-if="error != null">{{ $t('_pages.script.typeError', { slot: error.arg + 1, expect: $t(`script.types.${error.expect}`), actual: $t(`script.types.${error.actual}`) }) }}</p>
<p v-show="showBody" class="warn" v-if="warn != null">{{ $t('_pages.script.thereIsEmptySlot', { slot: warn.slot + 1 }) }}</p>
<div v-show="showBody">
<div v-show="showBody" class="body">
<slot></slot>
</div>
</div>
@@ -148,5 +148,17 @@ export default Vue.extend({
padding: 16px 16px 0 16px;
font-size: 14px;
}
> .body {
::v-deep .juejbjww, ::v-deep .eiipwacr {
&:not(.inline):first-child {
margin-top: 28px;
}
&:not(.inline):last-child {
margin-bottom: 20px;
}
}
}
}
</style>

View File

@@ -2,7 +2,7 @@
<x-container :removable="removable" @remove="() => $emit('remove')" :error="error" :warn="warn" :draggable="draggable">
<template #header><fa v-if="icon" :icon="icon"/> <template v-if="title">{{ title }} <span class="turmquns" v-if="typeText">({{ typeText }})</span></template><template v-else-if="typeText">{{ typeText }}</template></template>
<template #func>
<button @click="changeType()">
<button @click="changeType()" class="_button">
<fa :icon="faPencilAlt"/>
</button>
</template>
@@ -24,30 +24,33 @@
</section>
<section v-else-if="value.type === 'ref'" class="hpdwcrvs">
<select v-model="value.value">
<option v-for="v in aiScript.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option>
<option v-for="v in hpml.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option>
<optgroup :label="$t('_pages.script.argVariables')">
<option v-for="v in fnSlots" :value="v.name">{{ v.name }}</option>
</optgroup>
<optgroup :label="$t('_pages.script.pageVariables')">
<option v-for="v in aiScript.getPageVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
<option v-for="v in hpml.getPageVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
</optgroup>
<optgroup :label="$t('_pages.script.enviromentVariables')">
<option v-for="v in aiScript.getEnvVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
<option v-for="v in hpml.getEnvVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
</optgroup>
</select>
</section>
<section v-else-if="value.type === 'aiScriptVar'" class="tbwccoaw">
<input v-model="value.value"/>
</section>
<section v-else-if="value.type === 'fn'" class="" style="padding:0 16px 16px 16px;">
<mk-textarea v-model="slots">
<span>{{ $t('_pages.script.blocks._fn.slots') }}</span>
<template #desc>{{ $t('_pages.script.blocks._fn.slots-info') }}</template>
</mk-textarea>
<x-v v-if="value.value.expression" v-model="value.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :ai-script="aiScript" :fn-slots="value.value.slots" :name="name"/>
<x-v v-if="value.value.expression" v-model="value.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :hpml="hpml" :fn-slots="value.value.slots" :name="name"/>
</section>
<section v-else-if="value.type.startsWith('fn:')" class="" style="padding:16px;">
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="aiScript.getVarByName(value.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :ai-script="aiScript" :name="name" :key="i"/>
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="hpml.getVarByName(value.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :hpml="hpml" :name="name" :key="i"/>
</section>
<section v-else class="" style="padding:16px;">
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="$t(`_pages.script.blocks._${value.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :ai-script="aiScript" :name="name" :fn-slots="fnSlots" :key="i"/>
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="$t(`_pages.script.blocks._${value.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :hpml="hpml" :name="name" :fn-slots="fnSlots" :key="i"/>
</section>
</x-container>
</template>
@@ -59,7 +62,7 @@ import { v4 as uuid } from 'uuid';
import i18n from '../../i18n';
import XContainer from './page-editor.container.vue';
import MkTextarea from '../../components/ui/textarea.vue';
import { isLiteralBlock, funcDefs, blockDefs } from '../../scripts/aiscript/index';
import { isLiteralBlock, funcDefs, blockDefs } from '../../scripts/hpml/index';
export default Vue.extend({
i18n,
@@ -85,7 +88,7 @@ export default Vue.extend({
required: false,
default: false
},
aiScript: {
hpml: {
required: true,
},
name: {
@@ -153,7 +156,7 @@ export default Vue.extend({
if (this.value.type && this.value.type.startsWith('fn:')) {
const fnName = this.value.type.split(':')[1];
const fn = this.aiScript.getVarByName(fnName);
const fn = this.hpml.getVarByName(fnName);
const empties = [];
for (let i = 0; i < fn.value.slots.length; i++) {
@@ -199,9 +202,9 @@ export default Vue.extend({
deep: true
});
this.$watch('aiScript.variables', () => {
this.$watch('hpml.variables', () => {
if (this.type != null && this.value) {
this.error = this.aiScript.typeCheck(this.value);
this.error = this.hpml.typeCheck(this.value);
}
}, {
deep: true
@@ -223,7 +226,7 @@ export default Vue.extend({
},
_getExpectedType(slot: number) {
return this.aiScript.getExpectedType(this.value, slot);
return this.hpml.getExpectedType(this.value, slot);
}
}
});
@@ -258,6 +261,7 @@ export default Vue.extend({
font-size: 16px;
background: transparent;
color: var(--fg);
box-sizing: border-box;
}
> textarea {

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