Compare commits
	
		
			168 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7c3873887d | ||
| 
						 | 
					247ea4cf12 | ||
| 
						 | 
					0b7af5c669 | ||
| 
						 | 
					2b62a4e2e5 | ||
| 
						 | 
					65bfa3c0d6 | ||
| 
						 | 
					84db15694d | ||
| 
						 | 
					746189ba37 | ||
| 
						 | 
					74e845b3ac | ||
| 
						 | 
					90fe70540e | ||
| 
						 | 
					f28af75191 | ||
| 
						 | 
					924bb2bc70 | ||
| 
						 | 
					19d60f3d51 | ||
| 
						 | 
					6903476868 | ||
| 
						 | 
					cf0dccc209 | ||
| 
						 | 
					cfd959129d | ||
| 
						 | 
					819287951c | ||
| 
						 | 
					e136193925 | ||
| 
						 | 
					8c631864d9 | ||
| 
						 | 
					d7d0f6ae2e | ||
| 
						 | 
					b83b3fb9d1 | ||
| 
						 | 
					dfce5bc0af | ||
| 
						 | 
					3487ddabea | ||
| 
						 | 
					2dbff75e7a | ||
| 
						 | 
					02465ded9f | ||
| 
						 | 
					ffcd387945 | ||
| 
						 | 
					4806346707 | ||
| 
						 | 
					31c3f6abf7 | ||
| 
						 | 
					83e47fdd60 | ||
| 
						 | 
					340ce7fa4c | ||
| 
						 | 
					ac86fee9b4 | ||
| 
						 | 
					6dfa283d7a | ||
| 
						 | 
					0cce8a4d21 | ||
| 
						 | 
					1c6d9ab2ef | ||
| 
						 | 
					6ca265e579 | ||
| 
						 | 
					c612c4bf18 | ||
| 
						 | 
					481a791a60 | ||
| 
						 | 
					cb516c2943 | ||
| 
						 | 
					c0abd6f0c0 | ||
| 
						 | 
					47695ed685 | ||
| 
						 | 
					4ca8020ef5 | ||
| 
						 | 
					bfac83d5b8 | ||
| 
						 | 
					4cd2e55fd3 | ||
| 
						 | 
					61c7e7bc48 | ||
| 
						 | 
					bef41718e2 | ||
| 
						 | 
					5b4b52bb97 | ||
| 
						 | 
					8901b6d774 | ||
| 
						 | 
					e3a24e9215 | ||
| 
						 | 
					a515c1f53e | ||
| 
						 | 
					2e22874dec | ||
| 
						 | 
					30f0b1c30d | ||
| 
						 | 
					600aea4dbb | ||
| 
						 | 
					f5d53d784d | ||
| 
						 | 
					1061e1f7ae | ||
| 
						 | 
					1d5fc04aa6 | ||
| 
						 | 
					d1cf0c7998 | ||
| 
						 | 
					84218abf2b | ||
| 
						 | 
					5bebdb2511 | ||
| 
						 | 
					9c8e9b4165 | ||
| 
						 | 
					7b786bfde3 | ||
| 
						 | 
					42a08642a4 | ||
| 
						 | 
					e88f7ca7b2 | ||
| 
						 | 
					c26ed1421b | ||
| 
						 | 
					ed2f94a3c1 | ||
| 
						 | 
					daba7fe87c | ||
| 
						 | 
					afc9caf7bf | ||
| 
						 | 
					67697a7aa6 | ||
| 
						 | 
					1623d9e70c | ||
| 
						 | 
					c304351335 | ||
| 
						 | 
					c1520763c6 | ||
| 
						 | 
					4853bc9414 | ||
| 
						 | 
					e7c865f8e3 | ||
| 
						 | 
					46cb377bc2 | ||
| 
						 | 
					373a5ba3e1 | ||
| 
						 | 
					3bedef67c8 | ||
| 
						 | 
					17ea19ada8 | ||
| 
						 | 
					1f5b2285fd | ||
| 
						 | 
					17f0001966 | ||
| 
						 | 
					04ba09a6af | ||
| 
						 | 
					70d2744319 | ||
| 
						 | 
					6b2f0929ec | ||
| 
						 | 
					f2629bd3f2 | ||
| 
						 | 
					9e6c29c3c0 | ||
| 
						 | 
					abda973094 | ||
| 
						 | 
					86b08dd5bd | ||
| 
						 | 
					617e331f0f | ||
| 
						 | 
					cc438a9372 | ||
| 
						 | 
					b0fb218bfd | ||
| 
						 | 
					fc85a607e6 | ||
| 
						 | 
					fb244c45e3 | ||
| 
						 | 
					c123784c54 | ||
| 
						 | 
					342a5276fc | ||
| 
						 | 
					51a32846ee | ||
| 
						 | 
					35865429a8 | ||
| 
						 | 
					aadd5b95b8 | ||
| 
						 | 
					f9f2ca51ac | ||
| 
						 | 
					1cb93a8c10 | ||
| 
						 | 
					7e5dbb2ba5 | ||
| 
						 | 
					2772e3d80e | ||
| 
						 | 
					223c578734 | ||
| 
						 | 
					d01315dee2 | ||
| 
						 | 
					7dafb4ce4c | ||
| 
						 | 
					9671db9b14 | ||
| 
						 | 
					bec559f67c | ||
| 
						 | 
					14053c1394 | ||
| 
						 | 
					55e4b1c828 | ||
| 
						 | 
					dda3421159 | ||
| 
						 | 
					45e7488e60 | ||
| 
						 | 
					30c7bd66b7 | ||
| 
						 | 
					af4f5bdac0 | ||
| 
						 | 
					3d1a8cc341 | ||
| 
						 | 
					0e52fb2544 | ||
| 
						 | 
					e6d6c0a17c | ||
| 
						 | 
					cfd2d47e00 | ||
| 
						 | 
					83301a879d | ||
| 
						 | 
					d7881ba129 | ||
| 
						 | 
					b9fef1edf7 | ||
| 
						 | 
					2c606f7b23 | ||
| 
						 | 
					03797607ed | ||
| 
						 | 
					254b7f500d | ||
| 
						 | 
					51edd51bf2 | ||
| 
						 | 
					0d403f4a3f | ||
| 
						 | 
					0fa134addd | ||
| 
						 | 
					7002270084 | ||
| 
						 | 
					1c5452d047 | ||
| 
						 | 
					f0d62c07bf | ||
| 
						 | 
					496ca55bba | ||
| 
						 | 
					79cfba226b | ||
| 
						 | 
					f69b60dffe | ||
| 
						 | 
					513385133f | ||
| 
						 | 
					6f1e2f6636 | ||
| 
						 | 
					8ae94c034d | ||
| 
						 | 
					cd9696f25e | ||
| 
						 | 
					d62a6bab41 | ||
| 
						 | 
					20df002746 | ||
| 
						 | 
					fa6b01546e | ||
| 
						 | 
					91b37a6e52 | ||
| 
						 | 
					d8171d7c8b | ||
| 
						 | 
					fa96e2daf1 | ||
| 
						 | 
					87708c3b84 | ||
| 
						 | 
					6319023cc9 | ||
| 
						 | 
					efad9d1b60 | ||
| 
						 | 
					a1dea657fa | ||
| 
						 | 
					6b1b75717b | ||
| 
						 | 
					efe08e0bd3 | ||
| 
						 | 
					62892c4894 | ||
| 
						 | 
					0c2a62da11 | ||
| 
						 | 
					5bc9e9aadd | ||
| 
						 | 
					112c33d35b | ||
| 
						 | 
					864da3030f | ||
| 
						 | 
					f2e719b361 | ||
| 
						 | 
					6aab515389 | ||
| 
						 | 
					819b535ab0 | ||
| 
						 | 
					60e95ac2ac | ||
| 
						 | 
					9b94ddff0a | ||
| 
						 | 
					174f8022eb | ||
| 
						 | 
					ddc3c5ba68 | ||
| 
						 | 
					a7e6b766be | ||
| 
						 | 
					befc35a3ac | ||
| 
						 | 
					2e9bbf389e | ||
| 
						 | 
					80b5fda292 | ||
| 
						 | 
					c48cbd95f6 | ||
| 
						 | 
					931bdc6aac | ||
| 
						 | 
					7f81506c8b | ||
| 
						 | 
					b4b9e76c8d | ||
| 
						 | 
					e5a3dcf868 | ||
| 
						 | 
					825648535c | ||
| 
						 | 
					5cbc908ba3 | ||
| 
						 | 
					895cf53ee1 | 
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "左に重ねる"
 | 
					    stack-left: "左に重ねる"
 | 
				
			||||||
    pop-right: "右に出す"
 | 
					    pop-right: "右に出す"
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
					  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
				
			||||||
  permission-ask: "このアプリは次の権限を要求しています:"
 | 
					  permission-ask: "このアプリは次の権限を要求しています:"
 | 
				
			||||||
@@ -874,7 +875,7 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "パスワードを変更する"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "現在のパスワードを入力してください"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "新しいパスワードを入力してください"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "閉じる"
 | 
					  close: "閉じる"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿のみ"
 | 
					  is-media-only: "メディア投稿のみ"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "フォローされています"
 | 
					  follows-you: "フォローされています"
 | 
				
			||||||
  following: "フォロー"
 | 
					  following: "フォロー"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "Nach links schichten"
 | 
					    stack-left: "Nach links schichten"
 | 
				
			||||||
    pop-right: "Rechts andocken"
 | 
					    pop-right: "Rechts andocken"
 | 
				
			||||||
  dev: "Fehler beim Erstellen der Applikation. Bitte versuche es erneut."
 | 
					  dev: "Fehler beim Erstellen der Applikation. Bitte versuche es erneut."
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
					  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
				
			||||||
  permission-ask: "このアプリは次の権限を要求しています:"
 | 
					  permission-ask: "このアプリは次の権限を要求しています:"
 | 
				
			||||||
@@ -874,13 +875,13 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "Passwort ändern"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "Derzeitiges Passwort eingeben"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "Neues Passwort eingeben"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
  enter-new-password-again: "Neues Passwort erneut eingeben"
 | 
					  enter-new-password-again: "もう一度新しいパスワードを入力してください"
 | 
				
			||||||
  not-match: "Passwörter stimmen nicht überein."
 | 
					  not-match: "新しいパスワードが一致しません"
 | 
				
			||||||
  changed: "Passwort geändert"
 | 
					  changed: "パスワードを変更しました"
 | 
				
			||||||
desktop/views/components/sub-note-content.vue:
 | 
					desktop/views/components/sub-note-content.vue:
 | 
				
			||||||
  private: "この投稿は非公開です"
 | 
					  private: "この投稿は非公開です"
 | 
				
			||||||
  deleted: "この投稿は削除されました"
 | 
					  deleted: "この投稿は削除されました"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "閉じる"
 | 
					  close: "閉じる"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿のみ"
 | 
					  is-media-only: "メディア投稿のみ"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "フォローされています"
 | 
					  follows-you: "フォローされています"
 | 
				
			||||||
  following: "フォロー"
 | 
					  following: "フォロー"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "Stack to the left"
 | 
					    stack-left: "Stack to the left"
 | 
				
			||||||
    pop-right: "Dock on the right"
 | 
					    pop-right: "Dock on the right"
 | 
				
			||||||
  dev: "Failed to create the application. Please try again."
 | 
					  dev: "Failed to create the application. Please try again."
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "Ai-chan kawaii!"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "Would you <b>allow</b> <i>{{ app.name }}</i> to access your account?"
 | 
					  share-access: "Would you <b>allow</b> <i>{{ app.name }}</i> to access your account?"
 | 
				
			||||||
  permission-ask: "This application requires the following permissions:"
 | 
					  permission-ask: "This application requires the following permissions:"
 | 
				
			||||||
@@ -249,7 +250,7 @@ common/views/components/games/reversi/reversi.room.vue:
 | 
				
			|||||||
  this-game-is-started-soon: "The game will begin in seconds"
 | 
					  this-game-is-started-soon: "The game will begin in seconds"
 | 
				
			||||||
  waiting-for-other: "Waiting for the opponent"
 | 
					  waiting-for-other: "Waiting for the opponent"
 | 
				
			||||||
  waiting-for-me: "Waiting for the your preparation"
 | 
					  waiting-for-me: "Waiting for the your preparation"
 | 
				
			||||||
  waiting-for-both: "Prepareing"
 | 
					  waiting-for-both: "Preparing"
 | 
				
			||||||
  cancel: "Cancel"
 | 
					  cancel: "Cancel"
 | 
				
			||||||
  ready: "Ready"
 | 
					  ready: "Ready"
 | 
				
			||||||
  cancel-ready: "Cancel \"Ready\""
 | 
					  cancel-ready: "Cancel \"Ready\""
 | 
				
			||||||
@@ -874,13 +875,13 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "Blocking"
 | 
					  block: "Blocking"
 | 
				
			||||||
  no-muted-users: "No muted users"
 | 
					  no-muted-users: "No muted users"
 | 
				
			||||||
  no-blocked-users: "No blocked users"
 | 
					  no-blocked-users: "No blocked users"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "Change password"
 | 
					  reset: "Change password"
 | 
				
			||||||
  enter-current-password: "Enter the current password"
 | 
					  enter-current-password: "Enter the current password"
 | 
				
			||||||
  enter-new-password: "Enter the new password"
 | 
					  enter-new-password: "Enter the new password"
 | 
				
			||||||
  enter-new-password-again: "Enter new password again"
 | 
					  enter-new-password-again: "Enter the new password again"
 | 
				
			||||||
  not-match: "The new passwords do not match"
 | 
					  not-match: "The new passwords do not match"
 | 
				
			||||||
  changed: "Password updated"
 | 
					  changed: "Password changed"
 | 
				
			||||||
desktop/views/components/sub-note-content.vue:
 | 
					desktop/views/components/sub-note-content.vue:
 | 
				
			||||||
  private: "This post is private"
 | 
					  private: "This post is private"
 | 
				
			||||||
  deleted: "This post has been deleted"
 | 
					  deleted: "This post has been deleted"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "Pop-out"
 | 
					  popout: "Pop-out"
 | 
				
			||||||
  close: "Close"
 | 
					  close: "Close"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "Dashboard"
 | 
					  dashboard: "Dashboard"
 | 
				
			||||||
 | 
					  instance: "Instance"
 | 
				
			||||||
 | 
					  emoji: "Emoji"
 | 
				
			||||||
  users: "Users"
 | 
					  users: "Users"
 | 
				
			||||||
  update: "Updates"
 | 
					  update: "Update"
 | 
				
			||||||
  announcements: "Announcements"
 | 
					  announcements: "Announcements"
 | 
				
			||||||
  hashtags: "Hashtags"
 | 
					  hashtags: "Hashtags"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Back to Misskey"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "Dashboard"
 | 
					  dashboard: "Dashboard"
 | 
				
			||||||
  all-users: "All Users"
 | 
					  accounts: "Accounts"
 | 
				
			||||||
  original-users: "Users on this instance"
 | 
					  notes: "Notes"
 | 
				
			||||||
  all-notes: "All the posts"
 | 
					  drive: "Drive"
 | 
				
			||||||
  original-notes: "Posts on this instance"
 | 
					  instances: "Instances"
 | 
				
			||||||
 | 
					  this-instance: "This instance"
 | 
				
			||||||
 | 
					  federated: "Federated"
 | 
				
			||||||
  invite: "Invite"
 | 
					  invite: "Invite"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "Chart"
 | 
				
			||||||
 | 
					  per-day: "per Day"
 | 
				
			||||||
 | 
					  per-hour: "per Hour"
 | 
				
			||||||
 | 
					  federation: "Federation"
 | 
				
			||||||
 | 
					  notes: "Posts"
 | 
				
			||||||
 | 
					  users: "Users"
 | 
				
			||||||
 | 
					  drive: "Drive"
 | 
				
			||||||
 | 
					  network: "Network"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "The number of instances: increase/decrease"
 | 
				
			||||||
 | 
					    federation-instances-total: "Total number of instances"
 | 
				
			||||||
 | 
					    notes: "The number of posts: increase/decrease (Combined)"
 | 
				
			||||||
 | 
					    local-notes: "The number of posts: increase/decrease (Local)"
 | 
				
			||||||
 | 
					    remote-notes: "The number of posts: increase/decrease (Remote)"
 | 
				
			||||||
 | 
					    notes-total: "Total posts"
 | 
				
			||||||
 | 
					    users: "The number of users: increase/decrease"
 | 
				
			||||||
 | 
					    users-total: "Total users"
 | 
				
			||||||
 | 
					    drive: "Capacity used as the storage: increase/decrease"
 | 
				
			||||||
 | 
					    drive-total: "Total usage of Drive"
 | 
				
			||||||
 | 
					    drive-files: "The number of files on the storage: increase/decrease"
 | 
				
			||||||
 | 
					    drive-files-total: "Total number of files on Drive"
 | 
				
			||||||
 | 
					    network-requests: "Requests"
 | 
				
			||||||
 | 
					    network-time: "Response time"
 | 
				
			||||||
 | 
					    network-usage: "Traffic"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "Suspend a user"
 | 
					  suspend-user: "Suspend a user"
 | 
				
			||||||
  suspend: "Suspend"
 | 
					  suspend: "Suspend"
 | 
				
			||||||
  suspended: "Successfully suspended."
 | 
					  suspended: "Successfully suspended."
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "Unsuspend users"
 | 
					  unsuspend-user: "Unsuspend users"
 | 
				
			||||||
  unsuspend: "Unsuspend"
 | 
					  unsuspend: "Unsuspend"
 | 
				
			||||||
  unsuspended: "The user has successfully unsuspended."
 | 
					  unsuspended: "The user has successfully unsuspended."
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "User account verification settings"
 | 
					  verify-user: "User account verification settings"
 | 
				
			||||||
  verify: "Verify account"
 | 
					  verify: "Verify account"
 | 
				
			||||||
  verified: "The account is now being verified"
 | 
					  verified: "The account is now being verified"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "User account unverification settings"
 | 
					  unverify-user: "User account unverification settings"
 | 
				
			||||||
  unverify: "Unverify account"
 | 
					  unverify: "Unverify account"
 | 
				
			||||||
  unverified: "The account is now being unverified"
 | 
					  unverified: "The account is now being unverified"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "Add emoji"
 | 
				
			||||||
 | 
					    name: "Emoji name"
 | 
				
			||||||
 | 
					    name-desc: "You can use the characters a~z 0~9 _"
 | 
				
			||||||
 | 
					    aliases: "Aliases"
 | 
				
			||||||
 | 
					    aliases-desc: "You can add more than one, separated by spaces."
 | 
				
			||||||
 | 
					    url: "Image URL"
 | 
				
			||||||
 | 
					    add: "Add"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "Emojis"
 | 
				
			||||||
 | 
					    update: "Update"
 | 
				
			||||||
 | 
					    remove: "Remove"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "Announcements"
 | 
					  announcements: "Announcements"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "Save"
 | 
				
			||||||
 | 
					  remove: "Remove"
 | 
				
			||||||
 | 
					  add: "Add"
 | 
				
			||||||
 | 
					  title: "Title"
 | 
				
			||||||
 | 
					  text: "Content"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "Only media posts"
 | 
					  is-media-only: "Only media posts"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "Sounds"
 | 
					  sound: "Sounds"
 | 
				
			||||||
  enable-sounds: "Enable sounds"
 | 
					  enable-sounds: "Enable sounds"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "Mark all posts as read"
 | 
					  mark-as-read-all-unread-notes: "Mark all posts as read"
 | 
				
			||||||
 | 
					  password: "Password"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "Follows you"
 | 
					  follows-you: "Follows you"
 | 
				
			||||||
  following: "Following"
 | 
					  following: "Following"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "A la izqda."
 | 
					    stack-left: "A la izqda."
 | 
				
			||||||
    pop-right: "A la dcha."
 | 
					    pop-right: "A la dcha."
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "¿Deseas <b>permitir</b> a <i>{{ app.name }}</i> acceder a tu cuenta?"
 | 
					  share-access: "¿Deseas <b>permitir</b> a <i>{{ app.name }}</i> acceder a tu cuenta?"
 | 
				
			||||||
  permission-ask: "La aplicación requiere los siguientes permisos:"
 | 
					  permission-ask: "La aplicación requiere los siguientes permisos:"
 | 
				
			||||||
@@ -874,13 +875,13 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "Cambiar contraseña"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "Ingresar contraseña actual"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "Ingresar nueva contraseña"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
  enter-new-password-again: "Ingresar nueva contraseña de nuevo"
 | 
					  enter-new-password-again: "もう一度新しいパスワードを入力してください"
 | 
				
			||||||
  not-match: "Las nuevas contraseñas no se corresponden consigo mismas"
 | 
					  not-match: "新しいパスワードが一致しません"
 | 
				
			||||||
  changed: "Contraseña actualizada"
 | 
					  changed: "パスワードを変更しました"
 | 
				
			||||||
desktop/views/components/sub-note-content.vue:
 | 
					desktop/views/components/sub-note-content.vue:
 | 
				
			||||||
  private: "この投稿は非公開です"
 | 
					  private: "この投稿は非公開です"
 | 
				
			||||||
  deleted: "この投稿は削除されました"
 | 
					  deleted: "この投稿は削除されました"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "閉じる"
 | 
					  close: "閉じる"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿のみ"
 | 
					  is-media-only: "メディア投稿のみ"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "フォローされています"
 | 
					  follows-you: "フォローされています"
 | 
				
			||||||
  following: "フォロー"
 | 
					  following: "フォロー"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "Vers la gauche"
 | 
					    stack-left: "Vers la gauche"
 | 
				
			||||||
    pop-right: "Vers la droite"
 | 
					    pop-right: "Vers la droite"
 | 
				
			||||||
  dev: "Échec lors de la création de l’application. Veuillez réessayer."
 | 
					  dev: "Échec lors de la création de l’application. Veuillez réessayer."
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "Désirez-vous <b>autoriser</b> <i>{{ app.name }}</i> à avoir accès à votre compte ?"
 | 
					  share-access: "Désirez-vous <b>autoriser</b> <i>{{ app.name }}</i> à avoir accès à votre compte ?"
 | 
				
			||||||
  permission-ask: "Cette application nécessite les autorisations suivantes :"
 | 
					  permission-ask: "Cette application nécessite les autorisations suivantes :"
 | 
				
			||||||
@@ -444,7 +445,7 @@ common/views/components/profile-editor.vue:
 | 
				
			|||||||
  is-cat: "Ce compte est un Chat"
 | 
					  is-cat: "Ce compte est un Chat"
 | 
				
			||||||
  is-bot: "Ce compte est un Bot"
 | 
					  is-bot: "Ce compte est un Bot"
 | 
				
			||||||
  is-locked: "Demandes d’abonnements requièrent l’approbation"
 | 
					  is-locked: "Demandes d’abonnements requièrent l’approbation"
 | 
				
			||||||
  careful-bot: "Botからのフォローだけ承認制にする"
 | 
					  careful-bot: "Les demandes d’abonnements venant de Bots requièrent l’approbation"
 | 
				
			||||||
  advanced: "Avancé"
 | 
					  advanced: "Avancé"
 | 
				
			||||||
  privacy: "Vie privée"
 | 
					  privacy: "Vie privée"
 | 
				
			||||||
  save: "Mettre à jour le profil"
 | 
					  save: "Mettre à jour le profil"
 | 
				
			||||||
@@ -501,7 +502,7 @@ common/views/widgets/tips.vue:
 | 
				
			|||||||
  tips-line14: "ホームのカスタマイズ中、ウィジェットを右クリックしてデザインを変更できます"
 | 
					  tips-line14: "ホームのカスタマイズ中、ウィジェットを右クリックしてデザインを変更できます"
 | 
				
			||||||
  tips-line17: "Vous pouvez mettre un texte en surbrillance en le mettant entre ** **"
 | 
					  tips-line17: "Vous pouvez mettre un texte en surbrillance en le mettant entre ** **"
 | 
				
			||||||
  tips-line19: "Plusieurs fenêtres peuvent être détachées en dehors du navigateur."
 | 
					  tips-line19: "Plusieurs fenêtres peuvent être détachées en dehors du navigateur."
 | 
				
			||||||
  tips-line20: "カレンダーウィジェットのパーセンテージは、経過の割合を示しています"
 | 
					  tips-line20: "Pourcentage sur le widget calendrier qui indique le pourcentage de temps passé"
 | 
				
			||||||
  tips-line21: "Vous pouvez aussi utiliser l'API pour développer des Bots."
 | 
					  tips-line21: "Vous pouvez aussi utiliser l'API pour développer des Bots."
 | 
				
			||||||
  tips-line23: "Mayu est mignone avec ses sourcils."
 | 
					  tips-line23: "Mayu est mignone avec ses sourcils."
 | 
				
			||||||
  tips-line24: "Misskey a vu le jour en 2014"
 | 
					  tips-line24: "Misskey a vu le jour en 2014"
 | 
				
			||||||
@@ -548,7 +549,7 @@ desktop/views/components/charts.vue:
 | 
				
			|||||||
  drive: "Drive"
 | 
					  drive: "Drive"
 | 
				
			||||||
  network: "Réseau"
 | 
					  network: "Réseau"
 | 
				
			||||||
  charts:
 | 
					  charts:
 | 
				
			||||||
    federation-instances: "インスタンスの増減"
 | 
					    federation-instances: "Nombre d’instances : augmentation/diminution"
 | 
				
			||||||
    federation-instances-total: "Nombre total d’instances"
 | 
					    federation-instances-total: "Nombre total d’instances"
 | 
				
			||||||
    notes: "投稿の増減 (統合)"
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
    local-notes: "投稿の増減 (ローカル)"
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
@@ -781,7 +782,7 @@ desktop/views/components/settings.vue:
 | 
				
			|||||||
  timeline: "Chronologie"
 | 
					  timeline: "Chronologie"
 | 
				
			||||||
  show-my-renotes: "Afficher mes republications dans le fil"
 | 
					  show-my-renotes: "Afficher mes republications dans le fil"
 | 
				
			||||||
  show-renoted-my-notes: "Afficher mes republications dans les fils"
 | 
					  show-renoted-my-notes: "Afficher mes republications dans les fils"
 | 
				
			||||||
  show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する"
 | 
					  show-local-renotes: "Afficher les partages locaux sur les fils"
 | 
				
			||||||
  show-maps: "Afficher la carte"
 | 
					  show-maps: "Afficher la carte"
 | 
				
			||||||
  deck-column-align: "デッキのカラムの位置"
 | 
					  deck-column-align: "デッキのカラムの位置"
 | 
				
			||||||
  deck-column-align-center: "Centrer"
 | 
					  deck-column-align-center: "Centrer"
 | 
				
			||||||
@@ -865,21 +866,21 @@ common/views/components/api-settings.vue:
 | 
				
			|||||||
desktop/views/components/settings.apps.vue:
 | 
					desktop/views/components/settings.apps.vue:
 | 
				
			||||||
  no-apps: "Aucune application autorisée"
 | 
					  no-apps: "Aucune application autorisée"
 | 
				
			||||||
common/views/components/drive-settings.vue:
 | 
					common/views/components/drive-settings.vue:
 | 
				
			||||||
  max: "容量"
 | 
					  max: "Maximale"
 | 
				
			||||||
  in-use: "utilisé"
 | 
					  in-use: "utilisé"
 | 
				
			||||||
  stats: "Statistiques"
 | 
					  stats: "Statistiques"
 | 
				
			||||||
common/views/components/mute-and-block.vue:
 | 
					common/views/components/mute-and-block.vue:
 | 
				
			||||||
  mute-and-block: "ミュートとブロック"
 | 
					  mute-and-block: "Silencer / Bloquer"
 | 
				
			||||||
  mute: "ミュート"
 | 
					  mute: "Mettre en sourdine"
 | 
				
			||||||
  block: "ブロック"
 | 
					  block: "En cours blocage"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "Aucun utilisateur·rice n’est mis·e en sourdine"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "Aucun utilisateur·rice n’est bloqué·e"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "Changer votre mot de passe"
 | 
					  reset: "Modifier le mot de passe"
 | 
				
			||||||
  enter-current-password: "Entrez votre mot de passe actuel"
 | 
					  enter-current-password: "Entrez votre mot de passe actuel"
 | 
				
			||||||
  enter-new-password: "Entrez votre nouveau mot de passe"
 | 
					  enter-new-password: "Saisissez le nouveau mot de passe"
 | 
				
			||||||
  enter-new-password-again: "Entrez à nouveau le nouveau mot de passe"
 | 
					  enter-new-password-again: "Entrez à nouveau le nouveau mot de passe"
 | 
				
			||||||
  not-match: "Le nouveau mot de passe ne correspond pas."
 | 
					  not-match: "Les nouveaux mots de passe ne sont pas identiques"
 | 
				
			||||||
  changed: "Mot de passe modifié avec succès"
 | 
					  changed: "Mot de passe modifié avec succès"
 | 
				
			||||||
desktop/views/components/sub-note-content.vue:
 | 
					desktop/views/components/sub-note-content.vue:
 | 
				
			||||||
  private: "cette publication est privée"
 | 
					  private: "cette publication est privée"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "Fermer"
 | 
					  close: "Fermer"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "Tableau de bord"
 | 
					  dashboard: "Tableau de bord"
 | 
				
			||||||
 | 
					  instance: "Instance"
 | 
				
			||||||
 | 
					  emoji: "Emoji"
 | 
				
			||||||
  users: "Utilisateur·rice·s"
 | 
					  users: "Utilisateur·rice·s"
 | 
				
			||||||
  update: "Mises à jour"
 | 
					  update: "Mise à jour"
 | 
				
			||||||
  announcements: "Annonces"
 | 
					  announcements: "Annonces"
 | 
				
			||||||
  hashtags: "Hashtags"
 | 
					  hashtags: "Hashtags"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Retour vers Misskey"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "Tableau de bord"
 | 
					  dashboard: "Tableau de bord"
 | 
				
			||||||
  all-users: "Toutes les utilisateurrices"
 | 
					  accounts: "Comptes"
 | 
				
			||||||
  original-users: "Utilisateur·rice·s sur cette instance"
 | 
					  notes: "Notes"
 | 
				
			||||||
  all-notes: "Toutes les publications"
 | 
					  drive: "Lecteur"
 | 
				
			||||||
  original-notes: "Publications sur cette instance"
 | 
					  instances: "Instances"
 | 
				
			||||||
  invite: "Invitation"
 | 
					  this-instance: "Cette instance"
 | 
				
			||||||
 | 
					  federated: "Fédérées"
 | 
				
			||||||
 | 
					  invite: "Inviter"
 | 
				
			||||||
  banner-url: "URL de la bannière"
 | 
					  banner-url: "URL de la bannière"
 | 
				
			||||||
  disableRegistration: "Désactiver l’enregistrement de nouveaux utilisateurs·rices"
 | 
					  disableRegistration: "Désactiver l’enregistrement de nouveaux utilisateur·rice·s"
 | 
				
			||||||
  disableLocalTimeline: "Désactiver le fil local"
 | 
					  disableLocalTimeline: "Désactiver le fil local"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "Graph"
 | 
				
			||||||
 | 
					  per-day: "par jour"
 | 
				
			||||||
 | 
					  per-hour: "par heure"
 | 
				
			||||||
 | 
					  federation: "Fédération"
 | 
				
			||||||
 | 
					  notes: "Publications"
 | 
				
			||||||
 | 
					  users: "Utilisateur·rice·s"
 | 
				
			||||||
 | 
					  drive: "Lecteur"
 | 
				
			||||||
 | 
					  network: "Réseau"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "Nombre d’instances : augmentation/diminution"
 | 
				
			||||||
 | 
					    federation-instances-total: "Nombre total d’instances"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "Total des publications"
 | 
				
			||||||
 | 
					    users: "Nombre d’utilisateur·rice·s : augmentation/diminution"
 | 
				
			||||||
 | 
					    users-total: "Nombre total des utilisateur·rice·s"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "Utilisation totale du lecteur"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "Nombre total de fichiers sur le lecteur"
 | 
				
			||||||
 | 
					    network-requests: "Requêtes"
 | 
				
			||||||
 | 
					    network-time: "Temps de réponse"
 | 
				
			||||||
 | 
					    network-usage: "Traffic"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "Suspendre un·e utilisateur·rice"
 | 
					  suspend-user: "Suspendre un·e utilisateur·rice"
 | 
				
			||||||
  suspend: "Suspendre"
 | 
					  suspend: "Suspendre"
 | 
				
			||||||
  suspended: "Suspendu avec succès"
 | 
					  suspended: "Suspendu·e avec succès."
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "Lever la suspension d’utilisateur·rice·s"
 | 
					  unsuspend-user: "Lever la suspension d’utilisateur·rice·s"
 | 
				
			||||||
  unsuspend: "Suspension levée"
 | 
					  unsuspend: "Suspension levée"
 | 
				
			||||||
  unsuspended: "La suspension de l’utilisateur·rice a été levée avec succès"
 | 
					  unsuspended: "La suspension de l’utilisateur·rice a été levée avec succès"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "Paramètres de vérification du compte utilisateur"
 | 
					  verify-user: "Paramètres de vérification du compte utilisateur"
 | 
				
			||||||
  verify: "Vérification du compte"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "Le compte a été vérifié"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "Ôter la vérification du compte"
 | 
					  unverify: "Ôter la vérification du compte"
 | 
				
			||||||
  unverified: "Ce compte n'est pas vérifié"
 | 
					  unverified: "Ce compte n'est plus vérifié"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "Ajouter un émoji"
 | 
				
			||||||
 | 
					    name: "Nom de l’émoji"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "Aliases"
 | 
				
			||||||
 | 
					    aliases-desc: "Vous pouvez définir plus d’un, séparés par des espaces."
 | 
				
			||||||
 | 
					    url: "URL de l’image"
 | 
				
			||||||
 | 
					    add: "Ajouter"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "Annonces"
 | 
					  announcements: "Annonces"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "Enregistrer"
 | 
				
			||||||
 | 
					  remove: "Supprimer"
 | 
				
			||||||
 | 
					  add: "Ajouter"
 | 
				
			||||||
 | 
					  title: "Titre"
 | 
				
			||||||
 | 
					  text: "Contenu"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Tags cachés"
 | 
					  hided-tags: "Tags cachés"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "Les publications médias uniquement"
 | 
					  is-media-only: "Les publications médias uniquement"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "Sons"
 | 
					  sound: "Sons"
 | 
				
			||||||
  enable-sounds: "Activer les sons"
 | 
					  enable-sounds: "Activer les sons"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "Marquer toutes les publications comme lues"
 | 
					  mark-as-read-all-unread-notes: "Marquer toutes les publications comme lues"
 | 
				
			||||||
 | 
					  password: "Mot de Passe"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "Vous suit"
 | 
					  follows-you: "Vous suit"
 | 
				
			||||||
  following: "Abonnements"
 | 
					  following: "Abonnements"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "左に重ねる"
 | 
					    stack-left: "左に重ねる"
 | 
				
			||||||
    pop-right: "右に出す"
 | 
					    pop-right: "右に出す"
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
					  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
				
			||||||
  permission-ask: "このアプリは次の権限を要求しています:"
 | 
					  permission-ask: "このアプリは次の権限を要求しています:"
 | 
				
			||||||
@@ -874,7 +875,7 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "パスワードを変更する"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "現在のパスワードを入力してください"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "新しいパスワードを入力してください"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "閉じる"
 | 
					  close: "閉じる"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿のみ"
 | 
					  is-media-only: "メディア投稿のみ"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "フォローされています"
 | 
					  follows-you: "フォローされています"
 | 
				
			||||||
  following: "フォロー"
 | 
					  following: "フォロー"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -201,6 +201,7 @@ common:
 | 
				
			|||||||
    pop-right: "右に出す"
 | 
					    pop-right: "右に出す"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
					  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
				
			||||||
@@ -981,7 +982,7 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "パスワードを変更する"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "現在のパスワードを入力してください"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "新しいパスワードを入力してください"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
@@ -1069,48 +1070,92 @@ desktop/views/components/window.vue:
 | 
				
			|||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "閉じる"
 | 
					  close: "閉じる"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
 | 
					 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
 | 
					 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
 | 
					 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
@@ -1529,6 +1574,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "フォローされています"
 | 
					  follows-you: "フォローされています"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "左に重ねんで!"
 | 
					    stack-left: "左に重ねんで!"
 | 
				
			||||||
    pop-right: "右に出すで!"
 | 
					    pop-right: "右に出すで!"
 | 
				
			||||||
  dev: "アプリの作成あかんかったわ。もっぺんやってみて。"
 | 
					  dev: "アプリの作成あかんかったわ。もっぺんやってみて。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "<i>{{ app.name }}</i>があんさんのアカウントにアクセスすんのを<b>許可</b>してもええか?"
 | 
					  share-access: "<i>{{ app.name }}</i>があんさんのアカウントにアクセスすんのを<b>許可</b>してもええか?"
 | 
				
			||||||
  permission-ask: "このアプリは次の権限を要求してんで:"
 | 
					  permission-ask: "このアプリは次の権限を要求してんで:"
 | 
				
			||||||
@@ -874,13 +875,13 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "パスワードを変更する"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "今のパスワードを入れてや"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "さらのパスワード入れてや"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
  enter-new-password-again: "もういっぺんさらのパスワードを入れてや"
 | 
					  enter-new-password-again: "もう一度新しいパスワードを入力してください"
 | 
				
			||||||
  not-match: "パスワードがおうとらん"
 | 
					  not-match: "新しいパスワードが一致しません"
 | 
				
			||||||
  changed: "パスワード変えたわ"
 | 
					  changed: "パスワードを変更しました"
 | 
				
			||||||
desktop/views/components/sub-note-content.vue:
 | 
					desktop/views/components/sub-note-content.vue:
 | 
				
			||||||
  private: "この投稿は見せられへんわ"
 | 
					  private: "この投稿は見せられへんわ"
 | 
				
			||||||
  deleted: "この投稿なんか無くなってもうたわ"
 | 
					  deleted: "この投稿なんか無くなってもうたわ"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "さいなら"
 | 
					  close: "さいなら"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "知っといてや"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "知り合い全員や"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "ここの人らだけ"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
  invite: "来てや"
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結したで"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除したで"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしたで"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウントにせーへん"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverify: "公式アカウントにはさせへんで"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
  unverified: "公式アカウントを解除したで"
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					  add-emoji:
 | 
				
			||||||
  announcements: "知っといてや"
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿だけや"
 | 
					  is-media-only: "メディア投稿だけや"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンド鳴らす"
 | 
					  enable-sounds: "サウンド鳴らす"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "全部もう読んだわ"
 | 
					  mark-as-read-all-unread-notes: "全部もう読んだわ"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "フォローされとるで"
 | 
					  follows-you: "フォローされとるで"
 | 
				
			||||||
  following: "フォロー"
 | 
					  following: "フォロー"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "左に重ねる"
 | 
					    stack-left: "左に重ねる"
 | 
				
			||||||
    pop-right: "右に出す"
 | 
					    pop-right: "右に出す"
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
					  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
				
			||||||
  permission-ask: "このアプリは次の権限を要求しています:"
 | 
					  permission-ask: "このアプリは次の権限を要求しています:"
 | 
				
			||||||
@@ -874,7 +875,7 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "パスワードを変更する"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "現在のパスワードを入力してください"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "新しいパスワードを入力してください"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "閉じる"
 | 
					  close: "閉じる"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿のみ"
 | 
					  is-media-only: "メディア投稿のみ"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "フォローされています"
 | 
					  follows-you: "フォローされています"
 | 
				
			||||||
  following: "フォロー"
 | 
					  following: "フォロー"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "左に重ねる"
 | 
					    stack-left: "左に重ねる"
 | 
				
			||||||
    pop-right: "右に出す"
 | 
					    pop-right: "右に出す"
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
					  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
				
			||||||
  permission-ask: "このアプリは次の権限を要求しています:"
 | 
					  permission-ask: "このアプリは次の権限を要求しています:"
 | 
				
			||||||
@@ -874,13 +875,13 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "Wachtwoord wijzigen"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "Voer je huidige wachtwoord in"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "Voer je nieuwe wachtwoord in"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
  enter-new-password-again: "Voer je nieuwe wachtwoord nogmaals in"
 | 
					  enter-new-password-again: "もう一度新しいパスワードを入力してください"
 | 
				
			||||||
  not-match: "Het nieuwe wachtwoord komt niet overeen"
 | 
					  not-match: "新しいパスワードが一致しません"
 | 
				
			||||||
  changed: "Wachtwoord bijgewerkt"
 | 
					  changed: "パスワードを変更しました"
 | 
				
			||||||
desktop/views/components/sub-note-content.vue:
 | 
					desktop/views/components/sub-note-content.vue:
 | 
				
			||||||
  private: "この投稿は非公開です"
 | 
					  private: "この投稿は非公開です"
 | 
				
			||||||
  deleted: "この投稿は削除されました"
 | 
					  deleted: "この投稿は削除されました"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "Uitvouwen"
 | 
					  popout: "Uitvouwen"
 | 
				
			||||||
  close: "Sluiten"
 | 
					  close: "Sluiten"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿のみ"
 | 
					  is-media-only: "メディア投稿のみ"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "Volgt jou"
 | 
					  follows-you: "Volgt jou"
 | 
				
			||||||
  following: "Volgend"
 | 
					  following: "Volgend"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "左に重ねる"
 | 
					    stack-left: "左に重ねる"
 | 
				
			||||||
    pop-right: "Til høyre"
 | 
					    pop-right: "Til høyre"
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
					  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
				
			||||||
  permission-ask: "このアプリは次の権限を要求しています:"
 | 
					  permission-ask: "このアプリは次の権限を要求しています:"
 | 
				
			||||||
@@ -874,7 +875,7 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "パスワードを変更する"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "現在のパスワードを入力してください"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "新しいパスワードを入力してください"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "Lukk"
 | 
					  close: "Lukk"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  users: "Brukere"
 | 
					  instance: "インスタンス"
 | 
				
			||||||
  update: "Oppdater"
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
  invite: "Inviter"
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "Suspender"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿のみ"
 | 
					  is-media-only: "メディア投稿のみ"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "Lyder"
 | 
					  sound: "Lyder"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "フォローされています"
 | 
					  follows-you: "フォローされています"
 | 
				
			||||||
  following: "Følger"
 | 
					  following: "Følger"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "Przypnij do lewej"
 | 
					    stack-left: "Przypnij do lewej"
 | 
				
			||||||
    pop-right: "Odepnij w prawo"
 | 
					    pop-right: "Odepnij w prawo"
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "Czy chcesz <b>zezwolić</b> <i>{{ app.name }}</i> na dostęp do Twojego konta?"
 | 
					  share-access: "Czy chcesz <b>zezwolić</b> <i>{{ app.name }}</i> na dostęp do Twojego konta?"
 | 
				
			||||||
  permission-ask: "Ta aplikacja wymaga następujących uprawnień:"
 | 
					  permission-ask: "Ta aplikacja wymaga następujących uprawnień:"
 | 
				
			||||||
@@ -874,13 +875,13 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "Zmień hasło"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "Wprowadź obecne hasło"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "Wprowadź nowe hasło"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
  enter-new-password-again: "Wprowadź ponownie nowe hasło"
 | 
					  enter-new-password-again: "もう一度新しいパスワードを入力してください"
 | 
				
			||||||
  not-match: "Nowe hasła nie pasują do siebie"
 | 
					  not-match: "新しいパスワードが一致しません"
 | 
				
			||||||
  changed: "Pomyślnie zmieniono hasło"
 | 
					  changed: "パスワードを変更しました"
 | 
				
			||||||
desktop/views/components/sub-note-content.vue:
 | 
					desktop/views/components/sub-note-content.vue:
 | 
				
			||||||
  private: "ten wpis jest prywatny"
 | 
					  private: "ten wpis jest prywatny"
 | 
				
			||||||
  deleted: "ten wpis został usunięty"
 | 
					  deleted: "ten wpis został usunięty"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "Pop-out"
 | 
					  popout: "Pop-out"
 | 
				
			||||||
  close: "Zamknij"
 | 
					  close: "Zamknij"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "Tylko wpisy z zawartością multimedialną"
 | 
					  is-media-only: "Tylko wpisy z zawartością multimedialną"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "Śledzi Cię"
 | 
					  follows-you: "Śledzi Cię"
 | 
				
			||||||
  following: "Śledzeni"
 | 
					  following: "Śledzeni"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "左に重ねる"
 | 
					    stack-left: "左に重ねる"
 | 
				
			||||||
    pop-right: "Acoplar à direita"
 | 
					    pop-right: "Acoplar à direita"
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "Você <b>permite</b> que <i>{{ app.name }}</i> acesse sua conta?"
 | 
					  share-access: "Você <b>permite</b> que <i>{{ app.name }}</i> acesse sua conta?"
 | 
				
			||||||
  permission-ask: "Este aplicativo precisa das seguintes permissões:"
 | 
					  permission-ask: "Este aplicativo precisa das seguintes permissões:"
 | 
				
			||||||
@@ -874,7 +875,7 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "パスワードを変更する"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "現在のパスワードを入力してください"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "新しいパスワードを入力してください"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "閉じる"
 | 
					  close: "閉じる"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  users: "Usuários"
 | 
					  instance: "インスタンス"
 | 
				
			||||||
  update: "Actualizações"
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "Todos os usuários"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿のみ"
 | 
					  is-media-only: "メディア投稿のみ"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "Sons"
 | 
					  sound: "Sons"
 | 
				
			||||||
  enable-sounds: "Ativar sons"
 | 
					  enable-sounds: "Ativar sons"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "Te segue"
 | 
					  follows-you: "Te segue"
 | 
				
			||||||
  following: "Seguindo"
 | 
					  following: "Seguindo"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "左に重ねる"
 | 
					    stack-left: "左に重ねる"
 | 
				
			||||||
    pop-right: "右に出す"
 | 
					    pop-right: "右に出す"
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
					  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
				
			||||||
  permission-ask: "このアプリは次の権限を要求しています:"
 | 
					  permission-ask: "このアプリは次の権限を要求しています:"
 | 
				
			||||||
@@ -874,7 +875,7 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "パスワードを変更する"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "現在のパスワードを入力してください"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "新しいパスワードを入力してください"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "閉じる"
 | 
					  close: "閉じる"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿のみ"
 | 
					  is-media-only: "メディア投稿のみ"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "フォローされています"
 | 
					  follows-you: "フォローされています"
 | 
				
			||||||
  following: "フォロー"
 | 
					  following: "フォロー"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -186,6 +186,7 @@ common:
 | 
				
			|||||||
    stack-left: "左に重ねる"
 | 
					    stack-left: "左に重ねる"
 | 
				
			||||||
    pop-right: "右に出す"
 | 
					    pop-right: "右に出す"
 | 
				
			||||||
  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
					  dev: "アプリの作成に失敗しました。再度お試しください。"
 | 
				
			||||||
 | 
					  ai-chan-kawaii: "藍ちゃかわいい"
 | 
				
			||||||
auth/views/form.vue:
 | 
					auth/views/form.vue:
 | 
				
			||||||
  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
					  share-access: "<i>{{ app.name }}</i>があなたのアカウントにアクセスすることを<b>許可</b>しますか?"
 | 
				
			||||||
  permission-ask: "このアプリは次の権限を要求しています:"
 | 
					  permission-ask: "このアプリは次の権限を要求しています:"
 | 
				
			||||||
@@ -874,7 +875,7 @@ common/views/components/mute-and-block.vue:
 | 
				
			|||||||
  block: "ブロック"
 | 
					  block: "ブロック"
 | 
				
			||||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
					  no-muted-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
					  no-blocked-users: "ブロックしているユーザーはいません"
 | 
				
			||||||
desktop/views/components/settings.password.vue:
 | 
					common/views/components/password-settings.vue:
 | 
				
			||||||
  reset: "パスワードを変更する"
 | 
					  reset: "パスワードを変更する"
 | 
				
			||||||
  enter-current-password: "現在のパスワードを入力してください"
 | 
					  enter-current-password: "現在のパスワードを入力してください"
 | 
				
			||||||
  enter-new-password: "新しいパスワードを入力してください"
 | 
					  enter-new-password: "新しいパスワードを入力してください"
 | 
				
			||||||
@@ -946,41 +947,86 @@ desktop/views/components/users-list-item.vue:
 | 
				
			|||||||
desktop/views/components/window.vue:
 | 
					desktop/views/components/window.vue:
 | 
				
			||||||
  popout: "ポップアウト"
 | 
					  popout: "ポップアウト"
 | 
				
			||||||
  close: "閉じる"
 | 
					  close: "閉じる"
 | 
				
			||||||
desktop/views/pages/admin/admin.vue:
 | 
					admin/views/index.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
 | 
					  instance: "インスタンス"
 | 
				
			||||||
 | 
					  emoji: "カスタム絵文字"
 | 
				
			||||||
  users: "ユーザー"
 | 
					  users: "ユーザー"
 | 
				
			||||||
  update: "更新"
 | 
					  update: "更新"
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
  hashtags: "ハッシュタグ"
 | 
					  hashtags: "ハッシュタグ"
 | 
				
			||||||
desktop/views/pages/admin/admin.dashboard.vue:
 | 
					  back-to-misskey: "Misskeyに戻る"
 | 
				
			||||||
 | 
					admin/views/dashboard.vue:
 | 
				
			||||||
  dashboard: "ダッシュボード"
 | 
					  dashboard: "ダッシュボード"
 | 
				
			||||||
  all-users: "全てのユーザー"
 | 
					  accounts: "アカウント"
 | 
				
			||||||
  original-users: "このインスタンスのユーザー"
 | 
					  notes: "投稿"
 | 
				
			||||||
  all-notes: "全ての投稿"
 | 
					  drive: "ドライブ"
 | 
				
			||||||
  original-notes: "このインスタンスの投稿"
 | 
					  instances: "インスタンス"
 | 
				
			||||||
 | 
					  this-instance: "このインスタンス"
 | 
				
			||||||
 | 
					  federated: "連合"
 | 
				
			||||||
  invite: "招待"
 | 
					  invite: "招待"
 | 
				
			||||||
  banner-url: "Banner URL"
 | 
					  banner-url: "Banner URL"
 | 
				
			||||||
  disableRegistration: "Disable new user registration"
 | 
					  disableRegistration: "Disable new user registration"
 | 
				
			||||||
  disableLocalTimeline: "Disable the local timeline"
 | 
					  disableLocalTimeline: "Disable the local timeline"
 | 
				
			||||||
desktop/views/pages/admin/admin.suspend-user.vue:
 | 
					admin/views/charts.vue:
 | 
				
			||||||
 | 
					  title: "チャート"
 | 
				
			||||||
 | 
					  per-day: "1日ごと"
 | 
				
			||||||
 | 
					  per-hour: "1時間ごと"
 | 
				
			||||||
 | 
					  federation: "フェデレーション"
 | 
				
			||||||
 | 
					  notes: "投稿"
 | 
				
			||||||
 | 
					  users: "ユーザー"
 | 
				
			||||||
 | 
					  drive: "ドライブ"
 | 
				
			||||||
 | 
					  network: "ネットワーク"
 | 
				
			||||||
 | 
					  charts:
 | 
				
			||||||
 | 
					    federation-instances: "インスタンスの増減"
 | 
				
			||||||
 | 
					    federation-instances-total: "インスタンスの積算"
 | 
				
			||||||
 | 
					    notes: "投稿の増減 (統合)"
 | 
				
			||||||
 | 
					    local-notes: "投稿の増減 (ローカル)"
 | 
				
			||||||
 | 
					    remote-notes: "投稿の増減 (リモート)"
 | 
				
			||||||
 | 
					    notes-total: "投稿の積算"
 | 
				
			||||||
 | 
					    users: "ユーザーの増減"
 | 
				
			||||||
 | 
					    users-total: "ユーザーの積算"
 | 
				
			||||||
 | 
					    drive: "ドライブ使用量の増減"
 | 
				
			||||||
 | 
					    drive-total: "ドライブ使用量の積算"
 | 
				
			||||||
 | 
					    drive-files: "ドライブのファイル数の増減"
 | 
				
			||||||
 | 
					    drive-files-total: "ドライブのファイル数の積算"
 | 
				
			||||||
 | 
					    network-requests: "リクエスト"
 | 
				
			||||||
 | 
					    network-time: "応答時間"
 | 
				
			||||||
 | 
					    network-usage: "通信量"
 | 
				
			||||||
 | 
					admin/views/users.vue:
 | 
				
			||||||
  suspend-user: "ユーザーの凍結"
 | 
					  suspend-user: "ユーザーの凍結"
 | 
				
			||||||
  suspend: "凍結"
 | 
					  suspend: "凍結"
 | 
				
			||||||
  suspended: "凍結しました"
 | 
					  suspended: "凍結しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unsuspend-user.vue:
 | 
					 | 
				
			||||||
  unsuspend-user: "ユーザーの凍結の解除"
 | 
					  unsuspend-user: "ユーザーの凍結の解除"
 | 
				
			||||||
  unsuspend: "凍結の解除"
 | 
					  unsuspend: "凍結の解除"
 | 
				
			||||||
  unsuspended: "凍結を解除しました"
 | 
					  unsuspended: "凍結を解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.verify-user.vue:
 | 
					 | 
				
			||||||
  verify-user: "ユーザーの公式アカウント設定"
 | 
					  verify-user: "ユーザーの公式アカウント設定"
 | 
				
			||||||
  verify: "公式アカウントにする"
 | 
					  verify: "公式アカウントにする"
 | 
				
			||||||
  verified: "公式アカウントにしました"
 | 
					  verified: "公式アカウントにしました"
 | 
				
			||||||
desktop/views/pages/admin/admin.unverify-user.vue:
 | 
					 | 
				
			||||||
  unverify-user: "ユーザーの公式アカウント解除"
 | 
					  unverify-user: "ユーザーの公式アカウント解除"
 | 
				
			||||||
  unverify: "公式アカウントを解除する"
 | 
					  unverify: "公式アカウントを解除する"
 | 
				
			||||||
  unverified: "公式アカウントを解除しました"
 | 
					  unverified: "公式アカウントを解除しました"
 | 
				
			||||||
desktop/views/pages/admin/admin.announcements.vue:
 | 
					admin/views/emoji.vue:
 | 
				
			||||||
 | 
					  add-emoji:
 | 
				
			||||||
 | 
					    title: "絵文字の登録"
 | 
				
			||||||
 | 
					    name: "絵文字名"
 | 
				
			||||||
 | 
					    name-desc: "a~z 0~9 _ の文字が使えます。"
 | 
				
			||||||
 | 
					    aliases: "エイリアス"
 | 
				
			||||||
 | 
					    aliases-desc: "スペースで区切って複数設定できます。"
 | 
				
			||||||
 | 
					    url: "絵文字画像URL"
 | 
				
			||||||
 | 
					    add: "追加"
 | 
				
			||||||
 | 
					  emojis:
 | 
				
			||||||
 | 
					    title: "絵文字一覧"
 | 
				
			||||||
 | 
					    update: "更新"
 | 
				
			||||||
 | 
					    remove: "削除"
 | 
				
			||||||
 | 
					admin/views/announcements.vue:
 | 
				
			||||||
  announcements: "お知らせ"
 | 
					  announcements: "お知らせ"
 | 
				
			||||||
desktop/views/pages/admin/admin.hashtags.vue:
 | 
					  save: "保存"
 | 
				
			||||||
 | 
					  remove: "削除"
 | 
				
			||||||
 | 
					  add: "追加"
 | 
				
			||||||
 | 
					  title: "タイトル"
 | 
				
			||||||
 | 
					  text: "内容"
 | 
				
			||||||
 | 
					admin/views/hashtags.vue:
 | 
				
			||||||
  hided-tags: "Hidden Tags"
 | 
					  hided-tags: "Hidden Tags"
 | 
				
			||||||
desktop/views/pages/deck/deck.tl-column.vue:
 | 
					desktop/views/pages/deck/deck.tl-column.vue:
 | 
				
			||||||
  is-media-only: "メディア投稿のみ"
 | 
					  is-media-only: "メディア投稿のみ"
 | 
				
			||||||
@@ -1328,6 +1374,7 @@ mobile/views/pages/settings.vue:
 | 
				
			|||||||
  sound: "サウンド"
 | 
					  sound: "サウンド"
 | 
				
			||||||
  enable-sounds: "サウンドを有効にする"
 | 
					  enable-sounds: "サウンドを有効にする"
 | 
				
			||||||
  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
					  mark-as-read-all-unread-notes: "すべての投稿を既読にする"
 | 
				
			||||||
 | 
					  password: "パスワード"
 | 
				
			||||||
mobile/views/pages/user.vue:
 | 
					mobile/views/pages/user.vue:
 | 
				
			||||||
  follows-you: "フォローされています"
 | 
					  follows-you: "フォローされています"
 | 
				
			||||||
  following: "フォロー"
 | 
					  following: "フォロー"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										77
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										77
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"name": "misskey",
 | 
						"name": "misskey",
 | 
				
			||||||
	"version": "10.36.0",
 | 
						"version": "10.37.0",
 | 
				
			||||||
	"lockfileVersion": 1,
 | 
						"lockfileVersion": 1,
 | 
				
			||||||
	"requires": true,
 | 
						"requires": true,
 | 
				
			||||||
	"dependencies": {
 | 
						"dependencies": {
 | 
				
			||||||
@@ -563,9 +563,9 @@
 | 
				
			|||||||
			"integrity": "sha512-OftRLCgAzJP7vmKn9by/GVjnf4hloz/pXNOwPo0vKGAfXI7GqWXJi9N2kRar4cP5s1dGwuwcagWqO6iHBTq1Mg=="
 | 
								"integrity": "sha512-OftRLCgAzJP7vmKn9by/GVjnf4hloz/pXNOwPo0vKGAfXI7GqWXJi9N2kRar4cP5s1dGwuwcagWqO6iHBTq1Mg=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"@types/node": {
 | 
							"@types/node": {
 | 
				
			||||||
			"version": "10.12.0",
 | 
								"version": "10.12.2",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.2.tgz",
 | 
				
			||||||
			"integrity": "sha512-3TUHC3jsBAB7qVRGxT6lWyYo2v96BMmD2PTcl47H25Lu7UXtFH/2qqmKiVrnel6Ne//0TFYf6uvNX+HW2FRkLQ=="
 | 
								"integrity": "sha512-53ElVDSnZeFUUFIYzI8WLQ25IhWzb6vbddNp8UHlXQyU0ET2RhV5zg0NfubzU7iNMh5bBXb0htCzfvrSVNgzaQ=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"@types/orchestrator": {
 | 
							"@types/orchestrator": {
 | 
				
			||||||
			"version": "0.3.2",
 | 
								"version": "0.3.2",
 | 
				
			||||||
@@ -626,9 +626,9 @@
 | 
				
			|||||||
			"integrity": "sha1-a9p9uGU/piZD9e5p6facEaOS46Y="
 | 
								"integrity": "sha1-a9p9uGU/piZD9e5p6facEaOS46Y="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"@types/request": {
 | 
							"@types/request": {
 | 
				
			||||||
			"version": "2.47.1",
 | 
								"version": "2.48.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.1.tgz",
 | 
								"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.0.tgz",
 | 
				
			||||||
			"integrity": "sha512-TV3XLvDjQbIeVxJ1Z3oCTDk/KuYwwcNKVwz2YaT0F5u86Prgc4syDAp6P96rkTQQ4bIdh+VswQIC9zS6NjY7/g==",
 | 
								"integrity": "sha512-KnfoOtqXKllSqfXSEvGTd8KDkNlpHs+PWr6I6XiEIWk/jckH3pNmWDXNFZyPkB9wApb8vzDq2wMByM/0GFSmXg==",
 | 
				
			||||||
			"requires": {
 | 
								"requires": {
 | 
				
			||||||
				"@types/caseless": "*",
 | 
									"@types/caseless": "*",
 | 
				
			||||||
				"@types/form-data": "*",
 | 
									"@types/form-data": "*",
 | 
				
			||||||
@@ -736,9 +736,9 @@
 | 
				
			|||||||
			"integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0="
 | 
								"integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"@types/tough-cookie": {
 | 
							"@types/tough-cookie": {
 | 
				
			||||||
			"version": "2.3.3",
 | 
								"version": "2.3.4",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.3.tgz",
 | 
								"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.4.tgz",
 | 
				
			||||||
			"integrity": "sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ=="
 | 
								"integrity": "sha512-Set5ZdrAaKI/qHdFlVMgm/GsAv/wkXhSTuZFkJ+JI7HK+wIkIlOaUXSXieIvJ0+OvGIqtREFoE+NHJtEq0gtEw=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"@types/uglify-js": {
 | 
							"@types/uglify-js": {
 | 
				
			||||||
			"version": "3.0.4",
 | 
								"version": "3.0.4",
 | 
				
			||||||
@@ -2250,9 +2250,9 @@
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"cafy": {
 | 
							"cafy": {
 | 
				
			||||||
			"version": "11.3.0",
 | 
								"version": "12.0.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/cafy/-/cafy-11.3.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/cafy/-/cafy-12.0.0.tgz",
 | 
				
			||||||
			"integrity": "sha512-7kqqF4I6seSNSAWihRfnM78wP/OwaZMrCNIUzu0+TC1pDGfF2uoVfMsAJ1oV1jZsZ2L2qlUSvo9zhSEIouS/xQ=="
 | 
								"integrity": "sha512-HGsunRfyqFyG1/oh+Szw8GtVpj4pwehyqmp8sTO1QwDF3htjDP+vVBWzg7iOU2Y3Cm+h+UiEpf6DJ0p57RNmAg=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"caller-path": {
 | 
							"caller-path": {
 | 
				
			||||||
			"version": "0.1.0",
 | 
								"version": "0.1.0",
 | 
				
			||||||
@@ -3097,15 +3097,15 @@
 | 
				
			|||||||
			"integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA="
 | 
								"integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"css-loader": {
 | 
							"css-loader": {
 | 
				
			||||||
			"version": "1.0.0",
 | 
								"version": "1.0.1",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.1.tgz",
 | 
				
			||||||
			"integrity": "sha512-tMXlTYf3mIMt3b0dDCOQFJiVvxbocJ5Ho577WiGPYPZcqVEO218L2iU22pDXzkTZCLDE+9AmGSUkWxeh/nZReA==",
 | 
								"integrity": "sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw==",
 | 
				
			||||||
			"requires": {
 | 
								"requires": {
 | 
				
			||||||
				"babel-code-frame": "^6.26.0",
 | 
									"babel-code-frame": "^6.26.0",
 | 
				
			||||||
				"css-selector-tokenizer": "^0.7.0",
 | 
									"css-selector-tokenizer": "^0.7.0",
 | 
				
			||||||
				"icss-utils": "^2.1.0",
 | 
									"icss-utils": "^2.1.0",
 | 
				
			||||||
				"loader-utils": "^1.0.2",
 | 
									"loader-utils": "^1.0.2",
 | 
				
			||||||
				"lodash.camelcase": "^4.3.0",
 | 
									"lodash": "^4.17.11",
 | 
				
			||||||
				"postcss": "^6.0.23",
 | 
									"postcss": "^6.0.23",
 | 
				
			||||||
				"postcss-modules-extract-imports": "^1.2.0",
 | 
									"postcss-modules-extract-imports": "^1.2.0",
 | 
				
			||||||
				"postcss-modules-local-by-default": "^1.2.0",
 | 
									"postcss-modules-local-by-default": "^1.2.0",
 | 
				
			||||||
@@ -3137,9 +3137,9 @@
 | 
				
			|||||||
			"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
 | 
								"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"css-selector-tokenizer": {
 | 
							"css-selector-tokenizer": {
 | 
				
			||||||
			"version": "0.7.0",
 | 
								"version": "0.7.1",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz",
 | 
				
			||||||
			"integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=",
 | 
								"integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==",
 | 
				
			||||||
			"requires": {
 | 
								"requires": {
 | 
				
			||||||
				"cssesc": "^0.1.0",
 | 
									"cssesc": "^0.1.0",
 | 
				
			||||||
				"fastparse": "^1.1.1",
 | 
									"fastparse": "^1.1.1",
 | 
				
			||||||
@@ -4504,9 +4504,9 @@
 | 
				
			|||||||
			"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
 | 
								"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"elasticsearch": {
 | 
							"elasticsearch": {
 | 
				
			||||||
			"version": "15.1.1",
 | 
								"version": "15.2.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/elasticsearch/-/elasticsearch-15.1.1.tgz",
 | 
								"resolved": "https://registry.npmjs.org/elasticsearch/-/elasticsearch-15.2.0.tgz",
 | 
				
			||||||
			"integrity": "sha512-Yr9xy10rUMjDty7qCys7X9AIW5+PX4Gtv2NksZqXIc+AZiWna/y2QwZdiSLtb5LTOKDp7PbegfuokhIjMHUpKw==",
 | 
								"integrity": "sha512-jOFcBoEh3Sn3gjUTozInODZTLriJtfppAUC7jnQCUE+OUj8o7GoAyC+L4h/L3ZxmXNFbQCunqVR+nmSofHdo9A==",
 | 
				
			||||||
			"requires": {
 | 
								"requires": {
 | 
				
			||||||
				"agentkeepalive": "^3.4.1",
 | 
									"agentkeepalive": "^3.4.1",
 | 
				
			||||||
				"chalk": "^1.0.0",
 | 
									"chalk": "^1.0.0",
 | 
				
			||||||
@@ -5275,9 +5275,9 @@
 | 
				
			|||||||
			"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
 | 
								"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"fastparse": {
 | 
							"fastparse": {
 | 
				
			||||||
			"version": "1.1.1",
 | 
								"version": "1.1.2",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz",
 | 
								"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
 | 
				
			||||||
			"integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg="
 | 
								"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"fd-slicer": {
 | 
							"fd-slicer": {
 | 
				
			||||||
			"version": "1.1.0",
 | 
								"version": "1.1.0",
 | 
				
			||||||
@@ -5314,9 +5314,9 @@
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"file-type": {
 | 
							"file-type": {
 | 
				
			||||||
			"version": "10.2.0",
 | 
								"version": "10.3.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/file-type/-/file-type-10.2.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/file-type/-/file-type-10.3.0.tgz",
 | 
				
			||||||
			"integrity": "sha512-eqX81S1PWdLDPW39yyB214TVVOsUQjSmPcyUjeVH6ksH+94Y2YA/ItiIwa53rJiSofJZLK6lGsuCE3rwt0vp4w=="
 | 
								"integrity": "sha512-wk3yZ4wav7qrpJJuDfW3zSYCoxA/ZWZ8YtvrFYcbAE8jSXGFEej7jWVqFKWeeNqFIlG3B3o+fzoSKC6HJvdWUg=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"filename-regex": {
 | 
							"filename-regex": {
 | 
				
			||||||
			"version": "2.0.1",
 | 
								"version": "2.0.1",
 | 
				
			||||||
@@ -9040,11 +9040,6 @@
 | 
				
			|||||||
			"resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
 | 
								"resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
 | 
				
			||||||
			"integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU="
 | 
								"integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"lodash.camelcase": {
 | 
					 | 
				
			||||||
			"version": "4.3.0",
 | 
					 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
 | 
					 | 
				
			||||||
			"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"lodash.clone": {
 | 
							"lodash.clone": {
 | 
				
			||||||
			"version": "4.5.0",
 | 
								"version": "4.5.0",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
 | 
				
			||||||
@@ -11989,9 +11984,9 @@
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"postcss-modules-extract-imports": {
 | 
							"postcss-modules-extract-imports": {
 | 
				
			||||||
			"version": "1.2.0",
 | 
								"version": "1.2.1",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz",
 | 
								"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz",
 | 
				
			||||||
			"integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=",
 | 
								"integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==",
 | 
				
			||||||
			"requires": {
 | 
								"requires": {
 | 
				
			||||||
				"postcss": "^6.0.1"
 | 
									"postcss": "^6.0.1"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -15687,9 +15682,9 @@
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"typescript": {
 | 
							"typescript": {
 | 
				
			||||||
			"version": "3.1.4",
 | 
								"version": "3.1.5",
 | 
				
			||||||
			"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.4.tgz",
 | 
								"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.5.tgz",
 | 
				
			||||||
			"integrity": "sha512-JZHJtA6ZL15+Q3Dqkbh8iCUmvxD3iJ7ujXS+fVkKnwIVAdHc5BJTDNM0aTrnr2luKulFjU7W+SRhDZvi66Ru7Q=="
 | 
								"integrity": "sha512-muYNWV9j5+3mXoKD6oPONKuGUmYiFX14gfo9lWm9ZXRHOqVDQiB4q1CzFPbF4QLV2E9TZXH6oK55oQ94rn3PpA=="
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"typescript-eslint-parser": {
 | 
							"typescript-eslint-parser": {
 | 
				
			||||||
			"version": "20.1.1",
 | 
								"version": "20.1.1",
 | 
				
			||||||
@@ -15830,7 +15825,7 @@
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
				"fast-deep-equal": {
 | 
									"fast-deep-equal": {
 | 
				
			||||||
					"version": "1.1.0",
 | 
										"version": "1.1.0",
 | 
				
			||||||
					"resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
 | 
										"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
 | 
				
			||||||
					"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
 | 
										"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				"ignore": {
 | 
									"ignore": {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								package.json
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"name": "misskey",
 | 
						"name": "misskey",
 | 
				
			||||||
	"author": "syuilo <i@syuilo.com>",
 | 
						"author": "syuilo <i@syuilo.com>",
 | 
				
			||||||
	"version": "10.37.0",
 | 
						"version": "10.38.1",
 | 
				
			||||||
	"clientVersion": "1.0.11314",
 | 
						"clientVersion": "1.0.11482",
 | 
				
			||||||
	"codename": "nighthike",
 | 
						"codename": "nighthike",
 | 
				
			||||||
	"main": "./built/index.js",
 | 
						"main": "./built/index.js",
 | 
				
			||||||
	"private": true,
 | 
						"private": true,
 | 
				
			||||||
@@ -61,13 +61,13 @@
 | 
				
			|||||||
		"@types/mocha": "5.2.5",
 | 
							"@types/mocha": "5.2.5",
 | 
				
			||||||
		"@types/mongodb": "3.1.12",
 | 
							"@types/mongodb": "3.1.12",
 | 
				
			||||||
		"@types/ms": "0.7.30",
 | 
							"@types/ms": "0.7.30",
 | 
				
			||||||
		"@types/node": "10.12.0",
 | 
							"@types/node": "10.12.2",
 | 
				
			||||||
		"@types/portscanner": "2.1.0",
 | 
							"@types/portscanner": "2.1.0",
 | 
				
			||||||
		"@types/pug": "2.0.4",
 | 
							"@types/pug": "2.0.4",
 | 
				
			||||||
		"@types/qrcode": "1.3.0",
 | 
							"@types/qrcode": "1.3.0",
 | 
				
			||||||
		"@types/ratelimiter": "2.1.28",
 | 
							"@types/ratelimiter": "2.1.28",
 | 
				
			||||||
		"@types/redis": "2.8.7",
 | 
							"@types/redis": "2.8.7",
 | 
				
			||||||
		"@types/request": "2.47.1",
 | 
							"@types/request": "2.48.0",
 | 
				
			||||||
		"@types/request-promise-native": "1.0.15",
 | 
							"@types/request-promise-native": "1.0.15",
 | 
				
			||||||
		"@types/rimraf": "2.0.2",
 | 
							"@types/rimraf": "2.0.2",
 | 
				
			||||||
		"@types/seedrandom": "2.4.27",
 | 
							"@types/seedrandom": "2.4.27",
 | 
				
			||||||
@@ -91,28 +91,28 @@
 | 
				
			|||||||
		"bcryptjs": "2.4.3",
 | 
							"bcryptjs": "2.4.3",
 | 
				
			||||||
		"bee-queue": "1.2.2",
 | 
							"bee-queue": "1.2.2",
 | 
				
			||||||
		"bootstrap-vue": "2.0.0-rc.11",
 | 
							"bootstrap-vue": "2.0.0-rc.11",
 | 
				
			||||||
		"cafy": "11.3.0",
 | 
							"cafy": "12.0.0",
 | 
				
			||||||
		"chai": "4.2.0",
 | 
							"chai": "4.2.0",
 | 
				
			||||||
		"chai-http": "4.2.0",
 | 
							"chai-http": "4.2.0",
 | 
				
			||||||
		"chalk": "2.4.1",
 | 
							"chalk": "2.4.1",
 | 
				
			||||||
		"chart.js": "2.7.3",
 | 
							"chart.js": "2.7.3",
 | 
				
			||||||
		"commander": "2.19.0",
 | 
							"commander": "2.19.0",
 | 
				
			||||||
		"crc-32": "1.2.0",
 | 
							"crc-32": "1.2.0",
 | 
				
			||||||
		"css-loader": "1.0.0",
 | 
							"css-loader": "1.0.1",
 | 
				
			||||||
		"dateformat": "3.0.3",
 | 
							"dateformat": "3.0.3",
 | 
				
			||||||
		"debug": "4.1.0",
 | 
							"debug": "4.1.0",
 | 
				
			||||||
		"deep-equal": "1.0.1",
 | 
							"deep-equal": "1.0.1",
 | 
				
			||||||
		"deepcopy": "0.6.3",
 | 
							"deepcopy": "0.6.3",
 | 
				
			||||||
		"diskusage": "0.2.5",
 | 
							"diskusage": "0.2.5",
 | 
				
			||||||
		"double-ended-queue": "2.1.0-0",
 | 
							"double-ended-queue": "2.1.0-0",
 | 
				
			||||||
		"elasticsearch": "15.1.1",
 | 
							"elasticsearch": "15.2.0",
 | 
				
			||||||
		"emojilib": "2.3.0",
 | 
							"emojilib": "2.3.0",
 | 
				
			||||||
		"escape-regexp": "0.0.1",
 | 
							"escape-regexp": "0.0.1",
 | 
				
			||||||
		"eslint": "5.8.0",
 | 
							"eslint": "5.8.0",
 | 
				
			||||||
		"eslint-plugin-vue": "4.7.1",
 | 
							"eslint-plugin-vue": "4.7.1",
 | 
				
			||||||
		"eventemitter3": "3.1.0",
 | 
							"eventemitter3": "3.1.0",
 | 
				
			||||||
		"file-loader": "2.0.0",
 | 
							"file-loader": "2.0.0",
 | 
				
			||||||
		"file-type": "10.2.0",
 | 
							"file-type": "10.3.0",
 | 
				
			||||||
		"fuckadblock": "3.2.1",
 | 
							"fuckadblock": "3.2.1",
 | 
				
			||||||
		"gulp": "3.9.1",
 | 
							"gulp": "3.9.1",
 | 
				
			||||||
		"gulp-cssnano": "2.1.3",
 | 
							"gulp-cssnano": "2.1.3",
 | 
				
			||||||
@@ -204,7 +204,7 @@
 | 
				
			|||||||
		"ts-loader": "5.3.0",
 | 
							"ts-loader": "5.3.0",
 | 
				
			||||||
		"ts-node": "7.0.1",
 | 
							"ts-node": "7.0.1",
 | 
				
			||||||
		"tslint": "5.10.0",
 | 
							"tslint": "5.10.0",
 | 
				
			||||||
		"typescript": "3.1.4",
 | 
							"typescript": "3.1.5",
 | 
				
			||||||
		"typescript-eslint-parser": "20.1.1",
 | 
							"typescript-eslint-parser": "20.1.1",
 | 
				
			||||||
		"uglify-es": "3.3.9",
 | 
							"uglify-es": "3.3.9",
 | 
				
			||||||
		"url-loader": "1.1.2",
 | 
							"url-loader": "1.1.2",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -230,7 +230,7 @@ export default abstract class Chart<T> {
 | 
				
			|||||||
			null;
 | 
								null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// ログ取得
 | 
							// ログ取得
 | 
				
			||||||
		const logs = await this.collection.find({
 | 
							let logs = await this.collection.find({
 | 
				
			||||||
			group: group,
 | 
								group: group,
 | 
				
			||||||
			span: span,
 | 
								span: span,
 | 
				
			||||||
			date: {
 | 
								date: {
 | 
				
			||||||
@@ -245,6 +245,27 @@ export default abstract class Chart<T> {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 要求された範囲にログがひとつもなかったら
 | 
				
			||||||
 | 
							if (logs.length == 0) {
 | 
				
			||||||
 | 
								// もっとも新しいログを持ってくる
 | 
				
			||||||
 | 
								// (すくなくともひとつログが無いと隙間埋めできないため)
 | 
				
			||||||
 | 
								const recentLog = await this.collection.findOne({
 | 
				
			||||||
 | 
									group: group,
 | 
				
			||||||
 | 
									span: span
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									sort: {
 | 
				
			||||||
 | 
										date: -1
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									fields: {
 | 
				
			||||||
 | 
										_id: 0
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (recentLog) {
 | 
				
			||||||
 | 
									logs = [recentLog];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 整形
 | 
							// 整形
 | 
				
			||||||
		for (let i = (range - 1); i >= 0; i--) {
 | 
							for (let i = (range - 1); i >= 0; i--) {
 | 
				
			||||||
			const current =
 | 
								const current =
 | 
				
			||||||
@@ -269,14 +290,11 @@ export default abstract class Chart<T> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		/**
 | 
							/**
 | 
				
			||||||
		 * [{
 | 
							 * [{
 | 
				
			||||||
		 * 	xxxxx: 1,
 | 
							 * 	xxxxx: 1, yyyyy: 5
 | 
				
			||||||
		 * 	yyyyy: 5
 | 
					 | 
				
			||||||
		 * }, {
 | 
							 * }, {
 | 
				
			||||||
		 * 	xxxxx: 2,
 | 
							 * 	xxxxx: 2, yyyyy: 6
 | 
				
			||||||
		 * 	yyyyy: 6
 | 
					 | 
				
			||||||
		 * }, {
 | 
							 * }, {
 | 
				
			||||||
		 * 	xxxxx: 3,
 | 
							 * 	xxxxx: 3, yyyyy: 7
 | 
				
			||||||
		 * 	yyyyy: 7
 | 
					 | 
				
			||||||
		 * }]
 | 
							 * }]
 | 
				
			||||||
		 *
 | 
							 *
 | 
				
			||||||
		 * を
 | 
							 * を
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										150
									
								
								src/client/app/admin/assets/header-icon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/client/app/admin/assets/header-icon.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
				
			||||||
 | 
					<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<svg
 | 
				
			||||||
 | 
					   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
				
			||||||
 | 
					   xmlns:cc="http://creativecommons.org/ns#"
 | 
				
			||||||
 | 
					   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
				
			||||||
 | 
					   xmlns:svg="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					   xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
				
			||||||
 | 
					   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
				
			||||||
 | 
					   width="512"
 | 
				
			||||||
 | 
					   height="512"
 | 
				
			||||||
 | 
					   viewBox="0 0 135.46667 135.46667"
 | 
				
			||||||
 | 
					   version="1.1"
 | 
				
			||||||
 | 
					   id="svg8"
 | 
				
			||||||
 | 
					   inkscape:version="0.92.1 r15371"
 | 
				
			||||||
 | 
					   sodipodi:docname="header-icon.dark.svg"
 | 
				
			||||||
 | 
					   inkscape:export-filename="C:\Users\syuilo\projects\misskey\assets\favicon\32.png"
 | 
				
			||||||
 | 
					   inkscape:export-xdpi="6"
 | 
				
			||||||
 | 
					   inkscape:export-ydpi="6">
 | 
				
			||||||
 | 
					  <defs
 | 
				
			||||||
 | 
					     id="defs2">
 | 
				
			||||||
 | 
					    <inkscape:path-effect
 | 
				
			||||||
 | 
					       effect="simplify"
 | 
				
			||||||
 | 
					       id="path-effect5115"
 | 
				
			||||||
 | 
					       is_visible="true"
 | 
				
			||||||
 | 
					       steps="1"
 | 
				
			||||||
 | 
					       threshold="0.000408163"
 | 
				
			||||||
 | 
					       smooth_angles="360"
 | 
				
			||||||
 | 
					       helper_size="0"
 | 
				
			||||||
 | 
					       simplify_individual_paths="false"
 | 
				
			||||||
 | 
					       simplify_just_coalesce="false"
 | 
				
			||||||
 | 
					       simplifyindividualpaths="false"
 | 
				
			||||||
 | 
					       simplifyJustCoalesce="false" />
 | 
				
			||||||
 | 
					    <inkscape:path-effect
 | 
				
			||||||
 | 
					       effect="simplify"
 | 
				
			||||||
 | 
					       id="path-effect5111"
 | 
				
			||||||
 | 
					       is_visible="true"
 | 
				
			||||||
 | 
					       steps="1"
 | 
				
			||||||
 | 
					       threshold="0.000408163"
 | 
				
			||||||
 | 
					       smooth_angles="360"
 | 
				
			||||||
 | 
					       helper_size="0"
 | 
				
			||||||
 | 
					       simplify_individual_paths="false"
 | 
				
			||||||
 | 
					       simplify_just_coalesce="false"
 | 
				
			||||||
 | 
					       simplifyindividualpaths="false"
 | 
				
			||||||
 | 
					       simplifyJustCoalesce="false" />
 | 
				
			||||||
 | 
					    <inkscape:path-effect
 | 
				
			||||||
 | 
					       effect="simplify"
 | 
				
			||||||
 | 
					       id="path-effect5104"
 | 
				
			||||||
 | 
					       is_visible="true"
 | 
				
			||||||
 | 
					       steps="1"
 | 
				
			||||||
 | 
					       threshold="0.000408163"
 | 
				
			||||||
 | 
					       smooth_angles="360"
 | 
				
			||||||
 | 
					       helper_size="0"
 | 
				
			||||||
 | 
					       simplify_individual_paths="false"
 | 
				
			||||||
 | 
					       simplify_just_coalesce="false"
 | 
				
			||||||
 | 
					       simplifyindividualpaths="false"
 | 
				
			||||||
 | 
					       simplifyJustCoalesce="false" />
 | 
				
			||||||
 | 
					  </defs>
 | 
				
			||||||
 | 
					  <sodipodi:namedview
 | 
				
			||||||
 | 
					     id="base"
 | 
				
			||||||
 | 
					     pagecolor="#ffffff"
 | 
				
			||||||
 | 
					     bordercolor="#666666"
 | 
				
			||||||
 | 
					     borderopacity="1.0"
 | 
				
			||||||
 | 
					     inkscape:pageopacity="0.0"
 | 
				
			||||||
 | 
					     inkscape:pageshadow="2"
 | 
				
			||||||
 | 
					     inkscape:zoom="1.4142136"
 | 
				
			||||||
 | 
					     inkscape:cx="114.309"
 | 
				
			||||||
 | 
					     inkscape:cy="251.50613"
 | 
				
			||||||
 | 
					     inkscape:document-units="px"
 | 
				
			||||||
 | 
					     inkscape:current-layer="g4502"
 | 
				
			||||||
 | 
					     showgrid="true"
 | 
				
			||||||
 | 
					     units="px"
 | 
				
			||||||
 | 
					     inkscape:snap-bbox="true"
 | 
				
			||||||
 | 
					     inkscape:bbox-nodes="true"
 | 
				
			||||||
 | 
					     inkscape:snap-bbox-edge-midpoints="false"
 | 
				
			||||||
 | 
					     inkscape:snap-smooth-nodes="true"
 | 
				
			||||||
 | 
					     inkscape:snap-center="true"
 | 
				
			||||||
 | 
					     inkscape:snap-page="true"
 | 
				
			||||||
 | 
					     inkscape:window-width="1920"
 | 
				
			||||||
 | 
					     inkscape:window-height="1027"
 | 
				
			||||||
 | 
					     inkscape:window-x="-8"
 | 
				
			||||||
 | 
					     inkscape:window-y="1072"
 | 
				
			||||||
 | 
					     inkscape:window-maximized="1"
 | 
				
			||||||
 | 
					     inkscape:snap-object-midpoints="true"
 | 
				
			||||||
 | 
					     inkscape:snap-midpoints="true"
 | 
				
			||||||
 | 
					     inkscape:object-paths="true"
 | 
				
			||||||
 | 
					     fit-margin-top="0"
 | 
				
			||||||
 | 
					     fit-margin-left="0"
 | 
				
			||||||
 | 
					     fit-margin-right="0"
 | 
				
			||||||
 | 
					     fit-margin-bottom="0"
 | 
				
			||||||
 | 
					     objecttolerance="1"
 | 
				
			||||||
 | 
					     guidetolerance="1"
 | 
				
			||||||
 | 
					     inkscape:snap-nodes="false"
 | 
				
			||||||
 | 
					     inkscape:snap-others="false">
 | 
				
			||||||
 | 
					    <inkscape:grid
 | 
				
			||||||
 | 
					       type="xygrid"
 | 
				
			||||||
 | 
					       id="grid4504"
 | 
				
			||||||
 | 
					       spacingx="4.2333334"
 | 
				
			||||||
 | 
					       spacingy="4.2333334"
 | 
				
			||||||
 | 
					       empcolor="#ff3fff"
 | 
				
			||||||
 | 
					       empopacity="0.25098039"
 | 
				
			||||||
 | 
					       empspacing="4" />
 | 
				
			||||||
 | 
					  </sodipodi:namedview>
 | 
				
			||||||
 | 
					  <metadata
 | 
				
			||||||
 | 
					     id="metadata5">
 | 
				
			||||||
 | 
					    <rdf:RDF>
 | 
				
			||||||
 | 
					      <cc:Work
 | 
				
			||||||
 | 
					         rdf:about="">
 | 
				
			||||||
 | 
					        <dc:format>image/svg+xml</dc:format>
 | 
				
			||||||
 | 
					        <dc:type
 | 
				
			||||||
 | 
					           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
				
			||||||
 | 
					        <dc:title></dc:title>
 | 
				
			||||||
 | 
					      </cc:Work>
 | 
				
			||||||
 | 
					    </rdf:RDF>
 | 
				
			||||||
 | 
					  </metadata>
 | 
				
			||||||
 | 
					  <g
 | 
				
			||||||
 | 
					     inkscape:label="レイヤー 1"
 | 
				
			||||||
 | 
					     inkscape:groupmode="layer"
 | 
				
			||||||
 | 
					     id="layer1"
 | 
				
			||||||
 | 
					     transform="translate(-30.809093,-111.78601)">
 | 
				
			||||||
 | 
					    <g
 | 
				
			||||||
 | 
					       id="g4502"
 | 
				
			||||||
 | 
					       transform="matrix(1.096096,0,0,1.096096,-2.960633,-44.023579)">
 | 
				
			||||||
 | 
					      <g
 | 
				
			||||||
 | 
					         style="fill-opacity:1"
 | 
				
			||||||
 | 
					         transform="translate(-1.3333333e-6,-1.3439941e-6)"
 | 
				
			||||||
 | 
					         id="g5125">
 | 
				
			||||||
 | 
					        <g
 | 
				
			||||||
 | 
					           transform="matrix(0.91391326,0,0,0.91391326,7.9719907,17.595761)"
 | 
				
			||||||
 | 
					           id="text4489"
 | 
				
			||||||
 | 
					           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:141.03404236px;line-height:476.69509888px;font-family:'OTADESIGN Rounded';-inkscape-font-specification:'OTADESIGN Rounded';letter-spacing:0px;word-spacing:0px;fill-opacity:1;stroke:none;stroke-width:0.28950602px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 | 
				
			||||||
 | 
					           aria-label="Mi">
 | 
				
			||||||
 | 
					          <path
 | 
				
			||||||
 | 
					             sodipodi:nodetypes="zccssscssccscczzzccsccsscscsccz"
 | 
				
			||||||
 | 
					             inkscape:connector-curvature="0"
 | 
				
			||||||
 | 
					             id="path5210"
 | 
				
			||||||
 | 
					             style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'OTADESIGN Rounded';-inkscape-font-specification:'OTADESIGN Rounded';fill-opacity:1;stroke-width:0.28950602px"
 | 
				
			||||||
 | 
					             d="m 75.196381,231.17126 c -5.855419,0.0202 -10.885068,-3.50766 -13.2572,-7.61584 -1.266603,-1.79454 -3.772419,-2.43291 -3.807919,0 v 11.2332 c 0,4.51309 -1.645397,8.41504 -4.936191,11.70583 -3.196772,3.19677 -7.098714,4.79516 -11.705826,4.79516 -4.513089,0 -8.415031,-1.59839 -11.705825,-4.79516 -3.196772,-3.29079 -4.795158,-7.19274 -4.795158,-11.70583 v -61.7729 c 0,-3.47884 0.987238,-6.6286 2.961715,-9.44928 2.068499,-2.91471 4.701135,-4.9362 7.897906,-6.06447 1.786431,-0.65816 3.666885,-0.98724 5.641362,-0.98724 5.077225,0 9.308247,1.97448 12.693064,5.92343 1.786431,1.97448 2.820681,3.00873 3.102749,3.10275 0,0 13.408119,16.21319 13.78421,16.49526 0.376091,0.28206 1.480789,2.43848 4.127113,2.43848 2.646324,0 3.89218,-2.15642 4.26827,-2.43848 0.376091,-0.28207 13.784088,-16.49526 13.784088,-16.49526 0.09402,0.094 1.081261,-0.94022 2.961715,-3.10275 3.478837,-3.94895 7.756866,-5.92343 12.834096,-5.92343 1.88045,0 3.76091,0.32908 5.64136,0.98724 3.19677,1.12827 5.7824,3.14976 7.75688,6.06447 2.06849,2.82068 3.10274,5.97044 3.10274,9.44928 v 61.7729 c 0,4.51309 -1.6454,8.41504 -4.93619,11.70583 -3.19677,3.19677 -7.09871,4.79516 -11.70582,4.79516 -4.51309,0 -8.41504,-1.59839 -11.705828,-4.79516 -3.196772,-3.29079 -4.795158,-7.19274 -4.795158,-11.70583 v -11.2332 c -0.277898,-3.06563 -2.987588,-1.13379 -3.948953,0 -2.538613,4.70114 -7.401781,7.59567 -13.2572,7.61584 z" />
 | 
				
			||||||
 | 
					          <path
 | 
				
			||||||
 | 
					             inkscape:connector-curvature="0"
 | 
				
			||||||
 | 
					             id="path5212"
 | 
				
			||||||
 | 
					             style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'OTADESIGN Rounded';-inkscape-font-specification:'OTADESIGN Rounded';fill-opacity:1;stroke-width:0.28950602px"
 | 
				
			||||||
 | 
					             d="m 145.83461,185.00361 q -5.92343,0 -10.15445,-4.08999 -4.08999,-4.23102 -4.08999,-10.15445 0,-5.92343 4.08999,-10.01342 4.23102,-4.23102 10.15445,-4.23102 5.92343,0 10.15445,4.23102 4.23102,4.08999 4.23102,10.01342 0,5.92343 -4.23102,10.15445 -4.23102,4.08999 -10.15445,4.08999 z m 0.14103,2.82068 q 5.92343,0 10.01342,4.23102 4.23102,4.23102 4.23102,10.15445 v 34.83541 q 0,5.92343 -4.23102,10.15445 -4.08999,4.08999 -10.01342,4.08999 -5.92343,0 -10.15445,-4.08999 -4.23102,-4.23102 -4.23102,-10.15445 v -34.83541 q 0,-5.92343 4.23102,-10.15445 4.23102,-4.23102 10.15445,-4.23102 z" />
 | 
				
			||||||
 | 
					        </g>
 | 
				
			||||||
 | 
					      </g>
 | 
				
			||||||
 | 
					    </g>
 | 
				
			||||||
 | 
					  </g>
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 7.0 KiB  | 
							
								
								
									
										27
									
								
								src/client/app/admin/script.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/client/app/admin/script.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Admin
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import VueRouter from 'vue-router';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Style
 | 
				
			||||||
 | 
					import './style.styl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import init from '../init';
 | 
				
			||||||
 | 
					import Index from './views/index.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					init(launch => {
 | 
				
			||||||
 | 
						document.title = 'Admin';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Init router
 | 
				
			||||||
 | 
						const router = new VueRouter({
 | 
				
			||||||
 | 
							mode: 'history',
 | 
				
			||||||
 | 
							base: '/admin/',
 | 
				
			||||||
 | 
							routes: [
 | 
				
			||||||
 | 
								{ path: '/', component: Index },
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Launch the app
 | 
				
			||||||
 | 
						launch(router);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										6
									
								
								src/client/app/admin/style.styl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/client/app/admin/style.styl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					@import "../app"
 | 
				
			||||||
 | 
					@import "../reset"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					html
 | 
				
			||||||
 | 
						height 100%
 | 
				
			||||||
 | 
						background var(--bg)
 | 
				
			||||||
							
								
								
									
										64
									
								
								src/client/app/admin/views/announcements.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/client/app/admin/views/announcements.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<div slot="title">%fa:broadcast-tower% %i18n:@announcements%</div>
 | 
				
			||||||
 | 
							<section v-for="(announcement, i) in announcements" class="fit-top">
 | 
				
			||||||
 | 
								<ui-input v-model="announcement.title" @change="save">
 | 
				
			||||||
 | 
									<span>%i18n:@title%</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-textarea v-model="announcement.text">
 | 
				
			||||||
 | 
									<span>%i18n:@text%</span>
 | 
				
			||||||
 | 
								</ui-textarea>
 | 
				
			||||||
 | 
								<ui-button-group>
 | 
				
			||||||
 | 
									<ui-button inline @click="save">%fa:save R% %i18n:@save%</ui-button>
 | 
				
			||||||
 | 
									<ui-button inline @click="remove(i)">%fa:trash-alt R% %i18n:@remove%</ui-button>
 | 
				
			||||||
 | 
								</ui-button-group>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
							<section>
 | 
				
			||||||
 | 
								<ui-button @click="add">%fa:plus% %i18n:@add%</ui-button>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								announcements: [],
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						created() {
 | 
				
			||||||
 | 
							(this as any).os.getMeta().then(meta => {
 | 
				
			||||||
 | 
								this.announcements = meta.broadcasts;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							add() {
 | 
				
			||||||
 | 
								this.announcements.push({
 | 
				
			||||||
 | 
									title: '',
 | 
				
			||||||
 | 
									text: ''
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							remove(i) {
 | 
				
			||||||
 | 
								this.announcements = this.announcements.filter((_, j) => j !== i);
 | 
				
			||||||
 | 
								this.save();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							save() {
 | 
				
			||||||
 | 
								(this as any).api('admin/update-meta', {
 | 
				
			||||||
 | 
									broadcasts: this.announcements
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Saved` });
 | 
				
			||||||
 | 
								}).catch(e => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										104
									
								
								src/client/app/admin/views/ap-log.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/client/app/admin/views/ap-log.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div class="hyhctythnmwihguaaapnbrbszsjqxpio">
 | 
				
			||||||
 | 
						<table>
 | 
				
			||||||
 | 
							<thead>
 | 
				
			||||||
 | 
								<tr>
 | 
				
			||||||
 | 
									<th>%fa:exchange-alt% In/Out</th>
 | 
				
			||||||
 | 
									<th>%fa:server% Host</th>
 | 
				
			||||||
 | 
									<th>%fa:bolt% Activity</th>
 | 
				
			||||||
 | 
									<th>%fa:user% Actor</th>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
							</thead>
 | 
				
			||||||
 | 
							<tbody>
 | 
				
			||||||
 | 
								<tr v-for="log in logs" :key="log.id">
 | 
				
			||||||
 | 
									<td :class="log.direction">{{ log.direction == 'in' ? '<' : '>' }} {{ log.direction }}</td>
 | 
				
			||||||
 | 
									<td>{{ log.host }}</td>
 | 
				
			||||||
 | 
									<td>{{ log.activity }}</td>
 | 
				
			||||||
 | 
									<td>@{{ log.actor }}</td>
 | 
				
			||||||
 | 
								</tr>
 | 
				
			||||||
 | 
							</tbody>
 | 
				
			||||||
 | 
						</table>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								logs: [],
 | 
				
			||||||
 | 
								connection: null
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mounted() {
 | 
				
			||||||
 | 
							this.connection = (this as any).os.stream.useSharedConnection('apLog');
 | 
				
			||||||
 | 
							this.connection.on('log', this.onLog);
 | 
				
			||||||
 | 
							this.connection.on('logs', this.onLogs);
 | 
				
			||||||
 | 
							this.connection.send('requestLog', {
 | 
				
			||||||
 | 
								id: Math.random().toString().substr(2, 8),
 | 
				
			||||||
 | 
								length: 50
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						beforeDestroy() {
 | 
				
			||||||
 | 
							this.connection.dispose();
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							onLog(log) {
 | 
				
			||||||
 | 
								log.id = Math.random();
 | 
				
			||||||
 | 
								this.logs.unshift(log);
 | 
				
			||||||
 | 
								if (this.logs.length > 50) this.logs.pop();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onLogs(logs) {
 | 
				
			||||||
 | 
								logs.reverse().forEach(log => this.onLog(log));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.hyhctythnmwihguaaapnbrbszsjqxpio
 | 
				
			||||||
 | 
						display block
 | 
				
			||||||
 | 
						padding 16px
 | 
				
			||||||
 | 
						height 250px
 | 
				
			||||||
 | 
						overflow auto
 | 
				
			||||||
 | 
						box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
 | 
				
			||||||
 | 
						background var(--face)
 | 
				
			||||||
 | 
						border-radius 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> table
 | 
				
			||||||
 | 
							width 100%
 | 
				
			||||||
 | 
							max-width 100%
 | 
				
			||||||
 | 
							overflow auto
 | 
				
			||||||
 | 
							border-spacing 0
 | 
				
			||||||
 | 
							border-collapse collapse
 | 
				
			||||||
 | 
							color #555
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							thead
 | 
				
			||||||
 | 
								border-bottom solid 2px #eee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								tr
 | 
				
			||||||
 | 
									th
 | 
				
			||||||
 | 
										font-weight normal
 | 
				
			||||||
 | 
										text-align left
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tbody
 | 
				
			||||||
 | 
								tr
 | 
				
			||||||
 | 
									&:nth-child(odd)
 | 
				
			||||||
 | 
										background #fbfbfb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							th, td
 | 
				
			||||||
 | 
								padding 8px 16px
 | 
				
			||||||
 | 
								min-width 128px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							td.in
 | 
				
			||||||
 | 
								color #d26755
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							td.out
 | 
				
			||||||
 | 
								color #55bb83
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										441
									
								
								src/client/app/admin/views/charts.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								src/client/app/admin/views/charts.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,441 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div class="qvgidhudpqhjttdhxubzuyrhyzgslujw">
 | 
				
			||||||
 | 
						<header>
 | 
				
			||||||
 | 
							<b>%fa:chart-bar R% %i18n:@title%:</b>
 | 
				
			||||||
 | 
							<select v-model="src">
 | 
				
			||||||
 | 
								<optgroup label="%i18n:@federation%">
 | 
				
			||||||
 | 
									<option value="federation-instances">%i18n:@charts.federation-instances%</option>
 | 
				
			||||||
 | 
									<option value="federation-instances-total">%i18n:@charts.federation-instances-total%</option>
 | 
				
			||||||
 | 
								</optgroup>
 | 
				
			||||||
 | 
								<optgroup label="%i18n:@users%">
 | 
				
			||||||
 | 
									<option value="users">%i18n:@charts.users%</option>
 | 
				
			||||||
 | 
									<option value="users-total">%i18n:@charts.users-total%</option>
 | 
				
			||||||
 | 
								</optgroup>
 | 
				
			||||||
 | 
								<optgroup label="%i18n:@notes%">
 | 
				
			||||||
 | 
									<option value="notes">%i18n:@charts.notes%</option>
 | 
				
			||||||
 | 
									<option value="local-notes">%i18n:@charts.local-notes%</option>
 | 
				
			||||||
 | 
									<option value="remote-notes">%i18n:@charts.remote-notes%</option>
 | 
				
			||||||
 | 
									<option value="notes-total">%i18n:@charts.notes-total%</option>
 | 
				
			||||||
 | 
								</optgroup>
 | 
				
			||||||
 | 
								<optgroup label="%i18n:@drive%">
 | 
				
			||||||
 | 
									<option value="drive-files">%i18n:@charts.drive-files%</option>
 | 
				
			||||||
 | 
									<option value="drive-files-total">%i18n:@charts.drive-files-total%</option>
 | 
				
			||||||
 | 
									<option value="drive">%i18n:@charts.drive%</option>
 | 
				
			||||||
 | 
									<option value="drive-total">%i18n:@charts.drive-total%</option>
 | 
				
			||||||
 | 
								</optgroup>
 | 
				
			||||||
 | 
								<optgroup label="%i18n:@network%">
 | 
				
			||||||
 | 
									<option value="network-requests">%i18n:@charts.network-requests%</option>
 | 
				
			||||||
 | 
									<option value="network-time">%i18n:@charts.network-time%</option>
 | 
				
			||||||
 | 
									<option value="network-usage">%i18n:@charts.network-usage%</option>
 | 
				
			||||||
 | 
								</optgroup>
 | 
				
			||||||
 | 
							</select>
 | 
				
			||||||
 | 
							<div>
 | 
				
			||||||
 | 
								<span @click="span = 'day'" :class="{ active: span == 'day' }">%i18n:@per-day%</span> | <span @click="span = 'hour'" :class="{ active: span == 'hour' }">%i18n:@per-hour%</span>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</header>
 | 
				
			||||||
 | 
						<div ref="chart"></div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import * as ApexCharts from 'apexcharts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const limit = 90;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
 | 
				
			||||||
 | 
					const negate = arr => arr.map(x => -x);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								chart: null,
 | 
				
			||||||
 | 
								src: 'notes',
 | 
				
			||||||
 | 
								span: 'hour',
 | 
				
			||||||
 | 
								chartInstance: null
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						computed: {
 | 
				
			||||||
 | 
							data(): any {
 | 
				
			||||||
 | 
								if (this.chart == null) return null;
 | 
				
			||||||
 | 
								switch (this.src) {
 | 
				
			||||||
 | 
									case 'federation-instances': return this.federationInstancesChart(false);
 | 
				
			||||||
 | 
									case 'federation-instances-total': return this.federationInstancesChart(true);
 | 
				
			||||||
 | 
									case 'users': return this.usersChart(false);
 | 
				
			||||||
 | 
									case 'users-total': return this.usersChart(true);
 | 
				
			||||||
 | 
									case 'notes': return this.notesChart('combined');
 | 
				
			||||||
 | 
									case 'local-notes': return this.notesChart('local');
 | 
				
			||||||
 | 
									case 'remote-notes': return this.notesChart('remote');
 | 
				
			||||||
 | 
									case 'notes-total': return this.notesTotalChart();
 | 
				
			||||||
 | 
									case 'drive': return this.driveChart();
 | 
				
			||||||
 | 
									case 'drive-total': return this.driveTotalChart();
 | 
				
			||||||
 | 
									case 'drive-files': return this.driveFilesChart();
 | 
				
			||||||
 | 
									case 'drive-files-total': return this.driveFilesTotalChart();
 | 
				
			||||||
 | 
									case 'network-requests': return this.networkRequestsChart();
 | 
				
			||||||
 | 
									case 'network-time': return this.networkTimeChart();
 | 
				
			||||||
 | 
									case 'network-usage': return this.networkUsageChart();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							stats(): any[] {
 | 
				
			||||||
 | 
								const stats =
 | 
				
			||||||
 | 
									this.span == 'day' ? this.chart.perDay :
 | 
				
			||||||
 | 
									this.span == 'hour' ? this.chart.perHour :
 | 
				
			||||||
 | 
									null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return stats;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						watch: {
 | 
				
			||||||
 | 
							src() {
 | 
				
			||||||
 | 
								this.render();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							span() {
 | 
				
			||||||
 | 
								this.render();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						async mounted() {
 | 
				
			||||||
 | 
							this.now = new Date();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const [perHour, perDay] = await Promise.all([Promise.all([
 | 
				
			||||||
 | 
								(this as any).api('charts/federation', { limit: limit, span: 'hour' }),
 | 
				
			||||||
 | 
								(this as any).api('charts/users', { limit: limit, span: 'hour' }),
 | 
				
			||||||
 | 
								(this as any).api('charts/notes', { limit: limit, span: 'hour' }),
 | 
				
			||||||
 | 
								(this as any).api('charts/drive', { limit: limit, span: 'hour' }),
 | 
				
			||||||
 | 
								(this as any).api('charts/network', { limit: limit, span: 'hour' })
 | 
				
			||||||
 | 
							]), Promise.all([
 | 
				
			||||||
 | 
								(this as any).api('charts/federation', { limit: limit, span: 'day' }),
 | 
				
			||||||
 | 
								(this as any).api('charts/users', { limit: limit, span: 'day' }),
 | 
				
			||||||
 | 
								(this as any).api('charts/notes', { limit: limit, span: 'day' }),
 | 
				
			||||||
 | 
								(this as any).api('charts/drive', { limit: limit, span: 'day' }),
 | 
				
			||||||
 | 
								(this as any).api('charts/network', { limit: limit, span: 'day' })
 | 
				
			||||||
 | 
							])]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const chart = {
 | 
				
			||||||
 | 
								perHour: {
 | 
				
			||||||
 | 
									federation: perHour[0],
 | 
				
			||||||
 | 
									users: perHour[1],
 | 
				
			||||||
 | 
									notes: perHour[2],
 | 
				
			||||||
 | 
									drive: perHour[3],
 | 
				
			||||||
 | 
									network: perHour[4]
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								perDay: {
 | 
				
			||||||
 | 
									federation: perDay[0],
 | 
				
			||||||
 | 
									users: perDay[1],
 | 
				
			||||||
 | 
									notes: perDay[2],
 | 
				
			||||||
 | 
									drive: perDay[3],
 | 
				
			||||||
 | 
									network: perDay[4]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.chart = chart;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.render();
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							setSrc(src) {
 | 
				
			||||||
 | 
								this.src = src;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							render() {
 | 
				
			||||||
 | 
								if (this.chartInstance) {
 | 
				
			||||||
 | 
									this.chartInstance.destroy();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.chartInstance = new ApexCharts(this.$refs.chart, Object.assign({
 | 
				
			||||||
 | 
									chart: {
 | 
				
			||||||
 | 
										type: 'area',
 | 
				
			||||||
 | 
										height: 300,
 | 
				
			||||||
 | 
										animations: {
 | 
				
			||||||
 | 
											dynamicAnimation: {
 | 
				
			||||||
 | 
												enabled: false
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										toolbar: {
 | 
				
			||||||
 | 
											show: false
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										zoom: {
 | 
				
			||||||
 | 
											enabled: false
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									dataLabels: {
 | 
				
			||||||
 | 
										enabled: false
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									grid: {
 | 
				
			||||||
 | 
										clipMarkers: false,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									stroke: {
 | 
				
			||||||
 | 
										curve: 'straight',
 | 
				
			||||||
 | 
										width: 2
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									xaxis: {
 | 
				
			||||||
 | 
										type: 'datetime'
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									yaxis: {
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}, this.data));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.chartInstance.render();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							getDate(i: number) {
 | 
				
			||||||
 | 
								const y = this.now.getFullYear();
 | 
				
			||||||
 | 
								const m = this.now.getMonth();
 | 
				
			||||||
 | 
								const d = this.now.getDate();
 | 
				
			||||||
 | 
								const h = this.now.getHours();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return (
 | 
				
			||||||
 | 
									this.span == 'day' ? new Date(y, m, d - i) :
 | 
				
			||||||
 | 
									this.span == 'hour' ? new Date(y, m, d, h - i) :
 | 
				
			||||||
 | 
									null
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							format(arr) {
 | 
				
			||||||
 | 
								return arr.map((v, i) => ({ x: this.getDate(i).getTime(), y: v }));
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							federationInstancesChart(total: boolean): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										data: this.format(total
 | 
				
			||||||
 | 
											? this.stats.federation.instance.total
 | 
				
			||||||
 | 
											: sum(this.stats.federation.instance.inc, negate(this.stats.federation.instance.dec))
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							notesChart(type: string): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'All',
 | 
				
			||||||
 | 
										type: 'line',
 | 
				
			||||||
 | 
										data: this.format(type == 'combined'
 | 
				
			||||||
 | 
											? sum(this.stats.notes.local.inc, negate(this.stats.notes.local.dec), this.stats.notes.remote.inc, negate(this.stats.notes.remote.dec))
 | 
				
			||||||
 | 
											: sum(this.stats.notes[type].inc, negate(this.stats.notes[type].dec))
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Renotes',
 | 
				
			||||||
 | 
										type: 'area',
 | 
				
			||||||
 | 
										data: this.format(type == 'combined'
 | 
				
			||||||
 | 
											? sum(this.stats.notes.local.diffs.renote, this.stats.notes.remote.diffs.renote)
 | 
				
			||||||
 | 
											: this.stats.notes[type].diffs.renote
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Replies',
 | 
				
			||||||
 | 
										type: 'area',
 | 
				
			||||||
 | 
										data: this.format(type == 'combined'
 | 
				
			||||||
 | 
											? sum(this.stats.notes.local.diffs.reply, this.stats.notes.remote.diffs.reply)
 | 
				
			||||||
 | 
											: this.stats.notes[type].diffs.reply
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Normal',
 | 
				
			||||||
 | 
										type: 'area',
 | 
				
			||||||
 | 
										data: this.format(type == 'combined'
 | 
				
			||||||
 | 
											? sum(this.stats.notes.local.diffs.normal, this.stats.notes.remote.diffs.normal)
 | 
				
			||||||
 | 
											: this.stats.notes[type].diffs.normal
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							notesTotalChart(): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'Combined',
 | 
				
			||||||
 | 
										data: this.format(sum(this.stats.notes.local.total, this.stats.notes.remote.total))
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Local',
 | 
				
			||||||
 | 
										data: this.format(this.stats.notes.local.total)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Remote',
 | 
				
			||||||
 | 
										data: this.format(this.stats.notes.remote.total)
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							usersChart(total: boolean): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'Combined',
 | 
				
			||||||
 | 
										data: this.format(total
 | 
				
			||||||
 | 
											? sum(this.stats.users.local.total, this.stats.users.remote.total)
 | 
				
			||||||
 | 
											: sum(this.stats.users.local.inc, negate(this.stats.users.local.dec), this.stats.users.remote.inc, negate(this.stats.users.remote.dec))
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Local',
 | 
				
			||||||
 | 
										data: this.format(total
 | 
				
			||||||
 | 
											? this.stats.users.local.total
 | 
				
			||||||
 | 
											: sum(this.stats.users.local.inc, negate(this.stats.users.local.dec))
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Remote',
 | 
				
			||||||
 | 
										data: this.format(total
 | 
				
			||||||
 | 
											? this.stats.users.remote.total
 | 
				
			||||||
 | 
											: sum(this.stats.users.remote.inc, negate(this.stats.users.remote.dec))
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							driveChart(): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'All',
 | 
				
			||||||
 | 
										data: this.format(
 | 
				
			||||||
 | 
											sum(
 | 
				
			||||||
 | 
												this.stats.drive.local.incSize,
 | 
				
			||||||
 | 
												negate(this.stats.drive.local.decSize),
 | 
				
			||||||
 | 
												this.stats.drive.remote.incSize,
 | 
				
			||||||
 | 
												negate(this.stats.drive.remote.decSize)
 | 
				
			||||||
 | 
											)
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Local +',
 | 
				
			||||||
 | 
										data: this.format(this.stats.drive.local.incSize)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Local -',
 | 
				
			||||||
 | 
										data: this.format(negate(this.stats.drive.local.decSize))
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Remote +',
 | 
				
			||||||
 | 
										data: this.format(this.stats.drive.remote.incSize)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Remote -',
 | 
				
			||||||
 | 
										data: this.format(negate(this.stats.drive.remote.decSize))
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							driveTotalChart(): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'Combined',
 | 
				
			||||||
 | 
										data: this.format(sum(this.stats.drive.local.totalSize, this.stats.drive.remote.totalSize))
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Local',
 | 
				
			||||||
 | 
										data: this.format(this.stats.drive.local.totalSize)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Remote',
 | 
				
			||||||
 | 
										data: this.format(this.stats.drive.remote.totalSize)
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							driveFilesChart(): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'All',
 | 
				
			||||||
 | 
										data: this.format(
 | 
				
			||||||
 | 
											sum(
 | 
				
			||||||
 | 
												this.stats.drive.local.incCount,
 | 
				
			||||||
 | 
												negate(this.stats.drive.local.decCount),
 | 
				
			||||||
 | 
												this.stats.drive.remote.incCount,
 | 
				
			||||||
 | 
												negate(this.stats.drive.remote.decCount)
 | 
				
			||||||
 | 
											)
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Local +',
 | 
				
			||||||
 | 
										data: this.format(this.stats.drive.local.incCount)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Local -',
 | 
				
			||||||
 | 
										data: this.format(negate(this.stats.drive.local.decCount))
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Remote +',
 | 
				
			||||||
 | 
										data: this.format(this.stats.drive.remote.incCount)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Remote -',
 | 
				
			||||||
 | 
										data: this.format(negate(this.stats.drive.remote.decCount))
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							driveFilesTotalChart(): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'Combined',
 | 
				
			||||||
 | 
										data: this.format(sum(this.stats.drive.local.totalCount, this.stats.drive.remote.totalCount))
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Local',
 | 
				
			||||||
 | 
										data: this.format(this.stats.drive.local.totalCount)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Remote',
 | 
				
			||||||
 | 
										data: this.format(this.stats.drive.remote.totalCount)
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							networkRequestsChart(): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'Incoming',
 | 
				
			||||||
 | 
										data: this.format(this.stats.network.incomingRequests)
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							networkTimeChart(): any {
 | 
				
			||||||
 | 
								const data = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (let i = 0; i < limit; i++) {
 | 
				
			||||||
 | 
									data.push(this.stats.network.incomingRequests[i] != 0 ? (this.stats.network.totalTime[i] / this.stats.network.incomingRequests[i]) : 0);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'Avg time',
 | 
				
			||||||
 | 
										data: this.format(data)
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							networkUsageChart(): any {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'Incoming',
 | 
				
			||||||
 | 
										data: this.format(this.stats.network.incomingBytes)
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Outgoing',
 | 
				
			||||||
 | 
										data: this.format(this.stats.network.outgoingBytes)
 | 
				
			||||||
 | 
									}]
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.qvgidhudpqhjttdhxubzuyrhyzgslujw
 | 
				
			||||||
 | 
						display block
 | 
				
			||||||
 | 
						flex 1
 | 
				
			||||||
 | 
						padding 32px 24px
 | 
				
			||||||
 | 
						padding-bottom 0
 | 
				
			||||||
 | 
						box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
 | 
				
			||||||
 | 
						background var(--face)
 | 
				
			||||||
 | 
						border-radius 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> header
 | 
				
			||||||
 | 
							display flex
 | 
				
			||||||
 | 
							margin 0 8px
 | 
				
			||||||
 | 
							padding 0 0 8px 0
 | 
				
			||||||
 | 
							font-size 1em
 | 
				
			||||||
 | 
							color #555
 | 
				
			||||||
 | 
							border-bottom solid 1px #eee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> b
 | 
				
			||||||
 | 
								margin-right 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> *:last-child
 | 
				
			||||||
 | 
								margin-left auto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								*
 | 
				
			||||||
 | 
									&:not(.active)
 | 
				
			||||||
 | 
										color var(--primary)
 | 
				
			||||||
 | 
										cursor pointer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										170
									
								
								src/client/app/admin/views/cpu-memory.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/client/app/admin/views/cpu-memory.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,170 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div class="zyknedwtlthezamcjlolyusmipqmjgxz">
 | 
				
			||||||
 | 
						<div>
 | 
				
			||||||
 | 
							<header>
 | 
				
			||||||
 | 
								<span>%fa:microchip% CPU <span>{{ cpuP }}%</span></span>
 | 
				
			||||||
 | 
								<span v-if="meta">{{ meta.cpu.model }}</span>
 | 
				
			||||||
 | 
							</header>
 | 
				
			||||||
 | 
							<div ref="cpu"></div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div>
 | 
				
			||||||
 | 
							<header>
 | 
				
			||||||
 | 
								<span>%fa:memory% MEM <span>{{ memP }}%</span></span>
 | 
				
			||||||
 | 
								<span v-if="meta"></span>
 | 
				
			||||||
 | 
							</header>
 | 
				
			||||||
 | 
							<div ref="mem"></div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import * as ApexCharts from 'apexcharts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						props: ['connection'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								stats: [],
 | 
				
			||||||
 | 
								cpuChart: null,
 | 
				
			||||||
 | 
								memChart: null,
 | 
				
			||||||
 | 
								cpuP: '',
 | 
				
			||||||
 | 
								memP: '',
 | 
				
			||||||
 | 
								meta: null
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						watch: {
 | 
				
			||||||
 | 
							stats(stats) {
 | 
				
			||||||
 | 
								this.cpuChart.updateSeries([{
 | 
				
			||||||
 | 
									data: stats.map((x, i) => ({ x: i, y: x.cpu_usage }))
 | 
				
			||||||
 | 
								}]);
 | 
				
			||||||
 | 
								this.memChart.updateSeries([{
 | 
				
			||||||
 | 
									data: stats.map((x, i) => ({ x: i, y: (x.mem.used / x.mem.total) }))
 | 
				
			||||||
 | 
								}]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mounted() {
 | 
				
			||||||
 | 
							(this as any).os.getMeta().then(meta => {
 | 
				
			||||||
 | 
								this.meta = meta;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.connection.on('stats', this.onStats);
 | 
				
			||||||
 | 
							this.connection.on('statsLog', this.onStatsLog);
 | 
				
			||||||
 | 
							this.connection.send('requestLog', {
 | 
				
			||||||
 | 
								id: Math.random().toString().substr(2, 8),
 | 
				
			||||||
 | 
								length: 200
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const chartOpts = {
 | 
				
			||||||
 | 
								chart: {
 | 
				
			||||||
 | 
									type: 'area',
 | 
				
			||||||
 | 
									height: 200,
 | 
				
			||||||
 | 
									animations: {
 | 
				
			||||||
 | 
										dynamicAnimation: {
 | 
				
			||||||
 | 
											enabled: false
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									toolbar: {
 | 
				
			||||||
 | 
										show: false
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									zoom: {
 | 
				
			||||||
 | 
										enabled: false
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								dataLabels: {
 | 
				
			||||||
 | 
									enabled: false
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								grid: {
 | 
				
			||||||
 | 
									clipMarkers: false,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								stroke: {
 | 
				
			||||||
 | 
									curve: 'straight',
 | 
				
			||||||
 | 
									width: 2
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								tooltip: {
 | 
				
			||||||
 | 
									enabled: false
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								series: [{
 | 
				
			||||||
 | 
									data: []
 | 
				
			||||||
 | 
								}],
 | 
				
			||||||
 | 
								xaxis: {
 | 
				
			||||||
 | 
									type: 'numeric',
 | 
				
			||||||
 | 
									labels: {
 | 
				
			||||||
 | 
										show: false
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									tooltip: {
 | 
				
			||||||
 | 
										enabled: false
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								yaxis: {
 | 
				
			||||||
 | 
									show: false,
 | 
				
			||||||
 | 
									min: 0,
 | 
				
			||||||
 | 
									max: 1
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.cpuChart = new ApexCharts(this.$refs.cpu, chartOpts);
 | 
				
			||||||
 | 
							this.memChart = new ApexCharts(this.$refs.mem, chartOpts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.cpuChart.render();
 | 
				
			||||||
 | 
							this.memChart.render();
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						beforeDestroy() {
 | 
				
			||||||
 | 
							this.connection.off('stats', this.onStats);
 | 
				
			||||||
 | 
							this.connection.off('statsLog', this.onStatsLog);
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							onStats(stats) {
 | 
				
			||||||
 | 
								this.stats.push(stats);
 | 
				
			||||||
 | 
								if (this.stats.length > 200) this.stats.shift();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.cpuP = (stats.cpu_usage * 100).toFixed(0);
 | 
				
			||||||
 | 
								this.memP = (stats.mem.used / stats.mem.total * 100).toFixed(0);
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onStatsLog(statsLog) {
 | 
				
			||||||
 | 
								statsLog.reverse().forEach(stats => this.onStats(stats));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.zyknedwtlthezamcjlolyusmipqmjgxz
 | 
				
			||||||
 | 
						display flex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> div
 | 
				
			||||||
 | 
							display block
 | 
				
			||||||
 | 
							flex 1
 | 
				
			||||||
 | 
							padding 20px 12px 0 12px
 | 
				
			||||||
 | 
							box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
 | 
				
			||||||
 | 
							background var(--face)
 | 
				
			||||||
 | 
							border-radius 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							&:first-child
 | 
				
			||||||
 | 
								margin-right 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> header
 | 
				
			||||||
 | 
								display flex
 | 
				
			||||||
 | 
								padding 0 8px
 | 
				
			||||||
 | 
								margin-bottom -16px
 | 
				
			||||||
 | 
								color #555
 | 
				
			||||||
 | 
								font-size 14px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> span
 | 
				
			||||||
 | 
									&:last-child
 | 
				
			||||||
 | 
										margin-left auto
 | 
				
			||||||
 | 
										opacity 0.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> span
 | 
				
			||||||
 | 
										opacity 0.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> div
 | 
				
			||||||
 | 
								margin-bottom -10px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										211
									
								
								src/client/app/admin/views/dashboard.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/client/app/admin/views/dashboard.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div class="obdskegsannmntldydackcpzezagxqfy">
 | 
				
			||||||
 | 
						<header v-if="meta">
 | 
				
			||||||
 | 
							<p><b>Misskey</b><span>{{ meta.version }}</span></p>
 | 
				
			||||||
 | 
							<p><b>Machine</b><span>{{ meta.machine }}</span></p>
 | 
				
			||||||
 | 
							<p><b>OS</b><span>{{ meta.os }}</span></p>
 | 
				
			||||||
 | 
							<p><b>Node</b><span>{{ meta.node }}</span></p>
 | 
				
			||||||
 | 
							<p>%i18n:common.ai-chan-kawaii%</p>
 | 
				
			||||||
 | 
						</header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<div v-if="stats" class="stats">
 | 
				
			||||||
 | 
							<div>
 | 
				
			||||||
 | 
								<div>
 | 
				
			||||||
 | 
									<div>%fa:user%</div>
 | 
				
			||||||
 | 
									<div>
 | 
				
			||||||
 | 
										<span>%i18n:@accounts%</span>
 | 
				
			||||||
 | 
										<b class="primary">{{ stats.originalUsersCount | number }}</b>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div>
 | 
				
			||||||
 | 
									<span>%fa:home% %i18n:@this-instance%</span>
 | 
				
			||||||
 | 
									<span @click="setChartSrc('users')">%fa:chart-bar R%</span>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<div>
 | 
				
			||||||
 | 
								<div>
 | 
				
			||||||
 | 
									<div>%fa:pencil-alt%</div>
 | 
				
			||||||
 | 
									<div>
 | 
				
			||||||
 | 
										<span>%i18n:@notes%</span>
 | 
				
			||||||
 | 
										<b class="primary">{{ stats.originalNotesCount | number }}</b>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div>
 | 
				
			||||||
 | 
									<span>%fa:home% %i18n:@this-instance%</span>
 | 
				
			||||||
 | 
									<span @click="setChartSrc('notes')">%fa:chart-bar R%</span>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<div>
 | 
				
			||||||
 | 
								<div>
 | 
				
			||||||
 | 
									<div>%fa:database%</div>
 | 
				
			||||||
 | 
									<div>
 | 
				
			||||||
 | 
										<span>%i18n:@drive%</span>
 | 
				
			||||||
 | 
										<b>{{ stats.driveUsageLocal | bytes }}</b>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div>
 | 
				
			||||||
 | 
									<span>%fa:home% %i18n:@this-instance%</span>
 | 
				
			||||||
 | 
									<span @click="setChartSrc('drive')">%fa:chart-bar R%</span>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<div>
 | 
				
			||||||
 | 
								<div>
 | 
				
			||||||
 | 
									<div>%fa:hdd R%</div>
 | 
				
			||||||
 | 
									<div>
 | 
				
			||||||
 | 
										<span>%i18n:@instances%</span>
 | 
				
			||||||
 | 
										<b>{{ stats.instances | number }}</b>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div>
 | 
				
			||||||
 | 
									<span>%fa:globe% %i18n:@federated%</span>
 | 
				
			||||||
 | 
									<span @click="setChartSrc('federation-instances-total')">%fa:chart-bar R%</span>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<div class="charts">
 | 
				
			||||||
 | 
							<x-charts ref="charts"/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<div class="cpu-memory">
 | 
				
			||||||
 | 
							<x-cpu-memory :connection="connection"/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<div class="ap">
 | 
				
			||||||
 | 
							<x-ap-log/>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from "vue";
 | 
				
			||||||
 | 
					import XCpuMemory from "./cpu-memory.vue";
 | 
				
			||||||
 | 
					import XCharts from "./charts.vue";
 | 
				
			||||||
 | 
					import XApLog from "./ap-log.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						components: {
 | 
				
			||||||
 | 
							XCpuMemory,
 | 
				
			||||||
 | 
							XCharts,
 | 
				
			||||||
 | 
							XApLog
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								stats: null,
 | 
				
			||||||
 | 
								connection: null,
 | 
				
			||||||
 | 
								meta: null
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						created() {
 | 
				
			||||||
 | 
							this.connection = (this as any).os.stream.useSharedConnection('serverStats');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							(this as any).os.getMeta().then(meta => {
 | 
				
			||||||
 | 
								this.meta = meta;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							(this as any).api('stats').then(stats => {
 | 
				
			||||||
 | 
								this.stats = stats;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						beforeDestroy() {
 | 
				
			||||||
 | 
							this.connection.dispose();
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							setChartSrc(src) {
 | 
				
			||||||
 | 
								this.$refs.charts.setSrc(src);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.obdskegsannmntldydackcpzezagxqfy
 | 
				
			||||||
 | 
						> header
 | 
				
			||||||
 | 
							display flex
 | 
				
			||||||
 | 
							margin-bottom 16px
 | 
				
			||||||
 | 
							padding-bottom 16px
 | 
				
			||||||
 | 
							border-bottom solid 1px #ccc
 | 
				
			||||||
 | 
							color #777
 | 
				
			||||||
 | 
							font-size 14px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> p
 | 
				
			||||||
 | 
								display inline
 | 
				
			||||||
 | 
								margin 0 32px 0 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> b
 | 
				
			||||||
 | 
									&:after
 | 
				
			||||||
 | 
										content ':'
 | 
				
			||||||
 | 
										margin-right 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								&:last-child
 | 
				
			||||||
 | 
									margin-left auto
 | 
				
			||||||
 | 
									margin-right 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> .stats
 | 
				
			||||||
 | 
							display flex
 | 
				
			||||||
 | 
							justify-content space-between
 | 
				
			||||||
 | 
							margin-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> div
 | 
				
			||||||
 | 
								flex 1
 | 
				
			||||||
 | 
								max-width 300px
 | 
				
			||||||
 | 
								margin-right 16px
 | 
				
			||||||
 | 
								color var(--text)
 | 
				
			||||||
 | 
								box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
 | 
				
			||||||
 | 
								background var(--face)
 | 
				
			||||||
 | 
								border-radius 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								&:last-child
 | 
				
			||||||
 | 
									margin-right 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> div:first-child
 | 
				
			||||||
 | 
									display flex
 | 
				
			||||||
 | 
									align-items center
 | 
				
			||||||
 | 
									text-align center
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:last-child
 | 
				
			||||||
 | 
										margin-right 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> div:first-child
 | 
				
			||||||
 | 
										padding 16px 24px
 | 
				
			||||||
 | 
										font-size 28px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> div:last-child
 | 
				
			||||||
 | 
										flex 1
 | 
				
			||||||
 | 
										padding 16px 32px 16px 0
 | 
				
			||||||
 | 
										text-align right
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										> span
 | 
				
			||||||
 | 
											font-size 70%
 | 
				
			||||||
 | 
											opacity 0.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										> b
 | 
				
			||||||
 | 
											display block
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											&.primary
 | 
				
			||||||
 | 
												color var(--primary)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> div:last-child
 | 
				
			||||||
 | 
									display flex
 | 
				
			||||||
 | 
									padding 6px 16px
 | 
				
			||||||
 | 
									border-top solid 1px #eee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> span
 | 
				
			||||||
 | 
										font-size 70%
 | 
				
			||||||
 | 
										opacity 0.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										&:last-child
 | 
				
			||||||
 | 
											margin-left auto
 | 
				
			||||||
 | 
											cursor pointer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> .charts
 | 
				
			||||||
 | 
							margin-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> .cpu-memory
 | 
				
			||||||
 | 
							margin-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										108
									
								
								src/client/app/admin/views/emoji.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/client/app/admin/views/emoji.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<div slot="title">%fa:plus% %i18n:@add-emoji.title%</div>
 | 
				
			||||||
 | 
							<section class="fit-top">
 | 
				
			||||||
 | 
								<ui-input v-model="name">
 | 
				
			||||||
 | 
									<span>%i18n:@add-emoji.name%</span>
 | 
				
			||||||
 | 
									<span slot="text">%i18n:@add-emoji.name-desc%</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-input v-model="aliases">
 | 
				
			||||||
 | 
									<span>%i18n:@add-emoji.aliases%</span>
 | 
				
			||||||
 | 
									<span slot="text">%i18n:@add-emoji.aliases-desc%</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-input v-model="url">
 | 
				
			||||||
 | 
									<span>%i18n:@add-emoji.url%</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-button @click="add">%i18n:@add-emoji.add%</ui-button>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<div slot="title">%fa:grin R% %i18n:@emojis.title%</div>
 | 
				
			||||||
 | 
							<section v-for="emoji in emojis">
 | 
				
			||||||
 | 
								<img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/>
 | 
				
			||||||
 | 
								<ui-input v-model="emoji.name">
 | 
				
			||||||
 | 
									<span>%i18n:@add-emoji.name%</span>
 | 
				
			||||||
 | 
									<span slot="text">%i18n:@add-emoji.name-desc%</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-input v-model="emoji.aliases">
 | 
				
			||||||
 | 
									<span>%i18n:@add-emoji.aliases%</span>
 | 
				
			||||||
 | 
									<span slot="text">%i18n:@add-emoji.aliases-desc%</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-input v-model="emoji.url">
 | 
				
			||||||
 | 
									<span>%i18n:@add-emoji.url%</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-button-group>
 | 
				
			||||||
 | 
									<ui-button inline @click="updateEmoji(emoji)">%fa:save R% %i18n:@emojis.update%</ui-button>
 | 
				
			||||||
 | 
									<ui-button inline @click="removeEmoji(emoji)">%fa:trash-alt R% %i18n:@emojis.remove%</ui-button>
 | 
				
			||||||
 | 
								</ui-button-group>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								name: '',
 | 
				
			||||||
 | 
								url: '',
 | 
				
			||||||
 | 
								aliases: '',
 | 
				
			||||||
 | 
								emojis: []
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mounted() {
 | 
				
			||||||
 | 
							this.fetchEmojis();
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							add() {
 | 
				
			||||||
 | 
								(this as any).api('admin/emoji/add', {
 | 
				
			||||||
 | 
									name: this.name,
 | 
				
			||||||
 | 
									url: this.url,
 | 
				
			||||||
 | 
									aliases: this.aliases.split(' ')
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Added` });
 | 
				
			||||||
 | 
									this.fetchEmojis();
 | 
				
			||||||
 | 
								}).catch(e => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fetchEmojis() {
 | 
				
			||||||
 | 
								(this as any).api('admin/emoji/list').then(emojis => {
 | 
				
			||||||
 | 
									emojis.forEach(e => e.aliases = (e.aliases || []).join(' '));
 | 
				
			||||||
 | 
									this.emojis = emojis;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							updateEmoji(emoji) {
 | 
				
			||||||
 | 
								(this as any).api('admin/emoji/update', {
 | 
				
			||||||
 | 
									id: emoji.id,
 | 
				
			||||||
 | 
									name: emoji.name,
 | 
				
			||||||
 | 
									url: emoji.url,
 | 
				
			||||||
 | 
									aliases: emoji.aliases.split(' ')
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Updated` });
 | 
				
			||||||
 | 
								}).catch(e => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							removeEmoji(emoji) {
 | 
				
			||||||
 | 
								(this as any).api('admin/emoji/remove', {
 | 
				
			||||||
 | 
									id: emoji.id
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Removed` });
 | 
				
			||||||
 | 
									this.fetchEmojis();
 | 
				
			||||||
 | 
								}).catch(e => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@@ -1,8 +1,12 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="jdnqwkzlnxcfftthoybjxrebyolvoucw mk-admin-card">
 | 
					<div>
 | 
				
			||||||
	<header>%i18n:@hided-tags%</header>
 | 
						<ui-card>
 | 
				
			||||||
	<textarea v-model="hidedTags"></textarea>
 | 
							<div slot="title">%i18n:@hided-tags%</div>
 | 
				
			||||||
	<button class="ui" @click="save">%i18n:@save%</button>
 | 
							<section>
 | 
				
			||||||
 | 
								<textarea class="jdnqwkzlnxcfftthoybjxrebyolvoucw" v-model="hidedTags"></textarea>
 | 
				
			||||||
 | 
								<ui-button @click="save">%i18n:@save%</ui-button>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,11 +39,8 @@ export default Vue.extend({
 | 
				
			|||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.jdnqwkzlnxcfftthoybjxrebyolvoucw
 | 
					.jdnqwkzlnxcfftthoybjxrebyolvoucw
 | 
				
			||||||
	textarea
 | 
						width 100%
 | 
				
			||||||
		width 100%
 | 
						min-height 300px
 | 
				
			||||||
		min-height 300px
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										195
									
								
								src/client/app/admin/views/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								src/client/app/admin/views/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,195 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div class="mk-admin">
 | 
				
			||||||
 | 
						<nav>
 | 
				
			||||||
 | 
							<div class="mi">
 | 
				
			||||||
 | 
								<img svg-inline src="../assets/header-icon.svg"/>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<div class="me">
 | 
				
			||||||
 | 
								<img class="avatar" :src="$store.state.i.avatarUrl" alt="avatar"/>
 | 
				
			||||||
 | 
								<p class="name">{{ $store.state.i | userName }}</p>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<ul>
 | 
				
			||||||
 | 
								<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%fa:home .fw%%i18n:@dashboard%</li>
 | 
				
			||||||
 | 
								<li @click="nav('instance')" :class="{ active: page == 'instance' }">%fa:cog .fw%%i18n:@instance%</li>
 | 
				
			||||||
 | 
								<li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li>
 | 
				
			||||||
 | 
								<li @click="nav('emoji')" :class="{ active: page == 'emoji' }">%fa:grin R .fw%%i18n:@emoji%</li>
 | 
				
			||||||
 | 
								<li @click="nav('announcements')" :class="{ active: page == 'announcements' }">%fa:broadcast-tower .fw%%i18n:@announcements%</li>
 | 
				
			||||||
 | 
								<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }">%fa:hashtag .fw%%i18n:@hashtags%</li>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:common.drive%</li> -->
 | 
				
			||||||
 | 
								<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> -->
 | 
				
			||||||
 | 
							</ul>
 | 
				
			||||||
 | 
							<div class="back-to-misskey">
 | 
				
			||||||
 | 
								<a href="/">%fa:arrow-left% %i18n:@back-to-misskey%</a>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<div class="version">
 | 
				
			||||||
 | 
								<small>Misskey {{ version }}</small>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</nav>
 | 
				
			||||||
 | 
						<main>
 | 
				
			||||||
 | 
							<div v-show="page == 'dashboard'"><x-dashboard/></div>
 | 
				
			||||||
 | 
							<div v-show="page == 'instance'"><x-instance/></div>
 | 
				
			||||||
 | 
							<div v-if="page == 'users'"><x-users/></div>
 | 
				
			||||||
 | 
							<div v-show="page == 'emoji'"><x-emoji/></div>
 | 
				
			||||||
 | 
							<div v-show="page == 'announcements'"><x-announcements/></div>
 | 
				
			||||||
 | 
							<div v-show="page == 'hashtags'"><x-hashtags/></div>
 | 
				
			||||||
 | 
							<div v-if="page == 'drive'"></div>
 | 
				
			||||||
 | 
							<div v-if="page == 'update'"></div>
 | 
				
			||||||
 | 
						</main>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from "vue";
 | 
				
			||||||
 | 
					import { version } from '../../config';
 | 
				
			||||||
 | 
					import XDashboard from "./dashboard.vue";
 | 
				
			||||||
 | 
					import XInstance from "./instance.vue";
 | 
				
			||||||
 | 
					import XEmoji from "./emoji.vue";
 | 
				
			||||||
 | 
					import XAnnouncements from "./announcements.vue";
 | 
				
			||||||
 | 
					import XHashtags from "./hashtags.vue";
 | 
				
			||||||
 | 
					import XUsers from "./users.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						components: {
 | 
				
			||||||
 | 
							XDashboard,
 | 
				
			||||||
 | 
							XInstance,
 | 
				
			||||||
 | 
							XEmoji,
 | 
				
			||||||
 | 
							XAnnouncements,
 | 
				
			||||||
 | 
							XHashtags,
 | 
				
			||||||
 | 
							XUsers
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								page: 'dashboard',
 | 
				
			||||||
 | 
								version
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							nav(page: string) {
 | 
				
			||||||
 | 
								this.page = page;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus">
 | 
				
			||||||
 | 
					.mk-admin
 | 
				
			||||||
 | 
						display flex
 | 
				
			||||||
 | 
						height 100%
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> nav
 | 
				
			||||||
 | 
							position fixed
 | 
				
			||||||
 | 
							z-index 10000
 | 
				
			||||||
 | 
							top 0
 | 
				
			||||||
 | 
							left 0
 | 
				
			||||||
 | 
							width 250px
 | 
				
			||||||
 | 
							height 100vh
 | 
				
			||||||
 | 
							overflow auto
 | 
				
			||||||
 | 
							background #333
 | 
				
			||||||
 | 
							color #fff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> .mi
 | 
				
			||||||
 | 
								text-align center
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> svg
 | 
				
			||||||
 | 
									width 24px
 | 
				
			||||||
 | 
									height 82px
 | 
				
			||||||
 | 
									vertical-align top
 | 
				
			||||||
 | 
									fill #fff
 | 
				
			||||||
 | 
									opacity 0.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> .me
 | 
				
			||||||
 | 
								display flex
 | 
				
			||||||
 | 
								margin 0 16px 16px 16px
 | 
				
			||||||
 | 
								padding 16px 0
 | 
				
			||||||
 | 
								align-items center
 | 
				
			||||||
 | 
								border-top solid 1px #555
 | 
				
			||||||
 | 
								border-bottom solid 1px #555
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> .avatar
 | 
				
			||||||
 | 
									height 48px
 | 
				
			||||||
 | 
									border-radius 100%
 | 
				
			||||||
 | 
									vertical-align middle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> .name
 | 
				
			||||||
 | 
									margin 0 16px
 | 
				
			||||||
 | 
									padding 0
 | 
				
			||||||
 | 
									color #fff
 | 
				
			||||||
 | 
									overflow hidden
 | 
				
			||||||
 | 
									text-overflow ellipsis
 | 
				
			||||||
 | 
									white-space nowrap
 | 
				
			||||||
 | 
									font-size 15px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> .back-to-misskey
 | 
				
			||||||
 | 
								margin 16px 16px 0 16px
 | 
				
			||||||
 | 
								padding 0
 | 
				
			||||||
 | 
								border-top solid 1px #555
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> a
 | 
				
			||||||
 | 
									display block
 | 
				
			||||||
 | 
									padding 16px 4px
 | 
				
			||||||
 | 
									color inherit
 | 
				
			||||||
 | 
									text-decoration none
 | 
				
			||||||
 | 
									color #eee
 | 
				
			||||||
 | 
									font-size 15px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:hover
 | 
				
			||||||
 | 
										color #fff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> [data-fa]
 | 
				
			||||||
 | 
										margin-right 6px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> .version
 | 
				
			||||||
 | 
								margin 0 16px 16px 16px
 | 
				
			||||||
 | 
								padding-top 16px
 | 
				
			||||||
 | 
								border-top solid 1px #555
 | 
				
			||||||
 | 
								text-align center
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> small
 | 
				
			||||||
 | 
									opacity 0.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> ul
 | 
				
			||||||
 | 
								margin 0
 | 
				
			||||||
 | 
								padding 0
 | 
				
			||||||
 | 
								list-style none
 | 
				
			||||||
 | 
								font-size 15px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> li
 | 
				
			||||||
 | 
									display block
 | 
				
			||||||
 | 
									padding 10px 16px
 | 
				
			||||||
 | 
									margin 0
 | 
				
			||||||
 | 
									cursor pointer
 | 
				
			||||||
 | 
									user-select none
 | 
				
			||||||
 | 
									color #eee
 | 
				
			||||||
 | 
									transition margin-left 0.2s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:hover
 | 
				
			||||||
 | 
										color #fff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> [data-fa]
 | 
				
			||||||
 | 
										margin-right 6px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&.active
 | 
				
			||||||
 | 
										margin-left 8px
 | 
				
			||||||
 | 
										color var(--primary) !important
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										&:after
 | 
				
			||||||
 | 
											content ""
 | 
				
			||||||
 | 
											display block
 | 
				
			||||||
 | 
											position absolute
 | 
				
			||||||
 | 
											top 0
 | 
				
			||||||
 | 
											right 0
 | 
				
			||||||
 | 
											bottom 0
 | 
				
			||||||
 | 
											margin auto 0
 | 
				
			||||||
 | 
											height 0
 | 
				
			||||||
 | 
											border-top solid 16px transparent
 | 
				
			||||||
 | 
											border-right solid 16px var(--bg)
 | 
				
			||||||
 | 
											border-bottom solid 16px transparent
 | 
				
			||||||
 | 
											border-left solid 16px transparent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> main
 | 
				
			||||||
 | 
							width 100%
 | 
				
			||||||
 | 
							padding 32px 32px 32px calc(32px + 250px)
 | 
				
			||||||
 | 
							max-width 1300px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										62
									
								
								src/client/app/admin/views/instance.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/client/app/admin/views/instance.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<div slot="title">%i18n:@banner-url%</div>
 | 
				
			||||||
 | 
							<section class="fit-top">
 | 
				
			||||||
 | 
								<ui-input v-model="bannerUrl"/>
 | 
				
			||||||
 | 
								<ui-button @click="updateMeta">%i18n:@save%</ui-button>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<div slot="title">%i18n:@disable-registration%</div>
 | 
				
			||||||
 | 
							<section>
 | 
				
			||||||
 | 
								<input type="checkbox" v-model="disableRegistration" @change="updateMeta">
 | 
				
			||||||
 | 
								<button class="ui" @click="invite">%i18n:@invite%</button>
 | 
				
			||||||
 | 
								<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<div slot="title">%i18n:@disable-local-timeline%</div>
 | 
				
			||||||
 | 
							<section>
 | 
				
			||||||
 | 
								<input type="checkbox" v-model="disableLocalTimeline" @change="updateMeta">
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								disableRegistration: false,
 | 
				
			||||||
 | 
								disableLocalTimeline: false,
 | 
				
			||||||
 | 
								bannerUrl: null,
 | 
				
			||||||
 | 
								inviteCode: null,
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							invite() {
 | 
				
			||||||
 | 
								(this as any).api('admin/invite').then(x => {
 | 
				
			||||||
 | 
									this.inviteCode = x.code;
 | 
				
			||||||
 | 
								}).catch(e => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							updateMeta() {
 | 
				
			||||||
 | 
								(this as any).api('admin/update-meta', {
 | 
				
			||||||
 | 
									disableRegistration: this.disableRegistration,
 | 
				
			||||||
 | 
									disableLocalTimeline: this.disableLocalTimeline,
 | 
				
			||||||
 | 
									bannerUrl: this.bannerUrl
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Saved` });
 | 
				
			||||||
 | 
								}).catch(e => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										129
									
								
								src/client/app/admin/views/users.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/client/app/admin/views/users.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<div slot="title">%i18n:@verify-user%</div>
 | 
				
			||||||
 | 
							<section class="fit-top">
 | 
				
			||||||
 | 
								<ui-input v-model="verifyUsername" type="text">
 | 
				
			||||||
 | 
									<span slot="prefix">@</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-button @click="verifyUser" :disabled="verifying">%i18n:@verify%</ui-button>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<div slot="title">%i18n:@unverify-user%</div>
 | 
				
			||||||
 | 
							<section class="fit-top">
 | 
				
			||||||
 | 
								<ui-input v-model="unverifyUsername" type="text">
 | 
				
			||||||
 | 
									<span slot="prefix">@</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-button @click="unverifyUser" :disabled="unverifying">%i18n:@unverify%</ui-button>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<div slot="title">%i18n:@suspend-user%</div>
 | 
				
			||||||
 | 
							<section class="fit-top">
 | 
				
			||||||
 | 
								<ui-input v-model="suspendUsername" type="text">
 | 
				
			||||||
 | 
									<span slot="prefix">@</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-button @click="suspendUser" :disabled="suspending">%i18n:@suspend%</ui-button>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ui-card>
 | 
				
			||||||
 | 
							<div slot="title">%i18n:@unsuspend-user%</div>
 | 
				
			||||||
 | 
							<section class="fit-top">
 | 
				
			||||||
 | 
								<ui-input v-model="unsuspendUsername" type="text">
 | 
				
			||||||
 | 
									<span slot="prefix">@</span>
 | 
				
			||||||
 | 
								</ui-input>
 | 
				
			||||||
 | 
								<ui-button @click="unsuspendUser" :disabled="unsuspending">%i18n:@unsuspend%</ui-button>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</ui-card>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from "vue";
 | 
				
			||||||
 | 
					import parseAcct from "../../../../misc/acct/parse";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								verifyUsername: null,
 | 
				
			||||||
 | 
								verifying: false,
 | 
				
			||||||
 | 
								unverifyUsername: null,
 | 
				
			||||||
 | 
								unverifying: false,
 | 
				
			||||||
 | 
								suspendUsername: null,
 | 
				
			||||||
 | 
								suspending: false,
 | 
				
			||||||
 | 
								unsuspendUsername: null,
 | 
				
			||||||
 | 
								unsuspending: false
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							async verifyUser() {
 | 
				
			||||||
 | 
								this.verifying = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const process = async () => {
 | 
				
			||||||
 | 
									const user = await (this as any).os.api('users/show', parseAcct(this.verifyUsername));
 | 
				
			||||||
 | 
									await (this as any).os.api('admin/verify-user', { userId: user.id });
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: '%i18n:@verified%' });
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								await process().catch(e => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.verifying = false;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							async unverifyUser() {
 | 
				
			||||||
 | 
								this.unverifying = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const process = async () => {
 | 
				
			||||||
 | 
									const user = await (this as any).os.api('users/show', parseAcct(this.unverifyUsername));
 | 
				
			||||||
 | 
									await (this as any).os.api('admin/unverify-user', { userId: user.id });
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: '%i18n:@unverified%' });
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								await process().catch(e => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.unverifying = false;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							async suspendUser() {
 | 
				
			||||||
 | 
								this.suspending = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const process = async () => {
 | 
				
			||||||
 | 
									const user = await (this as any).os.api('users/show', parseAcct(this.suspendUsername));
 | 
				
			||||||
 | 
									await (this as any).os.api('admin/suspend-user', { userId: user.id });
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: '%i18n:@suspended%' });
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								await process().catch(e => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.suspending = false;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							async unsuspendUser() {
 | 
				
			||||||
 | 
								this.unsuspending = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const process = async () => {
 | 
				
			||||||
 | 
									const user = await (this as any).os.api('users/show', parseAcct(this.unsuspendUsername));
 | 
				
			||||||
 | 
									await (this as any).os.api('admin/unsuspend-user', { userId: user.id });
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: '%i18n:@unsuspended%' });
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								await process().catch(e => {
 | 
				
			||||||
 | 
									(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.unsuspending = false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
@@ -46,6 +46,7 @@
 | 
				
			|||||||
	if (`${url.pathname}/`.startsWith('/docs/')) app = 'docs';
 | 
						if (`${url.pathname}/`.startsWith('/docs/')) app = 'docs';
 | 
				
			||||||
	if (`${url.pathname}/`.startsWith('/dev/')) app = 'dev';
 | 
						if (`${url.pathname}/`.startsWith('/dev/')) app = 'dev';
 | 
				
			||||||
	if (`${url.pathname}/`.startsWith('/auth/')) app = 'auth';
 | 
						if (`${url.pathname}/`.startsWith('/auth/')) app = 'auth';
 | 
				
			||||||
 | 
						if (`${url.pathname}/`.startsWith('/admin/')) app = 'admin';
 | 
				
			||||||
	//#endregion
 | 
						//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//#region Detect the user language
 | 
						//#region Detect the user language
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import MiOS from '../../mios';
 | 
					import MiOS from '../../mios';
 | 
				
			||||||
import { version as current } from '../../config';
 | 
					import { clientVersion as current } from '../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function(mios: MiOS, force = false, silent = false) {
 | 
					export default async function(mios: MiOS, force = false, silent = false) {
 | 
				
			||||||
	const meta = await mios.getMeta(force);
 | 
						const meta = await mios.getMeta(force);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@
 | 
				
			|||||||
			<span class="emoji" v-if="emoji.url"><img :src="emoji.url" :alt="emoji.emoji"/></span>
 | 
								<span class="emoji" v-if="emoji.url"><img :src="emoji.url" :alt="emoji.emoji"/></span>
 | 
				
			||||||
			<span class="emoji" v-else>{{ emoji.emoji }}</span>
 | 
								<span class="emoji" v-else>{{ emoji.emoji }}</span>
 | 
				
			||||||
			<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
 | 
								<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
 | 
				
			||||||
			<span class="alias" v-if="emoji.alias">({{ emoji.alias }})</span>
 | 
								<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span>
 | 
				
			||||||
		</li>
 | 
							</li>
 | 
				
			||||||
	</ol>
 | 
						</ol>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
@@ -28,14 +28,21 @@ import Vue from 'vue';
 | 
				
			|||||||
import * as emojilib from 'emojilib';
 | 
					import * as emojilib from 'emojilib';
 | 
				
			||||||
import contains from '../../../common/scripts/contains';
 | 
					import contains from '../../../common/scripts/contains';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type EmojiDef = {
 | 
				
			||||||
 | 
						emoji: string;
 | 
				
			||||||
 | 
						name: string;
 | 
				
			||||||
 | 
						aliasOf?: string;
 | 
				
			||||||
 | 
						url?: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const lib = Object.entries(emojilib.lib).filter((x: any) => {
 | 
					const lib = Object.entries(emojilib.lib).filter((x: any) => {
 | 
				
			||||||
	return x[1].category != 'flags';
 | 
						return x[1].category != 'flags';
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emjdb = lib.map((x: any) => ({
 | 
					const emjdb: EmojiDef[] = lib.map((x: any) => ({
 | 
				
			||||||
	emoji: x[1].char,
 | 
						emoji: x[1].char,
 | 
				
			||||||
	name: x[0],
 | 
						name: x[0],
 | 
				
			||||||
	alias: null
 | 
						aliasOf: null
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lib.forEach((x: any) => {
 | 
					lib.forEach((x: any) => {
 | 
				
			||||||
@@ -44,7 +51,7 @@ lib.forEach((x: any) => {
 | 
				
			|||||||
			emjdb.push({
 | 
								emjdb.push({
 | 
				
			||||||
				emoji: x[1].char,
 | 
									emoji: x[1].char,
 | 
				
			||||||
				name: k,
 | 
									name: k,
 | 
				
			||||||
				alias: x[0]
 | 
									aliasOf: x[0]
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -62,7 +69,8 @@ export default Vue.extend({
 | 
				
			|||||||
			hashtags: [],
 | 
								hashtags: [],
 | 
				
			||||||
			emojis: [],
 | 
								emojis: [],
 | 
				
			||||||
			select: -1,
 | 
								select: -1,
 | 
				
			||||||
			emojilib
 | 
								emojilib,
 | 
				
			||||||
 | 
								emojiDb: [] as EmojiDef[]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -91,6 +99,34 @@ export default Vue.extend({
 | 
				
			|||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
 | 
							//#region Construct Emoji DB
 | 
				
			||||||
 | 
							const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis || [];
 | 
				
			||||||
 | 
							const emojiDefinitions: EmojiDef[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							customEmojis.forEach(x => {
 | 
				
			||||||
 | 
								emojiDefinitions.push({
 | 
				
			||||||
 | 
									name: x.name,
 | 
				
			||||||
 | 
									emoji: `:${x.name}:`,
 | 
				
			||||||
 | 
									url: x.url
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (x.aliases) {
 | 
				
			||||||
 | 
									x.aliases.forEach(alias => {
 | 
				
			||||||
 | 
										emojiDefinitions.push({
 | 
				
			||||||
 | 
											name: alias,
 | 
				
			||||||
 | 
											aliasOf: x.name,
 | 
				
			||||||
 | 
											emoji: `:${x.name}:`,
 | 
				
			||||||
 | 
											url: x.url
 | 
				
			||||||
 | 
										});
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							emojiDefinitions.sort((a, b) => a.name.length - b.name.length);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.emojiDb = emojiDefinitions.concat(emjdb);
 | 
				
			||||||
 | 
							//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.textarea.addEventListener('keydown', this.onKeydown);
 | 
							this.textarea.addEventListener('keydown', this.onKeydown);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Array.from(document.querySelectorAll('body *')).forEach(el => {
 | 
							Array.from(document.querySelectorAll('body *')).forEach(el => {
 | 
				
			||||||
@@ -172,39 +208,19 @@ export default Vue.extend({
 | 
				
			|||||||
				const matched = [];
 | 
									const matched = [];
 | 
				
			||||||
				const max = 30;
 | 
									const max = 30;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis;
 | 
									this.emojiDb.some(x => {
 | 
				
			||||||
				customEmojis.some(x => {
 | 
										if (x.name.startsWith(this.q) && !x.aliasOf && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
 | 
				
			||||||
					if (x.name.startsWith(this.q)) matched.push({
 | 
					 | 
				
			||||||
						name: x.name,
 | 
					 | 
				
			||||||
						emoji: `:${x.name}:`,
 | 
					 | 
				
			||||||
						url: x.url
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
					return matched.length == max;
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
				customEmojis.some(x => {
 | 
					 | 
				
			||||||
					const alias = (x.aliases || []).find(a => a.startsWith(this.q));
 | 
					 | 
				
			||||||
					if (alias) matched.push({
 | 
					 | 
				
			||||||
						alias: x.name,
 | 
					 | 
				
			||||||
						name: alias,
 | 
					 | 
				
			||||||
						emoji: `:${x.name}:`,
 | 
					 | 
				
			||||||
						url: x.url
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
					return matched.length == max;
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				emjdb.some(x => {
 | 
					 | 
				
			||||||
					if (x.name.indexOf(this.q) == 0 && !x.alias && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
 | 
					 | 
				
			||||||
					return matched.length == max;
 | 
										return matched.length == max;
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
				if (matched.length < max) {
 | 
									if (matched.length < max) {
 | 
				
			||||||
					emjdb.some(x => {
 | 
										this.emojiDb.some(x => {
 | 
				
			||||||
						if (x.name.indexOf(this.q) == 0 && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
 | 
											if (x.name.startsWith(this.q) && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
 | 
				
			||||||
						return matched.length == max;
 | 
											return matched.length == max;
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if (matched.length < max) {
 | 
									if (matched.length < max) {
 | 
				
			||||||
					emjdb.some(x => {
 | 
										this.emojiDb.some(x => {
 | 
				
			||||||
						if (x.name.indexOf(this.q) > -1 && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
 | 
											if (x.name.includes(this.q) && !matched.some(y => y.emoji == x.emoji)) matched.push(x);
 | 
				
			||||||
						return matched.length == max;
 | 
											return matched.length == max;
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
<a class="a" href="https://github.com/syuilo/misskey" target="_blank" title="View source on Github">
 | 
					<a class="a" href="https://github.com/syuilo/misskey" target="_blank" title="View source on GitHub">
 | 
				
			||||||
	<svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="aria-hidden">
 | 
						<svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="aria-hidden">
 | 
				
			||||||
		<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
 | 
							<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
 | 
				
			||||||
		<path class="octo-arm" d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor"></path>
 | 
							<path class="octo-arm" d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor"></path>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ import Vue from 'vue';
 | 
				
			|||||||
import muteAndBlock from './mute-and-block.vue';
 | 
					import muteAndBlock from './mute-and-block.vue';
 | 
				
			||||||
import error from './error.vue';
 | 
					import error from './error.vue';
 | 
				
			||||||
import apiSettings from './api-settings.vue';
 | 
					import apiSettings from './api-settings.vue';
 | 
				
			||||||
 | 
					import passwordSettings from './password-settings.vue';
 | 
				
			||||||
import driveSettings from './drive-settings.vue';
 | 
					import driveSettings from './drive-settings.vue';
 | 
				
			||||||
import profileEditor from './profile-editor.vue';
 | 
					import profileEditor from './profile-editor.vue';
 | 
				
			||||||
import noteSkeleton from './note-skeleton.vue';
 | 
					import noteSkeleton from './note-skeleton.vue';
 | 
				
			||||||
@@ -41,6 +42,7 @@ import Reversi from './games/reversi/reversi.vue';
 | 
				
			|||||||
import welcomeTimeline from './welcome-timeline.vue';
 | 
					import welcomeTimeline from './welcome-timeline.vue';
 | 
				
			||||||
import uiInput from './ui/input.vue';
 | 
					import uiInput from './ui/input.vue';
 | 
				
			||||||
import uiButton from './ui/button.vue';
 | 
					import uiButton from './ui/button.vue';
 | 
				
			||||||
 | 
					import uiButtonGroup from './ui/button-group.vue';
 | 
				
			||||||
import uiCard from './ui/card.vue';
 | 
					import uiCard from './ui/card.vue';
 | 
				
			||||||
import uiForm from './ui/form.vue';
 | 
					import uiForm from './ui/form.vue';
 | 
				
			||||||
import uiTextarea from './ui/textarea.vue';
 | 
					import uiTextarea from './ui/textarea.vue';
 | 
				
			||||||
@@ -54,6 +56,7 @@ import formRadio from './ui/form/radio.vue';
 | 
				
			|||||||
Vue.component('mk-mute-and-block', muteAndBlock);
 | 
					Vue.component('mk-mute-and-block', muteAndBlock);
 | 
				
			||||||
Vue.component('mk-error', error);
 | 
					Vue.component('mk-error', error);
 | 
				
			||||||
Vue.component('mk-api-settings', apiSettings);
 | 
					Vue.component('mk-api-settings', apiSettings);
 | 
				
			||||||
 | 
					Vue.component('mk-password-settings', passwordSettings);
 | 
				
			||||||
Vue.component('mk-drive-settings', driveSettings);
 | 
					Vue.component('mk-drive-settings', driveSettings);
 | 
				
			||||||
Vue.component('mk-profile-editor', profileEditor);
 | 
					Vue.component('mk-profile-editor', profileEditor);
 | 
				
			||||||
Vue.component('mk-note-skeleton', noteSkeleton);
 | 
					Vue.component('mk-note-skeleton', noteSkeleton);
 | 
				
			||||||
@@ -92,6 +95,7 @@ Vue.component('mk-reversi', Reversi);
 | 
				
			|||||||
Vue.component('mk-welcome-timeline', welcomeTimeline);
 | 
					Vue.component('mk-welcome-timeline', welcomeTimeline);
 | 
				
			||||||
Vue.component('ui-input', uiInput);
 | 
					Vue.component('ui-input', uiInput);
 | 
				
			||||||
Vue.component('ui-button', uiButton);
 | 
					Vue.component('ui-button', uiButton);
 | 
				
			||||||
 | 
					Vue.component('ui-button-group', uiButtonGroup);
 | 
				
			||||||
Vue.component('ui-card', uiCard);
 | 
					Vue.component('ui-card', uiCard);
 | 
				
			||||||
Vue.component('ui-form', uiForm);
 | 
					Vue.component('ui-form', uiForm);
 | 
				
			||||||
Vue.component('ui-textarea', uiTextarea);
 | 
					Vue.component('ui-textarea', uiTextarea);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,9 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
				
			|||||||
		i: {
 | 
							i: {
 | 
				
			||||||
			type: Object,
 | 
								type: Object,
 | 
				
			||||||
			default: null
 | 
								default: null
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							customEmojis: {
 | 
				
			||||||
 | 
								required: false,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -186,17 +189,18 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				case 'emoji': {
 | 
									case 'emoji': {
 | 
				
			||||||
					//#region カスタム絵文字
 | 
										//#region カスタム絵文字
 | 
				
			||||||
					const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis;
 | 
										if (this.customEmojis != null) {
 | 
				
			||||||
					const customEmoji = customEmojis.find(e => e.name == token.emoji || (e.aliases || []).includes(token.emoji));
 | 
											const customEmoji = this.customEmojis.find(e => e.name == token.emoji || (e.aliases || []).includes(token.emoji));
 | 
				
			||||||
					if (customEmoji) {
 | 
											if (customEmoji) {
 | 
				
			||||||
						return [createElement('img', {
 | 
												return [createElement('img', {
 | 
				
			||||||
							attrs: {
 | 
													attrs: {
 | 
				
			||||||
								src: customEmoji.url,
 | 
														src: customEmoji.url,
 | 
				
			||||||
								alt: token.emoji,
 | 
														alt: token.emoji,
 | 
				
			||||||
								title: token.emoji,
 | 
														title: token.emoji,
 | 
				
			||||||
								style: 'height: 2.5em; vertical-align: middle;'
 | 
														style: 'height: 2.5em; vertical-align: middle;'
 | 
				
			||||||
							}
 | 
													}
 | 
				
			||||||
						})];
 | 
												})];
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					//#endregion
 | 
										//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								src/client/app/common/views/components/ui/button-group.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/client/app/common/views/components/ui/button-group.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div class="pfzekjfwkwvadvlujpdnnxfggqgqjoze">
 | 
				
			||||||
 | 
						<slot></slot>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					export default Vue.extend({});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.pfzekjfwkwvadvlujpdnnxfggqgqjoze
 | 
				
			||||||
 | 
						display flex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> *
 | 
				
			||||||
 | 
							flex 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							&:not(:last-child)
 | 
				
			||||||
 | 
								margin-right 16px
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
@@ -14,7 +14,7 @@
 | 
				
			|||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</header>
 | 
									</header>
 | 
				
			||||||
				<div class="text">
 | 
									<div class="text">
 | 
				
			||||||
					<misskey-flavored-markdown v-if="note.text" :text="note.text"/>
 | 
										<misskey-flavored-markdown v-if="note.text" :text="note.text" :customEmojis="note.emojis"/>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Vue.filter('bytes', (v, digits = 0) => {
 | 
					Vue.filter('bytes', (v, digits = 0) => {
 | 
				
			||||||
 | 
						if (v == null) return '?';
 | 
				
			||||||
	const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
 | 
						const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
 | 
				
			||||||
	if (v == 0) return '0';
 | 
						if (v == 0) return '0';
 | 
				
			||||||
	const isMinus = v < 0;
 | 
						const isMinus = v < 0;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Vue.filter('number', (n) => {
 | 
					Vue.filter('number', (n) => {
 | 
				
			||||||
 | 
						if (n == null) return 'N/A';
 | 
				
			||||||
	return n.toLocaleString();
 | 
						return n.toLocaleString();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import { version, codename } from '../../../config';
 | 
					import { clientVersion as version, codename } from '../../../config';
 | 
				
			||||||
import define from '../../../common/define-widget';
 | 
					import define from '../../../common/define-widget';
 | 
				
			||||||
export default define({
 | 
					export default define({
 | 
				
			||||||
	name: 'version'
 | 
						name: 'version'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ declare const _LANGS_: string;
 | 
				
			|||||||
declare const _THEME_COLOR_: string;
 | 
					declare const _THEME_COLOR_: string;
 | 
				
			||||||
declare const _COPYRIGHT_: string;
 | 
					declare const _COPYRIGHT_: string;
 | 
				
			||||||
declare const _VERSION_: string;
 | 
					declare const _VERSION_: string;
 | 
				
			||||||
 | 
					declare const _CLIENT_VERSION_: string;
 | 
				
			||||||
declare const _CODENAME_: string;
 | 
					declare const _CODENAME_: string;
 | 
				
			||||||
declare const _ENV_: string;
 | 
					declare const _ENV_: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,5 +19,6 @@ export const langs = _LANGS_;
 | 
				
			|||||||
export const themeColor = _THEME_COLOR_;
 | 
					export const themeColor = _THEME_COLOR_;
 | 
				
			||||||
export const copyright = _COPYRIGHT_;
 | 
					export const copyright = _COPYRIGHT_;
 | 
				
			||||||
export const version = _VERSION_;
 | 
					export const version = _VERSION_;
 | 
				
			||||||
 | 
					export const clientVersion = _CLIENT_VERSION_;
 | 
				
			||||||
export const codename = _CODENAME_;
 | 
					export const codename = _CODENAME_;
 | 
				
			||||||
export const env = _ENV_;
 | 
					export const env = _ENV_;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,6 @@ import updateBanner from './api/update-banner';
 | 
				
			|||||||
import MkIndex from './views/pages/index.vue';
 | 
					import MkIndex from './views/pages/index.vue';
 | 
				
			||||||
import MkHome from './views/pages/home.vue';
 | 
					import MkHome from './views/pages/home.vue';
 | 
				
			||||||
import MkDeck from './views/pages/deck/deck.vue';
 | 
					import MkDeck from './views/pages/deck/deck.vue';
 | 
				
			||||||
import MkAdmin from './views/pages/admin/admin.vue';
 | 
					 | 
				
			||||||
import MkStats from './views/pages/stats/stats.vue';
 | 
					import MkStats from './views/pages/stats/stats.vue';
 | 
				
			||||||
import MkUser from './views/pages/user/user.vue';
 | 
					import MkUser from './views/pages/user/user.vue';
 | 
				
			||||||
import MkFavorites from './views/pages/favorites.vue';
 | 
					import MkFavorites from './views/pages/favorites.vue';
 | 
				
			||||||
@@ -57,7 +56,6 @@ init(async (launch) => {
 | 
				
			|||||||
			{ path: '/', name: 'index', component: MkIndex },
 | 
								{ path: '/', name: 'index', component: MkIndex },
 | 
				
			||||||
			{ path: '/home', name: 'home', component: MkHome },
 | 
								{ path: '/home', name: 'home', component: MkHome },
 | 
				
			||||||
			{ path: '/deck', name: 'deck', component: MkDeck },
 | 
								{ path: '/deck', name: 'deck', component: MkDeck },
 | 
				
			||||||
			{ path: '/admin', name: 'admin', component: MkAdmin },
 | 
					 | 
				
			||||||
			{ path: '/stats', name: 'stats', component: MkStats },
 | 
								{ path: '/stats', name: 'stats', component: MkStats },
 | 
				
			||||||
			{ path: '/i/customize-home', component: MkHomeCustomize },
 | 
								{ path: '/i/customize-home', component: MkHomeCustomize },
 | 
				
			||||||
			{ path: '/i/favorites', component: MkFavorites },
 | 
								{ path: '/i/favorites', component: MkFavorites },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,9 +32,21 @@ export default Vue.extend({
 | 
				
			|||||||
		this.data.forEach(d => d.total = d.notes + d.replies + d.renotes);
 | 
							this.data.forEach(d => d.total = d.notes + d.replies + d.renotes);
 | 
				
			||||||
		const peak = Math.max.apply(null, this.data.map(d => d.total));
 | 
							const peak = Math.max.apply(null, this.data.map(d => d.total));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const now = new Date();
 | 
				
			||||||
 | 
							const year = now.getFullYear();
 | 
				
			||||||
 | 
							const month = now.getMonth();
 | 
				
			||||||
 | 
							const day = now.getDate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let x = 0;
 | 
							let x = 0;
 | 
				
			||||||
		this.data.slice().reverse().forEach(d => {
 | 
							this.data.slice().reverse().forEach((d, i) => {
 | 
				
			||||||
			d.x = x;
 | 
								d.x = x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const date = new Date(year, month, day - i);
 | 
				
			||||||
 | 
								d.date = {
 | 
				
			||||||
 | 
									year: date.getFullYear(),
 | 
				
			||||||
 | 
									month: date.getMonth(),
 | 
				
			||||||
 | 
									day: date.getDate()
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
			d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
 | 
								d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			d.v = peak == 0 ? 0 : d.total / (peak / 2);
 | 
								d.v = peak == 0 ? 0 : d.total / (peak / 2);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,11 +43,17 @@ export default Vue.extend({
 | 
				
			|||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		(this as any).api('aggregation/users/activity', {
 | 
							(this as any).api('charts/user/notes', {
 | 
				
			||||||
			userId: this.user.id,
 | 
								userId: this.user.id,
 | 
				
			||||||
			limit: 20 * 7
 | 
								span: 'day',
 | 
				
			||||||
 | 
								limit: 7 * 20
 | 
				
			||||||
		}).then(activity => {
 | 
							}).then(activity => {
 | 
				
			||||||
			this.activity = activity;
 | 
								this.activity = activity.diffs.normal.map((_, i) => ({
 | 
				
			||||||
 | 
									total: activity.diffs.normal[i] + activity.diffs.reply[i] + activity.diffs.renote[i],
 | 
				
			||||||
 | 
									notes: activity.diffs.normal[i],
 | 
				
			||||||
 | 
									replies: activity.diffs.reply[i],
 | 
				
			||||||
 | 
									renotes: activity.diffs.renote[i]
 | 
				
			||||||
 | 
								}));
 | 
				
			||||||
			this.fetching = false;
 | 
								this.fetching = false;
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,7 +45,7 @@
 | 
				
			|||||||
				<div class="text">
 | 
									<div class="text">
 | 
				
			||||||
					<span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span>
 | 
										<span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span>
 | 
				
			||||||
					<span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 | 
										<span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 | 
				
			||||||
					<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
 | 
										<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :customEmojis="p.emojis" />
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
				<div class="files" v-if="p.files.length > 0">
 | 
									<div class="files" v-if="p.files.length > 0">
 | 
				
			||||||
					<mk-media-list :media-list="p.files" :raw="true"/>
 | 
										<mk-media-list :media-list="p.files" :raw="true"/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,7 @@
 | 
				
			|||||||
					<div class="text">
 | 
										<div class="text">
 | 
				
			||||||
						<span v-if="appearNote.isHidden" style="opacity: 0.5">%i18n:@private%</span>
 | 
											<span v-if="appearNote.isHidden" style="opacity: 0.5">%i18n:@private%</span>
 | 
				
			||||||
						<a class="reply" v-if="appearNote.reply">%fa:reply%</a>
 | 
											<a class="reply" v-if="appearNote.reply">%fa:reply%</a>
 | 
				
			||||||
						<misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text"/>
 | 
											<misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text" :customEmojis="appearNote.emojis"/>
 | 
				
			||||||
						<a class="rp" v-if="appearNote.renote">RN:</a>
 | 
											<a class="rp" v-if="appearNote.renote">RN:</a>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
					<div class="files" v-if="appearNote.files.length > 0">
 | 
										<div class="files" v-if="appearNote.files.length > 0">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -214,7 +214,7 @@
 | 
				
			|||||||
		<ui-card class="password" v-show="page == 'security'">
 | 
							<ui-card class="password" v-show="page == 'security'">
 | 
				
			||||||
			<div slot="title">%fa:unlock-alt% %i18n:@password%</div>
 | 
								<div slot="title">%fa:unlock-alt% %i18n:@password%</div>
 | 
				
			||||||
			<section>
 | 
								<section>
 | 
				
			||||||
				<x-password/>
 | 
									<mk-password-settings/>
 | 
				
			||||||
			</section>
 | 
								</section>
 | 
				
			||||||
		</ui-card>
 | 
							</ui-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -286,17 +286,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import XPassword from './settings.password.vue';
 | 
					 | 
				
			||||||
import X2fa from './settings.2fa.vue';
 | 
					import X2fa from './settings.2fa.vue';
 | 
				
			||||||
import XApps from './settings.apps.vue';
 | 
					import XApps from './settings.apps.vue';
 | 
				
			||||||
import XSignins from './settings.signins.vue';
 | 
					import XSignins from './settings.signins.vue';
 | 
				
			||||||
import XTags from './settings.tags.vue';
 | 
					import XTags from './settings.tags.vue';
 | 
				
			||||||
import { url, langs, version } from '../../../config';
 | 
					import { url, langs, clientVersion as version } from '../../../config';
 | 
				
			||||||
import checkForUpdate from '../../../common/scripts/check-for-update';
 | 
					import checkForUpdate from '../../../common/scripts/check-for-update';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
		XPassword,
 | 
					 | 
				
			||||||
		X2fa,
 | 
							X2fa,
 | 
				
			||||||
		XApps,
 | 
							XApps,
 | 
				
			||||||
		XSignins,
 | 
							XSignins,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
		<span v-if="note.isHidden" style="opacity: 0.5">%i18n:@private%</span>
 | 
							<span v-if="note.isHidden" style="opacity: 0.5">%i18n:@private%</span>
 | 
				
			||||||
		<span v-if="note.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 | 
							<span v-if="note.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 | 
				
			||||||
		<a class="reply" v-if="note.replyId">%fa:reply%</a>
 | 
							<a class="reply" v-if="note.replyId">%fa:reply%</a>
 | 
				
			||||||
		<misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i"/>
 | 
							<misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i" :customEmojis="note.emojis"/>
 | 
				
			||||||
		<a class="rp" v-if="note.renoteId" :href="`/notes/${note.renoteId}`">RN: ...</a>
 | 
							<a class="rp" v-if="note.renoteId" :href="`/notes/${note.renoteId}`">RN: ...</a>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<details v-if="note.files.length > 0">
 | 
						<details v-if="note.files.length > 0">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,52 +0,0 @@
 | 
				
			|||||||
<template>
 | 
					 | 
				
			||||||
<div class="qldxjjsrseehkusjuoooapmsprvfrxyl mk-admin-card">
 | 
					 | 
				
			||||||
	<header>%i18n:@announcements%</header>
 | 
					 | 
				
			||||||
	<textarea v-model="broadcasts" placeholder='[ { "title": "Title1", "text": "Text1" }, { "title": "Title2", "text": "Text2" } ]'></textarea>
 | 
					 | 
				
			||||||
	<button class="ui" @click="save">%i18n:@save%</button>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from "vue";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			broadcasts: '',
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	created() {
 | 
					 | 
				
			||||||
		(this as any).os.getMeta().then(meta => {
 | 
					 | 
				
			||||||
			this.broadcasts = JSON.stringify(meta.broadcasts, null, '  ');
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		save() {
 | 
					 | 
				
			||||||
			let json;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			try {
 | 
					 | 
				
			||||||
				json = JSON.parse(this.broadcasts);
 | 
					 | 
				
			||||||
			} catch (e) {
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			(this as any).api('admin/update-meta', {
 | 
					 | 
				
			||||||
				broadcasts: json
 | 
					 | 
				
			||||||
			}).then(() => {
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: `Saved` });
 | 
					 | 
				
			||||||
			}.catch(e => {
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
.qldxjjsrseehkusjuoooapmsprvfrxyl
 | 
					 | 
				
			||||||
	textarea
 | 
					 | 
				
			||||||
		width 100%
 | 
					 | 
				
			||||||
		min-height 300px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,137 +0,0 @@
 | 
				
			|||||||
<template>
 | 
					 | 
				
			||||||
<div class="zyknedwtlthezamcjlolyusmipqmjgxz">
 | 
					 | 
				
			||||||
	<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
 | 
					 | 
				
			||||||
		<defs>
 | 
					 | 
				
			||||||
			<linearGradient :id="cpuGradientId" x1="0" x2="0" y1="1" y2="0">
 | 
					 | 
				
			||||||
				<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
 | 
					 | 
				
			||||||
				<stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop>
 | 
					 | 
				
			||||||
			</linearGradient>
 | 
					 | 
				
			||||||
			<mask :id="cpuMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
 | 
					 | 
				
			||||||
				<polygon
 | 
					 | 
				
			||||||
					:points="cpuPolygonPoints"
 | 
					 | 
				
			||||||
					fill="#fff"
 | 
					 | 
				
			||||||
					fill-opacity="0.5"/>
 | 
					 | 
				
			||||||
				<polyline
 | 
					 | 
				
			||||||
					:points="cpuPolylinePoints"
 | 
					 | 
				
			||||||
					fill="none"
 | 
					 | 
				
			||||||
					stroke="#fff"
 | 
					 | 
				
			||||||
					stroke-width="1"/>
 | 
					 | 
				
			||||||
			</mask>
 | 
					 | 
				
			||||||
		</defs>
 | 
					 | 
				
			||||||
		<rect
 | 
					 | 
				
			||||||
			x="0" y="0"
 | 
					 | 
				
			||||||
			:width="viewBoxX" :height="viewBoxY"
 | 
					 | 
				
			||||||
			:style="`stroke: none; fill: url(#${ cpuGradientId }); mask: url(#${ cpuMaskId })`"/>
 | 
					 | 
				
			||||||
		<text x="1" y="12">CPU <tspan>{{ cpuP }}%</tspan></text>
 | 
					 | 
				
			||||||
	</svg>
 | 
					 | 
				
			||||||
	<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
 | 
					 | 
				
			||||||
		<defs>
 | 
					 | 
				
			||||||
			<linearGradient :id="memGradientId" x1="0" x2="0" y1="1" y2="0">
 | 
					 | 
				
			||||||
				<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
 | 
					 | 
				
			||||||
				<stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop>
 | 
					 | 
				
			||||||
			</linearGradient>
 | 
					 | 
				
			||||||
			<mask :id="memMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
 | 
					 | 
				
			||||||
				<polygon
 | 
					 | 
				
			||||||
					:points="memPolygonPoints"
 | 
					 | 
				
			||||||
					fill="#fff"
 | 
					 | 
				
			||||||
					fill-opacity="0.5"/>
 | 
					 | 
				
			||||||
				<polyline
 | 
					 | 
				
			||||||
					:points="memPolylinePoints"
 | 
					 | 
				
			||||||
					fill="none"
 | 
					 | 
				
			||||||
					stroke="#fff"
 | 
					 | 
				
			||||||
					stroke-width="1"/>
 | 
					 | 
				
			||||||
			</mask>
 | 
					 | 
				
			||||||
		</defs>
 | 
					 | 
				
			||||||
		<rect
 | 
					 | 
				
			||||||
			x="0" y="0"
 | 
					 | 
				
			||||||
			:width="viewBoxX" :height="viewBoxY"
 | 
					 | 
				
			||||||
			:style="`stroke: none; fill: url(#${ memGradientId }); mask: url(#${ memMaskId })`"/>
 | 
					 | 
				
			||||||
		<text x="1" y="12">MEM <tspan>{{ memP }}%</tspan></text>
 | 
					 | 
				
			||||||
	</svg>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from 'vue';
 | 
					 | 
				
			||||||
import * as uuid from 'uuid';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	props: ['connection'],
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			viewBoxX: 200,
 | 
					 | 
				
			||||||
			viewBoxY: 70,
 | 
					 | 
				
			||||||
			stats: [],
 | 
					 | 
				
			||||||
			cpuGradientId: uuid(),
 | 
					 | 
				
			||||||
			cpuMaskId: uuid(),
 | 
					 | 
				
			||||||
			memGradientId: uuid(),
 | 
					 | 
				
			||||||
			memMaskId: uuid(),
 | 
					 | 
				
			||||||
			cpuPolylinePoints: '',
 | 
					 | 
				
			||||||
			memPolylinePoints: '',
 | 
					 | 
				
			||||||
			cpuPolygonPoints: '',
 | 
					 | 
				
			||||||
			memPolygonPoints: '',
 | 
					 | 
				
			||||||
			cpuP: '',
 | 
					 | 
				
			||||||
			memP: ''
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	mounted() {
 | 
					 | 
				
			||||||
		this.connection.on('stats', this.onStats);
 | 
					 | 
				
			||||||
		this.connection.on('statsLog', this.onStatsLog);
 | 
					 | 
				
			||||||
		this.connection.send('requestLog', {
 | 
					 | 
				
			||||||
			id: Math.random().toString().substr(2, 8),
 | 
					 | 
				
			||||||
			length: 200
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	beforeDestroy() {
 | 
					 | 
				
			||||||
		this.connection.off('stats', this.onStats);
 | 
					 | 
				
			||||||
		this.connection.off('statsLog', this.onStatsLog);
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		onStats(stats) {
 | 
					 | 
				
			||||||
			this.stats.push(stats);
 | 
					 | 
				
			||||||
			if (this.stats.length > 200) this.stats.shift();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const cpuPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - s.cpu_usage) * this.viewBoxY]);
 | 
					 | 
				
			||||||
			const memPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.mem.used / s.mem.total)) * this.viewBoxY]);
 | 
					 | 
				
			||||||
			this.cpuPolylinePoints = cpuPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
 | 
					 | 
				
			||||||
			this.memPolylinePoints = memPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.cpuPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
 | 
					 | 
				
			||||||
			this.memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.memPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.cpuP = (stats.cpu_usage * 100).toFixed(0);
 | 
					 | 
				
			||||||
			this.memP = (stats.mem.used / stats.mem.total * 100).toFixed(0);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		onStatsLog(statsLog) {
 | 
					 | 
				
			||||||
			statsLog.reverse().forEach(stats => this.onStats(stats));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
.zyknedwtlthezamcjlolyusmipqmjgxz
 | 
					 | 
				
			||||||
	> svg
 | 
					 | 
				
			||||||
		display block
 | 
					 | 
				
			||||||
		width 50%
 | 
					 | 
				
			||||||
		float left
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&:first-child
 | 
					 | 
				
			||||||
			padding-right 5px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		&:last-child
 | 
					 | 
				
			||||||
			padding-left 5px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		> text
 | 
					 | 
				
			||||||
			font-size 10px
 | 
					 | 
				
			||||||
			fill var(--chartCaption)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			> tspan
 | 
					 | 
				
			||||||
				opacity 0.5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	&:after
 | 
					 | 
				
			||||||
		content ""
 | 
					 | 
				
			||||||
		display block
 | 
					 | 
				
			||||||
		clear both
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,135 +0,0 @@
 | 
				
			|||||||
<template>
 | 
					 | 
				
			||||||
<div class="obdskegsannmntldydackcpzezagxqfy mk-admin-card">
 | 
					 | 
				
			||||||
	<header>%i18n:@dashboard%</header>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<div v-if="stats" class="stats">
 | 
					 | 
				
			||||||
		<div><b>%fa:user% {{ stats.originalUsersCount | number }}</b><span>%i18n:@original-users%</span></div>
 | 
					 | 
				
			||||||
		<div><span>%fa:user% {{ stats.usersCount | number }}</span><span>%i18n:@all-users%</span></div>
 | 
					 | 
				
			||||||
		<div><b>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</b><span>%i18n:@original-notes%</span></div>
 | 
					 | 
				
			||||||
		<div><span>%fa:pencil-alt% {{ stats.notesCount | number }}</span><span>%i18n:@all-notes%</span></div>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<div class="cpu-memory">
 | 
					 | 
				
			||||||
		<x-cpu-memory :connection="connection"/>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<div v-if="this.$store.state.i && this.$store.state.i.isAdmin" class="form">
 | 
					 | 
				
			||||||
		<div>
 | 
					 | 
				
			||||||
			<label>
 | 
					 | 
				
			||||||
				<p>%i18n:@banner-url%</p>
 | 
					 | 
				
			||||||
				<input v-model="bannerUrl">
 | 
					 | 
				
			||||||
			</label>
 | 
					 | 
				
			||||||
			<button class="ui" @click="updateMeta">%i18n:@save%</button>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		<div>
 | 
					 | 
				
			||||||
			<label>
 | 
					 | 
				
			||||||
				<input type="checkbox" v-model="disableRegistration" @change="updateMeta">
 | 
					 | 
				
			||||||
				<span>%i18n:@disableRegistration%</span>
 | 
					 | 
				
			||||||
			</label>
 | 
					 | 
				
			||||||
			<button class="ui" @click="invite">%i18n:@invite%</button>
 | 
					 | 
				
			||||||
			<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		<div>
 | 
					 | 
				
			||||||
			<label>
 | 
					 | 
				
			||||||
				<input type="checkbox" v-model="disableLocalTimeline" @change="updateMeta">
 | 
					 | 
				
			||||||
				<span>%i18n:@disableLocalTimeline%</span>
 | 
					 | 
				
			||||||
			</label>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from "vue";
 | 
					 | 
				
			||||||
import XCpuMemory from "./admin.cpu-memory.vue";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	components: {
 | 
					 | 
				
			||||||
		XCpuMemory
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			stats: null,
 | 
					 | 
				
			||||||
			disableRegistration: false,
 | 
					 | 
				
			||||||
			disableLocalTimeline: false,
 | 
					 | 
				
			||||||
			bannerUrl: null,
 | 
					 | 
				
			||||||
			inviteCode: null,
 | 
					 | 
				
			||||||
			connection: null
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	created() {
 | 
					 | 
				
			||||||
		this.connection = (this as any).os.stream.useSharedConnection('serverStats');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		(this as any).os.getMeta().then(meta => {
 | 
					 | 
				
			||||||
			this.disableRegistration = meta.disableRegistration;
 | 
					 | 
				
			||||||
			this.disableLocalTimeline = meta.disableLocalTimeline;
 | 
					 | 
				
			||||||
			this.bannerUrl = meta.bannerUrl;
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		(this as any).api('stats').then(stats => {
 | 
					 | 
				
			||||||
			this.stats = stats;
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	beforeDestroy() {
 | 
					 | 
				
			||||||
		this.connection.dispose();
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		invite() {
 | 
					 | 
				
			||||||
			(this as any).api('admin/invite').then(x => {
 | 
					 | 
				
			||||||
				this.inviteCode = x.code;
 | 
					 | 
				
			||||||
			}).catch(e => {
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		updateMeta() {
 | 
					 | 
				
			||||||
			(this as any).api('admin/update-meta', {
 | 
					 | 
				
			||||||
				disableRegistration: this.disableRegistration,
 | 
					 | 
				
			||||||
				disableLocalTimeline: this.disableLocalTimeline,
 | 
					 | 
				
			||||||
				bannerUrl: this.bannerUrl
 | 
					 | 
				
			||||||
			}).then(() => {
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: `Saved` });
 | 
					 | 
				
			||||||
			}).catch(e => {
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.obdskegsannmntldydackcpzezagxqfy
 | 
					 | 
				
			||||||
	> .stats
 | 
					 | 
				
			||||||
		display flex
 | 
					 | 
				
			||||||
		justify-content center
 | 
					 | 
				
			||||||
		margin-bottom 16px
 | 
					 | 
				
			||||||
		padding 16px
 | 
					 | 
				
			||||||
		border solid 1px #eee
 | 
					 | 
				
			||||||
		border-radius 8px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		> div
 | 
					 | 
				
			||||||
			flex 1
 | 
					 | 
				
			||||||
			text-align center
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			> *:first-child
 | 
					 | 
				
			||||||
				display block
 | 
					 | 
				
			||||||
				color var(--primary)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			> *:last-child
 | 
					 | 
				
			||||||
				font-size 70%
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> .cpu-memory
 | 
					 | 
				
			||||||
		margin-bottom 16px
 | 
					 | 
				
			||||||
		padding 16px
 | 
					 | 
				
			||||||
		border solid 1px #eee
 | 
					 | 
				
			||||||
		border-radius: 8px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> .form
 | 
					 | 
				
			||||||
		> div
 | 
					 | 
				
			||||||
			padding 16px
 | 
					 | 
				
			||||||
			border-bottom solid 1px #eee
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,57 +0,0 @@
 | 
				
			|||||||
<template>
 | 
					 | 
				
			||||||
<div class="mk-admin-card">
 | 
					 | 
				
			||||||
	<header>%i18n:@suspend-user%</header>
 | 
					 | 
				
			||||||
	<input v-model="username" type="text" class="ui"/>
 | 
					 | 
				
			||||||
	<button class="ui" @click="suspendUser" :disabled="suspending">%i18n:@suspend%</button>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from "vue";
 | 
					 | 
				
			||||||
import parseAcct from "../../../../../../misc/acct/parse";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			username: null,
 | 
					 | 
				
			||||||
			suspending: false
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		async suspendUser() {
 | 
					 | 
				
			||||||
			this.suspending = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const process = async () => {
 | 
					 | 
				
			||||||
				const user = await (this as any).os.api(
 | 
					 | 
				
			||||||
					"users/show",
 | 
					 | 
				
			||||||
					parseAcct(this.username)
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				await (this as any).os.api("admin/suspend-user", {
 | 
					 | 
				
			||||||
					userId: user.id
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: "%i18n:@suspended%" });
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			await process().catch(e => {
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.suspending = false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
header
 | 
					 | 
				
			||||||
	margin 10px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
button
 | 
					 | 
				
			||||||
	margin 16px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,58 +0,0 @@
 | 
				
			|||||||
<template>
 | 
					 | 
				
			||||||
<div class="mk-admin-card">
 | 
					 | 
				
			||||||
	<header>%i18n:@unsuspend-user%</header>
 | 
					 | 
				
			||||||
	<input v-model="username" type="text" class="ui"/>
 | 
					 | 
				
			||||||
	<button class="ui" @click="unsuspendUser" :disabled="unsuspending">%i18n:@unsuspend%</button>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from "vue";
 | 
					 | 
				
			||||||
import parseAcct from "../../../../../../misc/acct/parse";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			username: null,
 | 
					 | 
				
			||||||
			unsuspending: false
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		async unsuspendUser() {
 | 
					 | 
				
			||||||
			this.unsuspending = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const process = async () => {
 | 
					 | 
				
			||||||
				const user = await (this as any).os.api(
 | 
					 | 
				
			||||||
					"users/show",
 | 
					 | 
				
			||||||
					parseAcct(this.username)
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				await (this as any).os.api("admin/unsuspend-user", {
 | 
					 | 
				
			||||||
					userId: user.id
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: "%i18n:@unsuspended%" });
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			await process().catch(e => {
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.unsuspending = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
header
 | 
					 | 
				
			||||||
	margin 10px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
button
 | 
					 | 
				
			||||||
	margin 16px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,57 +0,0 @@
 | 
				
			|||||||
<template>
 | 
					 | 
				
			||||||
<div class="mk-admin-card">
 | 
					 | 
				
			||||||
	<header>%i18n:@unverify-user%</header>
 | 
					 | 
				
			||||||
	<input v-model="username" type="text" class="ui"/>
 | 
					 | 
				
			||||||
	<button class="ui" @click="unverifyUser" :disabled="unverifying">%i18n:@unverify%</button>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from "vue";
 | 
					 | 
				
			||||||
import parseAcct from "../../../../../../misc/acct/parse";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			username: null,
 | 
					 | 
				
			||||||
			unverifying: false
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		async unverifyUser() {
 | 
					 | 
				
			||||||
			this.unverifying = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const process = async () => {
 | 
					 | 
				
			||||||
				const user = await (this as any).os.api(
 | 
					 | 
				
			||||||
					"users/show",
 | 
					 | 
				
			||||||
					parseAcct(this.username)
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				await (this as any).os.api("admin/unverify-user", {
 | 
					 | 
				
			||||||
					userId: user.id
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: "%i18n:@unverified%" });
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			await process().catch(e => {
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.unverifying = false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
header
 | 
					 | 
				
			||||||
	margin 10px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
button
 | 
					 | 
				
			||||||
	margin 16px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,57 +0,0 @@
 | 
				
			|||||||
<template>
 | 
					 | 
				
			||||||
<div class="mk-admin-card">
 | 
					 | 
				
			||||||
	<header>%i18n:@verify-user%</header>
 | 
					 | 
				
			||||||
	<input v-model="username" type="text" class="ui"/>
 | 
					 | 
				
			||||||
	<button class="ui" @click="verifyUser" :disabled="verifying">%i18n:@verify%</button>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from "vue";
 | 
					 | 
				
			||||||
import parseAcct from "../../../../../../misc/acct/parse";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			username: null,
 | 
					 | 
				
			||||||
			verifying: false
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		async verifyUser() {
 | 
					 | 
				
			||||||
			this.verifying = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const process = async () => {
 | 
					 | 
				
			||||||
				const user = await (this as any).os.api(
 | 
					 | 
				
			||||||
					"users/show",
 | 
					 | 
				
			||||||
					parseAcct(this.username)
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				await (this as any).os.api("admin/verify-user", {
 | 
					 | 
				
			||||||
					userId: user.id
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: "%i18n:@verified%" });
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			await process().catch(e => {
 | 
					 | 
				
			||||||
				(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.verifying = false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
header
 | 
					 | 
				
			||||||
	margin 10px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
button
 | 
					 | 
				
			||||||
	margin 16px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -1,140 +0,0 @@
 | 
				
			|||||||
<template>
 | 
					 | 
				
			||||||
<div class="mk-admin">
 | 
					 | 
				
			||||||
	<nav>
 | 
					 | 
				
			||||||
		<ul>
 | 
					 | 
				
			||||||
			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%fa:chalkboard .fw%%i18n:@dashboard%</li>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<li v-if="this.$store.state.i && this.$store.state.i.isAdmin"
 | 
					 | 
				
			||||||
				@click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<li v-if="this.$store.state.i && this.$store.state.i.isAdmin"
 | 
					 | 
				
			||||||
				@click="nav('announcements')" :class="{ active: page == 'announcements' }">%fa:broadcast-tower .fw%%i18n:@announcements%</li>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<li v-if="this.$store.state.i && this.$store.state.i.isAdmin"
 | 
					 | 
				
			||||||
				@click="nav('hashtags')" :class="{ active: page == 'hashtags' }">%fa:hashtag .fw%%i18n:@hashtags%</li>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:common.drive%</li> -->
 | 
					 | 
				
			||||||
			<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> -->
 | 
					 | 
				
			||||||
		</ul>
 | 
					 | 
				
			||||||
	</nav>
 | 
					 | 
				
			||||||
	<main>
 | 
					 | 
				
			||||||
		<div v-show="page == 'dashboard'">
 | 
					 | 
				
			||||||
			<x-dashboard/>
 | 
					 | 
				
			||||||
			<x-charts/>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
		<div v-show="page == 'announcements'">
 | 
					 | 
				
			||||||
			<x-announcements/>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
		<div v-show="page == 'hashtags'">
 | 
					 | 
				
			||||||
			<x-hashtags/>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
		<div v-if="page == 'users'">
 | 
					 | 
				
			||||||
			<x-suspend-user/>
 | 
					 | 
				
			||||||
			<x-unsuspend-user/>
 | 
					 | 
				
			||||||
			<x-verify-user/>
 | 
					 | 
				
			||||||
			<x-unverify-user/>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
		<div v-if="page == 'drive'"></div>
 | 
					 | 
				
			||||||
		<div v-if="page == 'update'"></div>
 | 
					 | 
				
			||||||
	</main>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from "vue";
 | 
					 | 
				
			||||||
import XDashboard from "./admin.dashboard.vue";
 | 
					 | 
				
			||||||
import XAnnouncements from "./admin.announcements.vue";
 | 
					 | 
				
			||||||
import XHashtags from "./admin.hashtags.vue";
 | 
					 | 
				
			||||||
import XSuspendUser from "./admin.suspend-user.vue";
 | 
					 | 
				
			||||||
import XUnsuspendUser from "./admin.unsuspend-user.vue";
 | 
					 | 
				
			||||||
import XVerifyUser from "./admin.verify-user.vue";
 | 
					 | 
				
			||||||
import XUnverifyUser from "./admin.unverify-user.vue";
 | 
					 | 
				
			||||||
import XCharts from "../../components/charts.vue";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	components: {
 | 
					 | 
				
			||||||
		XDashboard,
 | 
					 | 
				
			||||||
		XAnnouncements,
 | 
					 | 
				
			||||||
		XHashtags,
 | 
					 | 
				
			||||||
		XSuspendUser,
 | 
					 | 
				
			||||||
		XUnsuspendUser,
 | 
					 | 
				
			||||||
		XVerifyUser,
 | 
					 | 
				
			||||||
		XUnverifyUser,
 | 
					 | 
				
			||||||
		XCharts
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			page: 'dashboard'
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		nav(page: string) {
 | 
					 | 
				
			||||||
			this.page = page;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.mk-admin
 | 
					 | 
				
			||||||
	display flex
 | 
					 | 
				
			||||||
	height 100%
 | 
					 | 
				
			||||||
	margin 32px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> nav
 | 
					 | 
				
			||||||
		flex 0 0 250px
 | 
					 | 
				
			||||||
		width 100%
 | 
					 | 
				
			||||||
		height 100%
 | 
					 | 
				
			||||||
		padding 16px 0 0 0
 | 
					 | 
				
			||||||
		overflow auto
 | 
					 | 
				
			||||||
		border-right solid 1px #ddd
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		> ul
 | 
					 | 
				
			||||||
			list-style none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			> li
 | 
					 | 
				
			||||||
				display block
 | 
					 | 
				
			||||||
				padding 10px 16px
 | 
					 | 
				
			||||||
				margin 0
 | 
					 | 
				
			||||||
				color #666
 | 
					 | 
				
			||||||
				cursor pointer
 | 
					 | 
				
			||||||
				user-select none
 | 
					 | 
				
			||||||
				transition margin-left 0.2s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> [data-fa]
 | 
					 | 
				
			||||||
					margin-right 4px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				&:hover
 | 
					 | 
				
			||||||
					color #555
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				&.active
 | 
					 | 
				
			||||||
					margin-left 8px
 | 
					 | 
				
			||||||
					color var(--primary) !important
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> main
 | 
					 | 
				
			||||||
		width 100%
 | 
					 | 
				
			||||||
		padding 16px 32px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		> div
 | 
					 | 
				
			||||||
			> div
 | 
					 | 
				
			||||||
				max-width 800px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.mk-admin-card
 | 
					 | 
				
			||||||
	padding 32px
 | 
					 | 
				
			||||||
	background #fff
 | 
					 | 
				
			||||||
	box-shadow 0 2px 8px rgba(#000, 0.1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	&:not(:last-child)
 | 
					 | 
				
			||||||
		margin-bottom 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> header
 | 
					 | 
				
			||||||
		margin 0 0 1em 0
 | 
					 | 
				
			||||||
		padding 0 0 8px 0
 | 
					 | 
				
			||||||
		font-size 1em
 | 
					 | 
				
			||||||
		color #555
 | 
					 | 
				
			||||||
		border-bottom solid 1px #eee
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
@@ -14,7 +14,7 @@ import VueHotkey from './common/hotkey';
 | 
				
			|||||||
import App from './app.vue';
 | 
					import App from './app.vue';
 | 
				
			||||||
import checkForUpdate from './common/scripts/check-for-update';
 | 
					import checkForUpdate from './common/scripts/check-for-update';
 | 
				
			||||||
import MiOS, { API } from './mios';
 | 
					import MiOS, { API } from './mios';
 | 
				
			||||||
import { version, codename, lang } from './config';
 | 
					import { clientVersion as version, codename, lang } from './config';
 | 
				
			||||||
import { builtinThemes, lightTheme, applyTheme } from './theme';
 | 
					import { builtinThemes, lightTheme, applyTheme } from './theme';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (localStorage.getItem('theme') == null) {
 | 
					if (localStorage.getItem('theme') == null) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import { EventEmitter } from 'eventemitter3';
 | 
				
			|||||||
import * as uuid from 'uuid';
 | 
					import * as uuid from 'uuid';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import initStore from './store';
 | 
					import initStore from './store';
 | 
				
			||||||
import { apiUrl, version, lang } from './config';
 | 
					import { apiUrl, clientVersion as version, lang } from './config';
 | 
				
			||||||
import Progress from './common/scripts/loading';
 | 
					import Progress from './common/scripts/loading';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Err from './common/views/components/connect-failed.vue';
 | 
					import Err from './common/views/components/connect-failed.vue';
 | 
				
			||||||
@@ -537,7 +537,9 @@ export default class MiOS extends EventEmitter {
 | 
				
			|||||||
			// forceが有効, meta情報を保持していない or 期限切れ
 | 
								// forceが有効, meta情報を保持していない or 期限切れ
 | 
				
			||||||
			if (force || this.meta == null || Date.now() - this.meta.chachedAt.getTime() > expire) {
 | 
								if (force || this.meta == null || Date.now() - this.meta.chachedAt.getTime() > expire) {
 | 
				
			||||||
				this.isMetaFetching = true;
 | 
									this.isMetaFetching = true;
 | 
				
			||||||
				const meta = await this.api('meta');
 | 
									const meta = await this.api('meta', {
 | 
				
			||||||
 | 
										detail: false
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
				this.meta = {
 | 
									this.meta = {
 | 
				
			||||||
					data: meta,
 | 
										data: meta,
 | 
				
			||||||
					chachedAt: new Date()
 | 
										chachedAt: new Date()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,13 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="mk-activity">
 | 
					<div class="mk-activity">
 | 
				
			||||||
	<svg v-if="data" ref="canvas" viewBox="0 0 30 1" preserveAspectRatio="none">
 | 
						<div ref="chart"></div>
 | 
				
			||||||
		<g v-for="(d, i) in data">
 | 
					 | 
				
			||||||
			<rect width="0.8" :height="d.notesH"
 | 
					 | 
				
			||||||
				:x="i + 0.1" :y="1 - d.notesH - d.repliesH - d.renotesH"
 | 
					 | 
				
			||||||
				fill="#41ddde"/>
 | 
					 | 
				
			||||||
			<rect width="0.8" :height="d.repliesH"
 | 
					 | 
				
			||||||
				:x="i + 0.1" :y="1 - d.repliesH - d.renotesH"
 | 
					 | 
				
			||||||
				fill="#f7796c"/>
 | 
					 | 
				
			||||||
			<rect width="0.8" :height="d.renotesH"
 | 
					 | 
				
			||||||
				:x="i + 0.1" :y="1 - d.renotesH"
 | 
					 | 
				
			||||||
				fill="#a1de41"/>
 | 
					 | 
				
			||||||
			</g>
 | 
					 | 
				
			||||||
	</svg>
 | 
					 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import * as ApexCharts from 'apexcharts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	props: ['user'],
 | 
						props: ['user'],
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
@@ -28,19 +18,84 @@ export default Vue.extend({
 | 
				
			|||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		(this as any).api('aggregation/users/activity', {
 | 
							(this as any).api('charts/user/notes', {
 | 
				
			||||||
			userId: this.user.id,
 | 
								userId: this.user.id,
 | 
				
			||||||
			limit: 30
 | 
								span: 'day',
 | 
				
			||||||
		}).then(data => {
 | 
								limit: 21
 | 
				
			||||||
			data.forEach(d => d.total = d.notes + d.replies + d.renotes);
 | 
							}).then(stats => {
 | 
				
			||||||
			this.peak = Math.max.apply(null, data.map(d => d.total));
 | 
								const normal = [];
 | 
				
			||||||
			data.forEach(d => {
 | 
								const reply = [];
 | 
				
			||||||
				d.notesH = d.notes / this.peak;
 | 
								const renote = [];
 | 
				
			||||||
				d.repliesH = d.replies / this.peak;
 | 
					
 | 
				
			||||||
				d.renotesH = d.renotes / this.peak;
 | 
								const now = new Date();
 | 
				
			||||||
 | 
								const y = now.getFullYear();
 | 
				
			||||||
 | 
								const m = now.getMonth();
 | 
				
			||||||
 | 
								const d = now.getDate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (let i = 0; i < 21; i++) {
 | 
				
			||||||
 | 
									const x = new Date(y, m, d - i);
 | 
				
			||||||
 | 
									normal.push([
 | 
				
			||||||
 | 
										x,
 | 
				
			||||||
 | 
										stats.diffs.normal[i]
 | 
				
			||||||
 | 
									]);
 | 
				
			||||||
 | 
									reply.push([
 | 
				
			||||||
 | 
										x,
 | 
				
			||||||
 | 
										stats.diffs.reply[i]
 | 
				
			||||||
 | 
									]);
 | 
				
			||||||
 | 
									renote.push([
 | 
				
			||||||
 | 
										x,
 | 
				
			||||||
 | 
										stats.diffs.renote[i]
 | 
				
			||||||
 | 
									]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const chart = new ApexCharts(this.$refs.chart, {
 | 
				
			||||||
 | 
									chart: {
 | 
				
			||||||
 | 
										type: 'bar',
 | 
				
			||||||
 | 
										stacked: true,
 | 
				
			||||||
 | 
										height: 100,
 | 
				
			||||||
 | 
										sparkline: {
 | 
				
			||||||
 | 
											enabled: true
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									plotOptions: {
 | 
				
			||||||
 | 
										bar: {
 | 
				
			||||||
 | 
											columnWidth: '90%',
 | 
				
			||||||
 | 
											endingShape: 'rounded'
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									grid: {
 | 
				
			||||||
 | 
										clipMarkers: false,
 | 
				
			||||||
 | 
										padding: {
 | 
				
			||||||
 | 
											top: 0,
 | 
				
			||||||
 | 
											right: 8,
 | 
				
			||||||
 | 
											bottom: 0,
 | 
				
			||||||
 | 
											left: 8
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									tooltip: {
 | 
				
			||||||
 | 
										shared: true,
 | 
				
			||||||
 | 
										intersect: false
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									series: [{
 | 
				
			||||||
 | 
										name: 'Normal',
 | 
				
			||||||
 | 
										data: normal
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Reply',
 | 
				
			||||||
 | 
										data: reply
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										name: 'Renote',
 | 
				
			||||||
 | 
										data: renote
 | 
				
			||||||
 | 
									}],
 | 
				
			||||||
 | 
									xaxis: {
 | 
				
			||||||
 | 
										type: 'datetime',
 | 
				
			||||||
 | 
										crosshairs: {
 | 
				
			||||||
 | 
											width: 1,
 | 
				
			||||||
 | 
											opacity: 1
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
			data.reverse();
 | 
					
 | 
				
			||||||
			this.data = data;
 | 
								chart.render();
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@@ -51,12 +106,4 @@ export default Vue.extend({
 | 
				
			|||||||
	max-width 600px
 | 
						max-width 600px
 | 
				
			||||||
	margin 0 auto
 | 
						margin 0 auto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> svg
 | 
					 | 
				
			||||||
		display block
 | 
					 | 
				
			||||||
		width 100%
 | 
					 | 
				
			||||||
		height 80px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		> rect
 | 
					 | 
				
			||||||
			transform-origin center
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,7 @@
 | 
				
			|||||||
				<div class="text">
 | 
									<div class="text">
 | 
				
			||||||
					<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
 | 
										<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
 | 
				
			||||||
					<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
 | 
										<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
 | 
				
			||||||
					<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
 | 
										<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :customEmojis="p.emojis"/>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
				<div class="files" v-if="p.files.length > 0">
 | 
									<div class="files" v-if="p.files.length > 0">
 | 
				
			||||||
					<mk-media-list :media-list="p.files" :raw="true"/>
 | 
										<mk-media-list :media-list="p.files" :raw="true"/>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,7 +30,7 @@
 | 
				
			|||||||
					<div class="text">
 | 
										<div class="text">
 | 
				
			||||||
						<span v-if="appearNote.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
 | 
											<span v-if="appearNote.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
 | 
				
			||||||
						<a class="reply" v-if="appearNote.reply">%fa:reply%</a>
 | 
											<a class="reply" v-if="appearNote.reply">%fa:reply%</a>
 | 
				
			||||||
						<misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text"/>
 | 
											<misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text" :customEmojis="appearNote.emojis"/>
 | 
				
			||||||
						<a class="rp" v-if="appearNote.renote != null">RN:</a>
 | 
											<a class="rp" v-if="appearNote.renote != null">RN:</a>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
					<div class="files" v-if="appearNote.files.length > 0">
 | 
										<div class="files" v-if="appearNote.files.length > 0">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
		<span v-if="note.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
 | 
							<span v-if="note.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
 | 
				
			||||||
		<span v-if="note.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
 | 
							<span v-if="note.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
 | 
				
			||||||
		<a class="reply" v-if="note.replyId">%fa:reply%</a>
 | 
							<a class="reply" v-if="note.replyId">%fa:reply%</a>
 | 
				
			||||||
		<misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i"/>
 | 
							<misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i" :customEmojis="note.emojis"/>
 | 
				
			||||||
		<a class="rp" v-if="note.renoteId">RN: ...</a>
 | 
							<a class="rp" v-if="note.renoteId">RN: ...</a>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<details v-if="note.files.length > 0">
 | 
						<details v-if="note.files.length > 0">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -127,6 +127,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			<mk-api-settings />
 | 
								<mk-api-settings />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<ui-card>
 | 
				
			||||||
 | 
									<div slot="title">%fa:unlock-alt% %i18n:@password%</div>
 | 
				
			||||||
 | 
									<section>
 | 
				
			||||||
 | 
										<mk-password-settings/>
 | 
				
			||||||
 | 
									</section>
 | 
				
			||||||
 | 
								</ui-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<ui-card>
 | 
								<ui-card>
 | 
				
			||||||
				<div slot="title">%fa:sync-alt% %i18n:@update%</div>
 | 
									<div slot="title">%fa:sync-alt% %i18n:@update%</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -154,7 +161,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import { apiUrl, version, codename, langs } from '../../../config';
 | 
					import { apiUrl, clientVersion as version, codename, langs } from '../../../config';
 | 
				
			||||||
import checkForUpdate from '../../../common/scripts/check-for-update';
 | 
					import checkForUpdate from '../../../common/scripts/check-for-update';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,9 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementBig = {
 | 
					export type TextElementBig = {
 | 
				
			||||||
	type: 'big'
 | 
						type: 'big';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	big: string
 | 
						big: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,9 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementBold = {
 | 
					export type TextElementBold = {
 | 
				
			||||||
	type: 'bold'
 | 
						type: 'bold';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	bold: string
 | 
						bold: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,10 +5,10 @@
 | 
				
			|||||||
import genHtml from '../core/syntax-highlighter';
 | 
					import genHtml from '../core/syntax-highlighter';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementCode = {
 | 
					export type TextElementCode = {
 | 
				
			||||||
	type: 'code'
 | 
						type: 'code';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	code: string
 | 
						code: string;
 | 
				
			||||||
	html: string
 | 
						html: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,13 +3,13 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementEmoji = {
 | 
					export type TextElementEmoji = {
 | 
				
			||||||
	type: 'emoji'
 | 
						type: 'emoji';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	emoji: string
 | 
						emoji: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string) {
 | 
				
			||||||
	const match = text.match(/^:([a-zA-Z0-9+-_]+):/);
 | 
						const match = text.match(/^:([a-zA-Z0-9+-_]+?):/);
 | 
				
			||||||
	if (!match) return null;
 | 
						if (!match) return null;
 | 
				
			||||||
	const emoji = match[0];
 | 
						const emoji = match[0];
 | 
				
			||||||
	return {
 | 
						return {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,13 +3,13 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementHashtag = {
 | 
					export type TextElementHashtag = {
 | 
				
			||||||
	type: 'hashtag'
 | 
						type: 'hashtag';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	hashtag: string
 | 
						hashtag: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string, i: number) {
 | 
					export default function(text: string, isBegin: boolean) {
 | 
				
			||||||
	if (!(/^\s#[^\s\.,!\?#]+/.test(text) || (i == 0 && /^#[^\s\.,!\?#]+/.test(text)))) return null;
 | 
						if (!(/^\s#[^\s\.,!\?#]+/.test(text) || (isBegin && /^#[^\s\.,!\?#]+/.test(text)))) return null;
 | 
				
			||||||
	const isHead = text.startsWith('#');
 | 
						const isHead = text.startsWith('#');
 | 
				
			||||||
	const hashtag = text.match(/^\s?#[^\s\.,!\?#]+/)[0];
 | 
						const hashtag = text.match(/^\s?#[^\s\.,!\?#]+/)[0];
 | 
				
			||||||
	const res: any[] = !isHead ? [{
 | 
						const res: any[] = !isHead ? [{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,10 +5,10 @@
 | 
				
			|||||||
import genHtml from '../core/syntax-highlighter';
 | 
					import genHtml from '../core/syntax-highlighter';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementInlineCode = {
 | 
					export type TextElementInlineCode = {
 | 
				
			||||||
	type: 'inline-code'
 | 
						type: 'inline-code';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	code: string
 | 
						code: string;
 | 
				
			||||||
	html: string
 | 
						html: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,11 +3,11 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementLink = {
 | 
					export type TextElementLink = {
 | 
				
			||||||
	type: 'link'
 | 
						type: 'link';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	title: string
 | 
						title: string;
 | 
				
			||||||
	url: string
 | 
						url: string;
 | 
				
			||||||
	silent: boolean
 | 
						silent: boolean;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,11 +5,11 @@ import parseAcct from '../../../misc/acct/parse';
 | 
				
			|||||||
import { toUnicode } from 'punycode';
 | 
					import { toUnicode } from 'punycode';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementMention = {
 | 
					export type TextElementMention = {
 | 
				
			||||||
	type: 'mention'
 | 
						type: 'mention';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	canonical: string
 | 
						canonical: string;
 | 
				
			||||||
	username: string
 | 
						username: string;
 | 
				
			||||||
	host: string
 | 
						host: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,9 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementMotion = {
 | 
					export type TextElementMotion = {
 | 
				
			||||||
	type: 'motion'
 | 
						type: 'motion';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	motion: string
 | 
						motion: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,14 +3,14 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementQuote = {
 | 
					export type TextElementQuote = {
 | 
				
			||||||
	type: 'quote'
 | 
						type: 'quote';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	quote: string
 | 
						quote: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string, index: number) {
 | 
					export default function(text: string, isBegin: boolean) {
 | 
				
			||||||
	const match = text.match(/^"([\s\S]+?)\n"/) || text.match(/^\n>([\s\S]+?)(\n\n|$)/) ||
 | 
						const match = text.match(/^"([\s\S]+?)\n"/) || text.match(/^\n>([\s\S]+?)(\n\n|$)/) ||
 | 
				
			||||||
		(index == 0 ? text.match(/^>([\s\S]+?)(\n\n|$)/) : null);
 | 
							(isBegin ? text.match(/^>([\s\S]+?)(\n\n|$)/) : null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!match) return null;
 | 
						if (!match) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,9 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementSearch = {
 | 
					export type TextElementSearch = {
 | 
				
			||||||
	type: 'search'
 | 
						type: 'search';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	query: string
 | 
						query: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,13 +3,13 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementTitle = {
 | 
					export type TextElementTitle = {
 | 
				
			||||||
	type: 'title'
 | 
						type: 'title';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	title: string
 | 
						title: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string, isBegin: boolean) {
 | 
				
			||||||
	const match = text.match(/^(【|\[)(.+?)(】|])\n/);
 | 
						const match = isBegin ? text.match(/^(【|\[)(.+?)(】|])\n/) : text.match(/^\n(【|\[)(.+?)(】|])\n/);
 | 
				
			||||||
	if (!match) return null;
 | 
						if (!match) return null;
 | 
				
			||||||
	const title = match[0];
 | 
						const title = match[0];
 | 
				
			||||||
	return {
 | 
						return {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,9 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TextElementUrl = {
 | 
					export type TextElementUrl = {
 | 
				
			||||||
	type: 'url'
 | 
						type: 'url';
 | 
				
			||||||
	content: string
 | 
						content: string;
 | 
				
			||||||
	url: string
 | 
						url: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(text: string) {
 | 
					export default function(text: string) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,7 +46,7 @@ export type TextElement = { type: 'text', content: string }
 | 
				
			|||||||
	| TextElementTitle
 | 
						| TextElementTitle
 | 
				
			||||||
	| TextElementUrl
 | 
						| TextElementUrl
 | 
				
			||||||
	| TextElementMotion;
 | 
						| TextElementMotion;
 | 
				
			||||||
export type TextElementProcessor = (text: string, i: number) => TextElement | TextElement[];
 | 
					export type TextElementProcessor = (text: string, isBegin: boolean) => TextElement | TextElement[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default (source: string): TextElement[] => {
 | 
					export default (source: string): TextElement[] => {
 | 
				
			||||||
	if (source == null || source == '') {
 | 
						if (source == null || source == '') {
 | 
				
			||||||
@@ -67,7 +67,7 @@ export default (source: string): TextElement[] => {
 | 
				
			|||||||
	// パース
 | 
						// パース
 | 
				
			||||||
	while (source != '') {
 | 
						while (source != '') {
 | 
				
			||||||
		const parsed = elements.some(el => {
 | 
							const parsed = elements.some(el => {
 | 
				
			||||||
			let _tokens = el(source, i);
 | 
								let _tokens = el(source, i == 0);
 | 
				
			||||||
			if (_tokens) {
 | 
								if (_tokens) {
 | 
				
			||||||
				if (!Array.isArray(_tokens)) {
 | 
									if (!Array.isArray(_tokens)) {
 | 
				
			||||||
					_tokens = [_tokens];
 | 
										_tokens = [_tokens];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,23 +4,31 @@ import isObjectId from './is-objectid';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const isAnId = (x: any) => mongo.ObjectID.isValid(x);
 | 
					export const isAnId = (x: any) => mongo.ObjectID.isValid(x);
 | 
				
			||||||
export const isNotAnId = (x: any) => !isAnId(x);
 | 
					export const isNotAnId = (x: any) => !isAnId(x);
 | 
				
			||||||
 | 
					export const transform = (x: string | mongo.ObjectID): mongo.ObjectID => {
 | 
				
			||||||
 | 
						if (x == null) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (isAnId(x) && !isObjectId(x)) {
 | 
				
			||||||
 | 
							return new mongo.ObjectID(x);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return x as mongo.ObjectID;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export const transformMany = (xs: (string | mongo.ObjectID)[]): mongo.ObjectID[] => {
 | 
				
			||||||
 | 
						if (xs == null) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return xs.map(x => transform(x));
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ObjectId = mongo.ObjectID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * ID
 | 
					 * ID
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default class ID extends Context<mongo.ObjectID> {
 | 
					export default class ID extends Context<string> {
 | 
				
			||||||
	constructor() {
 | 
						constructor() {
 | 
				
			||||||
		super();
 | 
							super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.transform = v => {
 | 
							this.push((v: any) => {
 | 
				
			||||||
			if (isAnId(v) && !isObjectId(v)) {
 | 
					 | 
				
			||||||
				return new mongo.ObjectID(v);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				return v;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.push(v => {
 | 
					 | 
				
			||||||
			if (!isObjectId(v) && isNotAnId(v)) {
 | 
								if (!isObjectId(v) && isNotAnId(v)) {
 | 
				
			||||||
				return new Error('must-be-an-id');
 | 
									return new Error('must-be-an-id');
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								src/models/emoji.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/models/emoji.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					import * as mongo from 'mongodb';
 | 
				
			||||||
 | 
					import db from '../db/mongodb';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Emoji = db.get<IEmoji>('emoji');
 | 
				
			||||||
 | 
					Emoji.createIndex('name');
 | 
				
			||||||
 | 
					Emoji.createIndex('host');
 | 
				
			||||||
 | 
					Emoji.createIndex(['name', 'host'], { unique: true });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Emoji;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type IEmoji = {
 | 
				
			||||||
 | 
						_id: mongo.ObjectID;
 | 
				
			||||||
 | 
						name: string;
 | 
				
			||||||
 | 
						host: string;
 | 
				
			||||||
 | 
						url: string;
 | 
				
			||||||
 | 
						aliases?: string[];
 | 
				
			||||||
 | 
						updatedAt?: Date;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -15,24 +15,4 @@ export type IMeta = {
 | 
				
			|||||||
	disableLocalTimeline?: boolean;
 | 
						disableLocalTimeline?: boolean;
 | 
				
			||||||
	hidedTags?: string[];
 | 
						hidedTags?: string[];
 | 
				
			||||||
	bannerUrl?: string;
 | 
						bannerUrl?: string;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * カスタム絵文字定義
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	emojis?: {
 | 
					 | 
				
			||||||
		/**
 | 
					 | 
				
			||||||
		 * 絵文字名 (例: thinking_ai)
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		name: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/**
 | 
					 | 
				
			||||||
		 * エイリアス
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		aliases?: string[];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/**
 | 
					 | 
				
			||||||
		 * 絵文字画像のURL
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		url: string;
 | 
					 | 
				
			||||||
	}[];
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ import { packMany as packFileMany, IDriveFile } from './drive-file';
 | 
				
			|||||||
import Favorite from './favorite';
 | 
					import Favorite from './favorite';
 | 
				
			||||||
import Following from './following';
 | 
					import Following from './following';
 | 
				
			||||||
import config from '../config';
 | 
					import config from '../config';
 | 
				
			||||||
 | 
					import Emoji from './emoji';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Note = db.get<INote>('notes');
 | 
					const Note = db.get<INote>('notes');
 | 
				
			||||||
Note.createIndex('uri', { sparse: true, unique: true });
 | 
					Note.createIndex('uri', { sparse: true, unique: true });
 | 
				
			||||||
@@ -49,6 +50,7 @@ export type INote = {
 | 
				
			|||||||
	text: string;
 | 
						text: string;
 | 
				
			||||||
	tags: string[];
 | 
						tags: string[];
 | 
				
			||||||
	tagsLower: string[];
 | 
						tagsLower: string[];
 | 
				
			||||||
 | 
						emojis: string[];
 | 
				
			||||||
	cw: string;
 | 
						cw: string;
 | 
				
			||||||
	userId: mongo.ObjectID;
 | 
						userId: mongo.ObjectID;
 | 
				
			||||||
	appId: mongo.ObjectID;
 | 
						appId: mongo.ObjectID;
 | 
				
			||||||
@@ -228,6 +230,26 @@ export const pack = async (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	const id = _note._id;
 | 
						const id = _note._id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// _note._userを消す前か、_note.userを解決した後でないとホストがわからない
 | 
				
			||||||
 | 
						if (_note._user) {
 | 
				
			||||||
 | 
							const host = _note._user.host;
 | 
				
			||||||
 | 
							// 互換性のため。(古いMisskeyではNoteにemojisが無い)
 | 
				
			||||||
 | 
							if (_note.emojis == null) {
 | 
				
			||||||
 | 
								_note.emojis = Emoji.find({
 | 
				
			||||||
 | 
									host: host
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									fields: { _id: false }
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								_note.emojis = Emoji.find({
 | 
				
			||||||
 | 
									name: { $in: _note.emojis },
 | 
				
			||||||
 | 
									host: host
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									fields: { _id: false }
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Rename _id to id
 | 
						// Rename _id to id
 | 
				
			||||||
	_note.id = _note._id;
 | 
						_note.id = _note._id;
 | 
				
			||||||
	delete _note._id;
 | 
						delete _note._id;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,8 @@ import * as mongo from 'mongodb';
 | 
				
			|||||||
import db from '../db/mongodb';
 | 
					import db from '../db/mongodb';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PollVote = db.get<IPollVote>('pollVotes');
 | 
					const PollVote = db.get<IPollVote>('pollVotes');
 | 
				
			||||||
 | 
					PollVote.createIndex('userId');
 | 
				
			||||||
 | 
					PollVote.createIndex('noteId');
 | 
				
			||||||
export default PollVote;
 | 
					export default PollVote;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IPollVote {
 | 
					export interface IPollVote {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -189,7 +189,7 @@ export async function getRelation(me: mongo.ObjectId, target: mongo.ObjectId) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return {
 | 
						return {
 | 
				
			||||||
		isFollowing: following1 !== null,
 | 
							isFollowing: following1 !== null,
 | 
				
			||||||
		isStalking: following1 && following1.stalk,
 | 
							isStalking: following1 ? following1.stalk : false,
 | 
				
			||||||
		hasPendingFollowRequestFromYou: followReq1 !== null,
 | 
							hasPendingFollowRequestFromYou: followReq1 !== null,
 | 
				
			||||||
		hasPendingFollowRequestToYou: followReq2 !== null,
 | 
							hasPendingFollowRequestToYou: followReq2 !== null,
 | 
				
			||||||
		isFollowed: following2 !== null,
 | 
							isFollowed: following2 !== null,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ import perform from '../../../remote/activitypub/perform';
 | 
				
			|||||||
import { resolvePerson, updatePerson } from '../../../remote/activitypub/models/person';
 | 
					import { resolvePerson, updatePerson } from '../../../remote/activitypub/models/person';
 | 
				
			||||||
import { toUnicode } from 'punycode';
 | 
					import { toUnicode } from 'punycode';
 | 
				
			||||||
import { URL } from 'url';
 | 
					import { URL } from 'url';
 | 
				
			||||||
 | 
					import { publishApLogStream } from '../../../stream';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const log = debug('misskey:queue:inbox');
 | 
					const log = debug('misskey:queue:inbox');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,6 +62,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
 | 
				
			|||||||
		}) as IRemoteUser;
 | 
							}) as IRemoteUser;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//#region Log
 | 
				
			||||||
 | 
						publishApLogStream({
 | 
				
			||||||
 | 
							direction: 'in',
 | 
				
			||||||
 | 
							activity: activity.type,
 | 
				
			||||||
 | 
							host: user.host,
 | 
				
			||||||
 | 
							actor: user.username
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update activityの場合は、ここで署名検証/更新処理まで実施して終了
 | 
						// Update activityの場合は、ここで署名検証/更新処理まで実施して終了
 | 
				
			||||||
	if (activity.type === 'Update') {
 | 
						if (activity.type === 'Update') {
 | 
				
			||||||
		if (activity.object && activity.object.type === 'Person') {
 | 
							if (activity.object && activity.object.type === 'Person') {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								src/remote/activitypub/misc/get-emoji-names.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/remote/activitypub/misc/get-emoji-names.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					import parse from '../../../mfm/parse';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function(text: string) {
 | 
				
			||||||
 | 
						if (!text) return [];
 | 
				
			||||||
 | 
						return parse(text).filter(t => t.type === 'emoji').map(t => (t as any).emoji);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								src/remote/activitypub/models/icon.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/remote/activitypub/models/icon.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					export type IIcon = {
 | 
				
			||||||
 | 
						type: string;
 | 
				
			||||||
 | 
						mediaType?: string;
 | 
				
			||||||
 | 
						url?: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -10,6 +10,9 @@ import { resolvePerson, updatePerson } from './person';
 | 
				
			|||||||
import { resolveImage } from './image';
 | 
					import { resolveImage } from './image';
 | 
				
			||||||
import { IRemoteUser, IUser } from '../../../models/user';
 | 
					import { IRemoteUser, IUser } from '../../../models/user';
 | 
				
			||||||
import htmlToMFM from '../../../mfm/html-to-mfm';
 | 
					import htmlToMFM from '../../../mfm/html-to-mfm';
 | 
				
			||||||
 | 
					import Emoji from '../../../models/emoji';
 | 
				
			||||||
 | 
					import { ITag } from './tag';
 | 
				
			||||||
 | 
					import { toUnicode } from 'punycode';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const log = debug('misskey:activitypub');
 | 
					const log = debug('misskey:activitypub');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -93,6 +96,10 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
 | 
				
			|||||||
	// テキストのパース
 | 
						// テキストのパース
 | 
				
			||||||
	const text = note._misskey_content ? note._misskey_content : htmlToMFM(note.content);
 | 
						const text = note._misskey_content ? note._misskey_content : htmlToMFM(note.content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						await extractEmojis(note.tag, actor.host).catch(e => {
 | 
				
			||||||
 | 
							console.log(`extractEmojis: ${e}`);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// ユーザーの情報が古かったらついでに更新しておく
 | 
						// ユーザーの情報が古かったらついでに更新しておく
 | 
				
			||||||
	if (actor.updatedAt == null || Date.now() - actor.updatedAt.getTime() > 1000 * 60 * 60 * 24) {
 | 
						if (actor.updatedAt == null || Date.now() - actor.updatedAt.getTime() > 1000 * 60 * 60 * 24) {
 | 
				
			||||||
		updatePerson(note.attributedTo);
 | 
							updatePerson(note.attributedTo);
 | 
				
			||||||
@@ -135,3 +142,35 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
 | 
				
			|||||||
	// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
 | 
						// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
 | 
				
			||||||
	return await createNote(uri, resolver);
 | 
						return await createNote(uri, resolver);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function extractEmojis(tags: ITag[], host_: string) {
 | 
				
			||||||
 | 
						const host = toUnicode(host_.toLowerCase());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!tags) return [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const eomjiTags = tags.filter(tag => tag.type === 'Emoji' && tag.icon && tag.icon.url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return await Promise.all(
 | 
				
			||||||
 | 
							eomjiTags.map(async tag => {
 | 
				
			||||||
 | 
								const name = tag.name.replace(/^:/, '').replace(/:$/, '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const exists = await Emoji.findOne({
 | 
				
			||||||
 | 
									host,
 | 
				
			||||||
 | 
									name
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (exists) {
 | 
				
			||||||
 | 
									return exists;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								log(`register emoji host=${host}, name=${name}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return await Emoji.insert({
 | 
				
			||||||
 | 
									host,
 | 
				
			||||||
 | 
									name,
 | 
				
			||||||
 | 
									url: tag.icon.url,
 | 
				
			||||||
 | 
									aliases: [],
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								src/remote/activitypub/models/tag.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/remote/activitypub/models/tag.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					import { IIcon } from "./icon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***
 | 
				
			||||||
 | 
					 * tag (ActivityPub)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type ITag = {
 | 
				
			||||||
 | 
						id: string;
 | 
				
			||||||
 | 
						type: string;
 | 
				
			||||||
 | 
						name?: string;
 | 
				
			||||||
 | 
						updated?: Date;
 | 
				
			||||||
 | 
						icon?: IIcon;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										14
									
								
								src/remote/activitypub/renderer/emoji.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/remote/activitypub/renderer/emoji.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					import { IEmoji } from '../../../models/emoji';
 | 
				
			||||||
 | 
					import config from '../../../config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default (emoji: IEmoji) => ({
 | 
				
			||||||
 | 
						id: `${config.url}/emojis/${emoji.name}`,
 | 
				
			||||||
 | 
						type: 'Emoji',
 | 
				
			||||||
 | 
						name: `:${emoji.name}:`,
 | 
				
			||||||
 | 
						updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString,
 | 
				
			||||||
 | 
						icon: {
 | 
				
			||||||
 | 
							type: 'Image',
 | 
				
			||||||
 | 
							mediaType: 'image/png',	//Mei-TODO
 | 
				
			||||||
 | 
							url: emoji.url
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -1,12 +1,16 @@
 | 
				
			|||||||
import renderDocument from './document';
 | 
					import renderDocument from './document';
 | 
				
			||||||
import renderHashtag from './hashtag';
 | 
					import renderHashtag from './hashtag';
 | 
				
			||||||
import renderMention from './mention';
 | 
					import renderMention from './mention';
 | 
				
			||||||
 | 
					import renderEmoji from './emoji';
 | 
				
			||||||
import config from '../../../config';
 | 
					import config from '../../../config';
 | 
				
			||||||
import DriveFile, { IDriveFile } from '../../../models/drive-file';
 | 
					import DriveFile, { IDriveFile } from '../../../models/drive-file';
 | 
				
			||||||
import Note, { INote } from '../../../models/note';
 | 
					import Note, { INote } from '../../../models/note';
 | 
				
			||||||
import User from '../../../models/user';
 | 
					import User from '../../../models/user';
 | 
				
			||||||
import toHtml from '../misc/get-note-html';
 | 
					import toHtml from '../misc/get-note-html';
 | 
				
			||||||
import parseMfm from '../../../mfm/parse';
 | 
					import parseMfm from '../../../mfm/parse';
 | 
				
			||||||
 | 
					import getEmojiNames from '../misc/get-emoji-names';
 | 
				
			||||||
 | 
					import Emoji, { IEmoji } from '../../../models/emoji';
 | 
				
			||||||
 | 
					import { unique } from '../../../prelude/array';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function renderNote(note: INote, dive = true): Promise<any> {
 | 
					export default async function renderNote(note: INote, dive = true): Promise<any> {
 | 
				
			||||||
	const promisedFiles: Promise<IDriveFile[]> = note.fileIds
 | 
						const promisedFiles: Promise<IDriveFile[]> = note.fileIds
 | 
				
			||||||
@@ -75,10 +79,6 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	const hashtagTags = (note.tags || []).map(tag => renderHashtag(tag));
 | 
						const hashtagTags = (note.tags || []).map(tag => renderHashtag(tag));
 | 
				
			||||||
	const mentionTags = mentionedUsers.map(u => renderMention(u));
 | 
						const mentionTags = mentionedUsers.map(u => renderMention(u));
 | 
				
			||||||
	const tag = [
 | 
					 | 
				
			||||||
		...hashtagTags,
 | 
					 | 
				
			||||||
		...mentionTags,
 | 
					 | 
				
			||||||
	];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const files = await promisedFiles;
 | 
						const files = await promisedFiles;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -108,12 +108,24 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
 | 
				
			|||||||
		}).join('');
 | 
							}).join('');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const content = toHtml(Object.assign({}, note, { text }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const emojiNames = unique(getEmojiNames(content));
 | 
				
			||||||
 | 
						const emojis = await getEmojis(emojiNames);
 | 
				
			||||||
 | 
						const apemojis = emojis.map(emoji => renderEmoji(emoji));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const tag = [
 | 
				
			||||||
 | 
							...hashtagTags,
 | 
				
			||||||
 | 
							...mentionTags,
 | 
				
			||||||
 | 
							...apemojis,
 | 
				
			||||||
 | 
						];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return {
 | 
						return {
 | 
				
			||||||
		id: `${config.url}/notes/${note._id}`,
 | 
							id: `${config.url}/notes/${note._id}`,
 | 
				
			||||||
		type: 'Note',
 | 
							type: 'Note',
 | 
				
			||||||
		attributedTo,
 | 
							attributedTo,
 | 
				
			||||||
		summary: note.cw,
 | 
							summary: note.cw,
 | 
				
			||||||
		content: toHtml(Object.assign({}, note, { text })),
 | 
							content,
 | 
				
			||||||
		_misskey_content: text,
 | 
							_misskey_content: text,
 | 
				
			||||||
		published: note.createdAt.toISOString(),
 | 
							published: note.createdAt.toISOString(),
 | 
				
			||||||
		to,
 | 
							to,
 | 
				
			||||||
@@ -124,3 +136,18 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
 | 
				
			|||||||
		tag
 | 
							tag
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function getEmojis(names: string[]): Promise<IEmoji[]> {
 | 
				
			||||||
 | 
						if (names == null || names.length < 1) return [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const emojis = await Promise.all(
 | 
				
			||||||
 | 
							names.map(async name => {
 | 
				
			||||||
 | 
								return await Emoji.findOne({
 | 
				
			||||||
 | 
									name,
 | 
				
			||||||
 | 
									host: null
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return emojis.filter(emoji => emoji != null);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ const crypto = require('crypto');
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import config from '../../config';
 | 
					import config from '../../config';
 | 
				
			||||||
import { ILocalUser } from '../../models/user';
 | 
					import { ILocalUser } from '../../models/user';
 | 
				
			||||||
 | 
					import { publishApLogStream } from '../../stream';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const log = debug('misskey:activitypub:deliver');
 | 
					const log = debug('misskey:activitypub:deliver');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -64,4 +65,13 @@ export default (user: ILocalUser, url: string, object: any) => new Promise((reso
 | 
				
			|||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req.end(data);
 | 
						req.end(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//#region Log
 | 
				
			||||||
 | 
						publishApLogStream({
 | 
				
			||||||
 | 
							direction: 'out',
 | 
				
			||||||
 | 
							activity: object.type,
 | 
				
			||||||
 | 
							host: null,
 | 
				
			||||||
 | 
							actor: user.username
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						//#endregion
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import * as mongo from 'mongodb';
 | 
					import * as mongo from 'mongodb';
 | 
				
			||||||
import * as Router from 'koa-router';
 | 
					import * as Router from 'koa-router';
 | 
				
			||||||
import config from '../../config';
 | 
					import config from '../../config';
 | 
				
			||||||
import $ from 'cafy'; import ID from '../../misc/cafy-id';
 | 
					import $ from 'cafy'; import ID, { transform } from '../../misc/cafy-id';
 | 
				
			||||||
import User from '../../models/user';
 | 
					import User from '../../models/user';
 | 
				
			||||||
import Following from '../../models/following';
 | 
					import Following from '../../models/following';
 | 
				
			||||||
import pack from '../../remote/activitypub/renderer';
 | 
					import pack from '../../remote/activitypub/renderer';
 | 
				
			||||||
@@ -49,7 +49,7 @@ export default async (ctx: Router.IRouterContext) => {
 | 
				
			|||||||
		// カーソルが指定されている場合
 | 
							// カーソルが指定されている場合
 | 
				
			||||||
		if (cursor) {
 | 
							if (cursor) {
 | 
				
			||||||
			query._id = {
 | 
								query._id = {
 | 
				
			||||||
				$lt: cursor
 | 
									$lt: transform(cursor)
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import * as mongo from 'mongodb';
 | 
					import * as mongo from 'mongodb';
 | 
				
			||||||
import * as Router from 'koa-router';
 | 
					import * as Router from 'koa-router';
 | 
				
			||||||
import config from '../../config';
 | 
					import config from '../../config';
 | 
				
			||||||
import $ from 'cafy'; import ID from '../../misc/cafy-id';
 | 
					import $ from 'cafy'; import ID, { transform } from '../../misc/cafy-id';
 | 
				
			||||||
import User from '../../models/user';
 | 
					import User from '../../models/user';
 | 
				
			||||||
import Following from '../../models/following';
 | 
					import Following from '../../models/following';
 | 
				
			||||||
import pack from '../../remote/activitypub/renderer';
 | 
					import pack from '../../remote/activitypub/renderer';
 | 
				
			||||||
@@ -49,7 +49,7 @@ export default async (ctx: Router.IRouterContext) => {
 | 
				
			|||||||
		// カーソルが指定されている場合
 | 
							// カーソルが指定されている場合
 | 
				
			||||||
		if (cursor) {
 | 
							if (cursor) {
 | 
				
			||||||
			query._id = {
 | 
								query._id = {
 | 
				
			||||||
				$lt: cursor
 | 
									$lt: transform(cursor)
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import * as mongo from 'mongodb';
 | 
					import * as mongo from 'mongodb';
 | 
				
			||||||
import * as Router from 'koa-router';
 | 
					import * as Router from 'koa-router';
 | 
				
			||||||
import config from '../../config';
 | 
					import config from '../../config';
 | 
				
			||||||
import $ from 'cafy'; import ID from '../../misc/cafy-id';
 | 
					import $ from 'cafy'; import ID, { transform } from '../../misc/cafy-id';
 | 
				
			||||||
import User from '../../models/user';
 | 
					import User from '../../models/user';
 | 
				
			||||||
import pack from '../../remote/activitypub/renderer';
 | 
					import pack from '../../remote/activitypub/renderer';
 | 
				
			||||||
import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
 | 
					import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
 | 
				
			||||||
@@ -61,11 +61,11 @@ export default async (ctx: Router.IRouterContext) => {
 | 
				
			|||||||
		if (sinceId) {
 | 
							if (sinceId) {
 | 
				
			||||||
			sort._id = 1;
 | 
								sort._id = 1;
 | 
				
			||||||
			query._id = {
 | 
								query._id = {
 | 
				
			||||||
				$gt: sinceId
 | 
									$gt: transform(sinceId)
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		} else if (untilId) {
 | 
							} else if (untilId) {
 | 
				
			||||||
			query._id = {
 | 
								query._id = {
 | 
				
			||||||
				$lt: untilId
 | 
									$lt: transform(untilId)
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		//#endregion
 | 
							//#endregion
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,18 +42,12 @@ export default (endpoint: string, user: IUser, app: IApp, data: any, file?: any)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let exec = ep.exec;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ep.meta.requireFile && file) {
 | 
					 | 
				
			||||||
		exec = exec.bind(null, file);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let res;
 | 
						let res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// API invoking
 | 
						// API invoking
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		const before = performance.now();
 | 
							const before = performance.now();
 | 
				
			||||||
		res = await exec(data, user, app);
 | 
							res = await ep.exec(data, user, app, file);
 | 
				
			||||||
		const after = performance.now();
 | 
							const after = performance.now();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const time = after - before;
 | 
							const time = after - before;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										51
									
								
								src/server/api/define.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/server/api/define.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					import * as fs from 'fs';
 | 
				
			||||||
 | 
					import { ILocalUser } from '../../models/user';
 | 
				
			||||||
 | 
					import { IApp } from '../../models/app';
 | 
				
			||||||
 | 
					import { IEndpointMeta } from './endpoints';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Params<T extends IEndpointMeta> = {
 | 
				
			||||||
 | 
						[P in keyof T['params']]: T['params'][P]['transform'] extends Function
 | 
				
			||||||
 | 
							? ReturnType<T['params'][P]['transform']>
 | 
				
			||||||
 | 
							: ReturnType<T['params'][P]['validator']['get']>[0];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function <T extends IEndpointMeta>(meta: T, cb: (params: Params<T>, user: ILocalUser, app: IApp, file?: any, cleanup?: Function) => Promise<any>): (params: any, user: ILocalUser, app: IApp, file?: any) => Promise<any> {
 | 
				
			||||||
 | 
						return (params: any, user: ILocalUser, app: IApp, file?: any) => {
 | 
				
			||||||
 | 
							function cleanup() {
 | 
				
			||||||
 | 
								fs.unlink(file.path, () => {});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (meta.requireFile && file == null) return Promise.reject('file required');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const [ps, pserr] = getParams(meta, params);
 | 
				
			||||||
 | 
							if (pserr) {
 | 
				
			||||||
 | 
								if (file) cleanup();
 | 
				
			||||||
 | 
								return Promise.reject(pserr);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return cb(ps, user, app, file, cleanup);
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getParams<T extends IEndpointMeta>(defs: T, params: any): [Params<T>, Error] {
 | 
				
			||||||
 | 
						const x: any = {};
 | 
				
			||||||
 | 
						let err: Error = null;
 | 
				
			||||||
 | 
						Object.entries(defs.params).some(([k, def]) => {
 | 
				
			||||||
 | 
							const [v, e] = def.validator.get(params[k]);
 | 
				
			||||||
 | 
							if (e) {
 | 
				
			||||||
 | 
								err = new Error(e.message);
 | 
				
			||||||
 | 
								err.name = 'INVALID_PARAM';
 | 
				
			||||||
 | 
								(err as any).param = k;
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (v === undefined && def.default) {
 | 
				
			||||||
 | 
									x[k] = def.default;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									x[k] = v;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (def.transform) x[k] = def.transform(x[k]);
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						return [x, err];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,12 +1,21 @@
 | 
				
			|||||||
 | 
					import { Context } from 'cafy';
 | 
				
			||||||
import * as path from 'path';
 | 
					import * as path from 'path';
 | 
				
			||||||
import * as glob from 'glob';
 | 
					import * as glob from 'glob';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IEndpointMeta {
 | 
					export interface IEndpointMeta {
 | 
				
			||||||
	stability?: 'deprecated' | 'experimental' | 'stable';
 | 
						stability?: string; //'deprecated' | 'experimental' | 'stable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	desc?: any;
 | 
						desc?: { [key: string]: string };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	params?: any;
 | 
						params?: {
 | 
				
			||||||
 | 
							[key: string]: {
 | 
				
			||||||
 | 
								validator: Context<any>;
 | 
				
			||||||
 | 
								transform?: any;
 | 
				
			||||||
 | 
								default?: any;
 | 
				
			||||||
 | 
								desc?: { [key: string]: string };
 | 
				
			||||||
 | 
								ref?: string;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res?: any;
 | 
						res?: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user