1
0
mirror of https://github.com/hotomoe/hotomoe synced 2024-12-04 17:58:10 +09:00

Merge branch 'develop'

This commit is contained in:
syuilo 2023-02-10 20:14:47 +09:00
commit 000f876084
25 changed files with 475 additions and 207 deletions

View File

@ -8,6 +8,17 @@
You should also include the user name that made the change. You should also include the user name that made the change.
--> -->
## 13.5.6 (2023/02/10)
### Improvements
- 非ログイン時にMiAuthを踏んだ際にMiAuthであることを表示する
- /auth/のUIをアップデート
- 利用規約同意UIの調整
- クロップ時の質問を分かりやすく
### Bugfixes
- fix: prevent clipping audio plyr's tooltip
## 13.5.4 (2023/02/09) ## 13.5.4 (2023/02/09)
### Improvements ### Improvements

View File

@ -257,6 +257,8 @@ noMoreHistory: "Kein weiterer Verlauf vorhanden"
startMessaging: "Neuen Chat erstellen" startMessaging: "Neuen Chat erstellen"
nUsersRead: "Von {n} Benutzern gelesen" nUsersRead: "Von {n} Benutzern gelesen"
agreeTo: "Ich stimme {0} zu" agreeTo: "Ich stimme {0} zu"
agreeBelow: "Ich stimme Untenstehendem zu"
basicNotesBeforeCreateAccount: "Wichtige Infos"
tos: "Nutzungsbedingungen" tos: "Nutzungsbedingungen"
start: "Anfangen" start: "Anfangen"
home: "Startseite" home: "Startseite"
@ -862,6 +864,8 @@ failedToFetchAccountInformation: "Benutzerkontoinformationen konnten nicht abgef
rateLimitExceeded: "Versuchsanzahl überschritten" rateLimitExceeded: "Versuchsanzahl überschritten"
cropImage: "Bild zuschneiden" cropImage: "Bild zuschneiden"
cropImageAsk: "Möchtest du das Bild zuschneiden?" cropImageAsk: "Möchtest du das Bild zuschneiden?"
cropYes: "Zuschneiden"
cropNo: "Unbearbeitet verwenden"
file: "Datei" file: "Datei"
recentNHours: "Letzten {n} Stunden" recentNHours: "Letzten {n} Stunden"
recentNDays: "Letzten {n} Tage" recentNDays: "Letzten {n} Tage"
@ -940,6 +944,8 @@ cannotPerformTemporaryDescription: "Diese Aktion ist wegen des Überschreitenes
preset: "Vorlage" preset: "Vorlage"
selectFromPresets: "Aus Vorlagen wählen" selectFromPresets: "Aus Vorlagen wählen"
achievements: "Errungenschaften" achievements: "Errungenschaften"
gotInvalidResponseError: "Ungültige Antwort des Servers"
gotInvalidResponseErrorDescription: "Eventuell ist der Server momentan nicht erreichbar oder untergeht Wartungsarbeiten. Bitte versuche es später noch einmal."
_achievements: _achievements:
earnedAt: "Freigeschaltet am" earnedAt: "Freigeschaltet am"
_types: _types:
@ -1594,12 +1600,15 @@ _permissions:
"read:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge lesen" "read:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge lesen"
"write:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge bearbeiten" "write:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge bearbeiten"
_auth: _auth:
shareAccessTitle: "Verteilung von App-Berechtigungen"
shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?" shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?"
shareAccessAsk: "Bist du dir sicher, dass du diese Anwendung authorisieren möchtest, auf dein Benutzerkonto zugreifen zu können?" shareAccessAsk: "Bist du dir sicher, dass du diese Anwendung authorisieren möchtest, auf dein Benutzerkonto zugreifen zu können?"
permission: "{name} fordert folgende Berechtigungen"
permissionAsk: "Diese Anwendung fordert folgende Berechtigungen" permissionAsk: "Diese Anwendung fordert folgende Berechtigungen"
pleaseGoBack: "Bitte kehre zur Anwendung zurück" pleaseGoBack: "Bitte kehre zur Anwendung zurück"
callback: "Es wird zur Anwendung zurückgekehrt" callback: "Es wird zur Anwendung zurückgekehrt"
denied: "Zugriff verweigert" denied: "Zugriff verweigert"
pleaseLogin: "Bitte logge dich ein, um Apps zu authorisieren."
_antennaSources: _antennaSources:
all: "Alle Notizen" all: "Alle Notizen"
homeTimeline: "Notizen von Benutzern, denen gefolgt wird" homeTimeline: "Notizen von Benutzern, denen gefolgt wird"

View File

@ -257,6 +257,8 @@ noMoreHistory: "There is no further history"
startMessaging: "Start a new chat" startMessaging: "Start a new chat"
nUsersRead: "read by {n}" nUsersRead: "read by {n}"
agreeTo: "I agree to {0}" agreeTo: "I agree to {0}"
agreeBelow: "I agree to the below"
basicNotesBeforeCreateAccount: "Important notes"
tos: "Terms of Service" tos: "Terms of Service"
start: "Begin" start: "Begin"
home: "Home" home: "Home"
@ -862,6 +864,8 @@ failedToFetchAccountInformation: "Could not fetch account information"
rateLimitExceeded: "Rate limit exceeded" rateLimitExceeded: "Rate limit exceeded"
cropImage: "Crop image" cropImage: "Crop image"
cropImageAsk: "Do you want to crop this image?" cropImageAsk: "Do you want to crop this image?"
cropYes: "Crop"
cropNo: "Use as-is"
file: "File" file: "File"
recentNHours: "Last {n} hours" recentNHours: "Last {n} hours"
recentNDays: "Last {n} days" recentNDays: "Last {n} days"
@ -940,6 +944,8 @@ cannotPerformTemporaryDescription: "This action cannot be performed temporarily
preset: "Preset" preset: "Preset"
selectFromPresets: "Choose from presets" selectFromPresets: "Choose from presets"
achievements: "Achievements" achievements: "Achievements"
gotInvalidResponseError: "Invalid server response"
gotInvalidResponseErrorDescription: "The server may be unreachable or undergoing maintenance. Please try again later."
_achievements: _achievements:
earnedAt: "Unlocked at" earnedAt: "Unlocked at"
_types: _types:
@ -1594,12 +1600,15 @@ _permissions:
"read:gallery-likes": "View your list of liked gallery posts" "read:gallery-likes": "View your list of liked gallery posts"
"write:gallery-likes": "Edit your list of liked gallery posts" "write:gallery-likes": "Edit your list of liked gallery posts"
_auth: _auth:
shareAccessTitle: "Granting application permissions"
shareAccess: "Would you like to authorize \"{name}\" to access this account?" shareAccess: "Would you like to authorize \"{name}\" to access this account?"
shareAccessAsk: "Are you sure you want to authorize this application to access your account?" shareAccessAsk: "Are you sure you want to authorize this application to access your account?"
permission: "{name} requests the following permissions"
permissionAsk: "This application requests the following permissions" permissionAsk: "This application requests the following permissions"
pleaseGoBack: "Please go back to the application" pleaseGoBack: "Please go back to the application"
callback: "Returning to the application" callback: "Returning to the application"
denied: "Access denied" denied: "Access denied"
pleaseLogin: "Please log in to authorize applications."
_antennaSources: _antennaSources:
all: "All notes" all: "All notes"
homeTimeline: "Notes from followed users" homeTimeline: "Notes from followed users"

View File

@ -56,7 +56,7 @@ reply: "Responder"
loadMore: "Ver más" loadMore: "Ver más"
showMore: "Ver más" showMore: "Ver más"
showLess: "Cerrar" showLess: "Cerrar"
youGotNewFollower: "te ha seguido" youGotNewFollower: "ahora te sigue"
receiveFollowRequest: "Recibiste una solicitud de seguimiento" receiveFollowRequest: "Recibiste una solicitud de seguimiento"
followRequestAccepted: "La solicitud de seguimiento fue aceptada" followRequestAccepted: "La solicitud de seguimiento fue aceptada"
mention: "Menciones" mention: "Menciones"
@ -257,6 +257,8 @@ noMoreHistory: "El historial se ha acabado"
startMessaging: "Iniciar chat" startMessaging: "Iniciar chat"
nUsersRead: "Leído por {n} personas" nUsersRead: "Leído por {n} personas"
agreeTo: "De acuerdo con {0}" agreeTo: "De acuerdo con {0}"
agreeBelow: "Estoy de acuerdo con lo siguiente"
basicNotesBeforeCreateAccount: "Notas básicas"
tos: "Términos de uso" tos: "Términos de uso"
start: "Comenzar" start: "Comenzar"
home: "Inicio" home: "Inicio"
@ -940,6 +942,8 @@ cannotPerformTemporaryDescription: "Esta acción no se puede realizar porque se
preset: "Predefinido" preset: "Predefinido"
selectFromPresets: "Escoger desde predefinidos" selectFromPresets: "Escoger desde predefinidos"
achievements: "Logros" achievements: "Logros"
gotInvalidResponseError: "Respuesta del servidor inválida"
gotInvalidResponseErrorDescription: "Puede que el servidor esté caído o en mantenimiento. Favor de intentar más tarde"
_achievements: _achievements:
earnedAt: "Desbloqueado el" earnedAt: "Desbloqueado el"
_types: _types:
@ -1594,12 +1598,15 @@ _permissions:
"read:gallery-likes": "Ver favoritos de la galería" "read:gallery-likes": "Ver favoritos de la galería"
"write:gallery-likes": "Editar favoritos de la galería" "write:gallery-likes": "Editar favoritos de la galería"
_auth: _auth:
shareAccessTitle: "Permisos de la aplicación"
shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?" shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?"
shareAccessAsk: "¿Está seguro de que desea autorizar esta aplicación para acceder a su cuenta?" shareAccessAsk: "¿Está seguro de que desea autorizar esta aplicación para acceder a su cuenta?"
permission: "{name} solicita los siguientes permisos"
permissionAsk: "Esta aplicación requiere los siguientes permisos" permissionAsk: "Esta aplicación requiere los siguientes permisos"
pleaseGoBack: "Por favor, vuelve a la aplicación" pleaseGoBack: "Por favor, vuelve a la aplicación"
callback: "Volviendo a la aplicación" callback: "Volviendo a la aplicación"
denied: "Acceso denegado" denied: "Acceso denegado"
pleaseLogin: "Se requiere un inicio de sesión para darle permisos a la aplicación"
_antennaSources: _antennaSources:
all: "Todas las notas" all: "Todas las notas"
homeTimeline: "Notas de los usuarios que sigues" homeTimeline: "Notas de los usuarios que sigues"

View File

@ -257,6 +257,8 @@ noMoreHistory: "これより過去の履歴はありません"
startMessaging: "チャットを開始" startMessaging: "チャットを開始"
nUsersRead: "{n}人が読みました" nUsersRead: "{n}人が読みました"
agreeTo: "{0}に同意" agreeTo: "{0}に同意"
agreeBelow: "下記に同意する"
basicNotesBeforeCreateAccount: "基本的な注意事項"
tos: "利用規約" tos: "利用規約"
start: "始める" start: "始める"
home: "ホーム" home: "ホーム"
@ -862,6 +864,8 @@ failedToFetchAccountInformation: "アカウント情報の取得に失敗しま
rateLimitExceeded: "レート制限を超えました" rateLimitExceeded: "レート制限を超えました"
cropImage: "画像のクロップ" cropImage: "画像のクロップ"
cropImageAsk: "画像をクロップしますか?" cropImageAsk: "画像をクロップしますか?"
cropYes: "クロップする"
cropNo: "そのまま使う"
file: "ファイル" file: "ファイル"
recentNHours: "直近{n}時間" recentNHours: "直近{n}時間"
recentNDays: "直近{n}日" recentNDays: "直近{n}日"
@ -1628,12 +1632,15 @@ _permissions:
"write:gallery-likes": "ギャラリーのいいねを操作する" "write:gallery-likes": "ギャラリーのいいねを操作する"
_auth: _auth:
shareAccessTitle: "アプリへのアクセス許可"
shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?" shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?"
shareAccessAsk: "アカウントへのアクセスを許可しますか?" shareAccessAsk: "アカウントへのアクセスを許可しますか?"
permission: "{name}は次の権限を要求しています"
permissionAsk: "このアプリは次の権限を要求しています" permissionAsk: "このアプリは次の権限を要求しています"
pleaseGoBack: "アプリケーションに戻ってやっていってください" pleaseGoBack: "アプリケーションに戻ってやっていってください"
callback: "アプリケーションに戻っています" callback: "アプリケーションに戻っています"
denied: "アクセスを拒否しました" denied: "アクセスを拒否しました"
pleaseLogin: "アプリケーションにアクセス許可を与えるには、ログインが必要です。"
_antennaSources: _antennaSources:
all: "全てのノート" all: "全てのノート"

View File

@ -46,7 +46,7 @@ copyContent: "内容をコピー"
copyLink: "リンクをコピー" copyLink: "リンクをコピー"
delete: "ほかす" delete: "ほかす"
deleteAndEdit: "ほかして直す" deleteAndEdit: "ほかして直す"
deleteAndEditConfirm: "このノートをほかして書き直すんかこのートへのリアクション、Renote、返信も全部消えてまうで。" deleteAndEditConfirm: "このノートをほかしてもっかい直すこのートへのリアクション、Renote、返信も全部消えるんやけどそれでもええん"
addToList: "リストに入れたる" addToList: "リストに入れたる"
sendMessage: "メッセージを送る" sendMessage: "メッセージを送る"
copyRSS: "RSSをコピー" copyRSS: "RSSをコピー"
@ -89,7 +89,7 @@ serverIsDead: "サーバーからの応答がないで。もうちょい待っ
youShouldUpgradeClient: "このページを表示するには、リロードして新しいバージョンのクライアントを使ってなー。" youShouldUpgradeClient: "このページを表示するには、リロードして新しいバージョンのクライアントを使ってなー。"
enterListName: "リスト名を入れてや" enterListName: "リスト名を入れてや"
privacy: "プライバシー" privacy: "プライバシー"
makeFollowManuallyApprove: "自分が認めた人だけがこのアカウントをフォローできるようにする" makeFollowManuallyApprove: "他人のフォローは許可してからや!"
defaultNoteVisibility: "もとからの公開範囲" defaultNoteVisibility: "もとからの公開範囲"
follow: "フォロー" follow: "フォロー"
followRequest: "フォローを頼む" followRequest: "フォローを頼む"
@ -129,6 +129,7 @@ unblockConfirm: "ブロックやめたるってほんまか?"
suspendConfirm: "凍結してしもうてええか?" suspendConfirm: "凍結してしもうてええか?"
unsuspendConfirm: "解凍するけどええか?" unsuspendConfirm: "解凍するけどええか?"
selectList: "リストを選ぶ" selectList: "リストを選ぶ"
selectChannel: "チャンネルを選ぶ"
selectAntenna: "アンテナを選ぶ" selectAntenna: "アンテナを選ぶ"
selectWidget: "ウィジェットを選ぶ" selectWidget: "ウィジェットを選ぶ"
editWidgets: "ウィジェットをいじる" editWidgets: "ウィジェットをいじる"
@ -256,6 +257,8 @@ noMoreHistory: "これより過去の履歴はあらへんで"
startMessaging: "チャットやるで" startMessaging: "チャットやるで"
nUsersRead: "{n}人が読んでもうた" nUsersRead: "{n}人が読んでもうた"
agreeTo: "{0}に同意したで" agreeTo: "{0}に同意したで"
agreeBelow: "下記に同意したる"
basicNotesBeforeCreateAccount: "よう読んでやってや"
tos: "利用規約" tos: "利用規約"
start: "始める" start: "始める"
home: "ホーム" home: "ホーム"
@ -300,7 +303,7 @@ avatar: "アイコン"
banner: "バナー" banner: "バナー"
nsfw: "閲覧注意" nsfw: "閲覧注意"
whenServerDisconnected: "サーバーとの接続が切れたとき" whenServerDisconnected: "サーバーとの接続が切れたとき"
disconnectedFromServer: "サーバーとの通信が切れたで" disconnectedFromServer: "サーバーが機嫌悪いねん"
reload: "リロード" reload: "リロード"
doNothing: "何もせんとく" doNothing: "何もせんとく"
reloadConfirm: "リロードしてええか?" reloadConfirm: "リロードしてええか?"
@ -673,8 +676,8 @@ sentReactionsCount: "リアクションした数やで"
receivedReactionsCount: "リアクションされた数" receivedReactionsCount: "リアクションされた数"
pollVotesCount: "アンケートに投票した数" pollVotesCount: "アンケートに投票した数"
pollVotedCount: "アンケートに投票された数" pollVotedCount: "アンケートに投票された数"
yes: "はい" yes: "ええで"
no: "いいえ" no: "あかんで"
driveFilesCount: "ドライブのファイル数" driveFilesCount: "ドライブのファイル数"
driveUsage: "ドライブ使用量やで" driveUsage: "ドライブ使用量やで"
noCrawle: "クローラーによるインデックスを拒否するで" noCrawle: "クローラーによるインデックスを拒否するで"
@ -861,6 +864,8 @@ failedToFetchAccountInformation: "アカウントの取得に失敗したみた
rateLimitExceeded: "レート制限が超えたみたいやで" rateLimitExceeded: "レート制限が超えたみたいやで"
cropImage: "画像のクロップ" cropImage: "画像のクロップ"
cropImageAsk: "画像をクロップしたってええか?" cropImageAsk: "画像をクロップしたってええか?"
cropYes: "切り抜いたる"
cropNo: "切り抜かへん"
file: "ファイル" file: "ファイル"
recentNHours: "直近{n}時間" recentNHours: "直近{n}時間"
recentNDays: "直近{n}日" recentNDays: "直近{n}日"
@ -938,6 +943,37 @@ cannotPerformTemporary: "一時的に利用できへんで"
cannotPerformTemporaryDescription: "操作回数が制限を超えたから一時的に利用できへんくなったで。ちょっと時間置いてからもう一回やってやー。" cannotPerformTemporaryDescription: "操作回数が制限を超えたから一時的に利用できへんくなったで。ちょっと時間置いてからもう一回やってやー。"
preset: "プリセット" preset: "プリセット"
selectFromPresets: "プリセットから選ぶ" selectFromPresets: "プリセットから選ぶ"
achievements: "実績"
gotInvalidResponseError: "サーバー黙っとるわ、知らんけど"
gotInvalidResponseErrorDescription: "サーバーいま日曜日。またきて月曜日。"
_achievements:
earnedAt: "貰った日ぃ"
_types:
_notes1:
title: "まいど!"
description: "初めてノート投稿したった"
_notes10:
title: "ノートの天保山"
_notes100:
title: "ノートの真田山"
_notes500:
title: "ノートの生駒山"
_notes5000:
title: "箕面の滝からノート"
_login3:
flavor: "今日からワシはミスキストやで"
_iLoveMisskey:
title: "Misskey好きやねん"
_foundTreasure:
title: "なんでも鑑定団"
_client30min:
title: "ねんね"
_noteDeletedWithin1min:
title: "*おおっと*"
_open3windows:
title: "マド開けすぎ"
_driveFolderCircularReference:
title: "環状線"
_role: _role:
new: "ロールの作成" new: "ロールの作成"
edit: "ロールの編集" edit: "ロールの編集"
@ -1355,10 +1391,12 @@ _permissions:
_auth: _auth:
shareAccess: "「{name}」がアカウントにアクセスすることを許可してええか?" shareAccess: "「{name}」がアカウントにアクセスすることを許可してええか?"
shareAccessAsk: "アカウントのアクセスを許可してもええか?" shareAccessAsk: "アカウントのアクセスを許可してもええか?"
permission: "{name}に次の権限つけたってやって"
permissionAsk: "このアプリは次の権限を要求しとるで" permissionAsk: "このアプリは次の権限を要求しとるで"
pleaseGoBack: "アプリケーションに戻ってええよ" pleaseGoBack: "アプリケーションに戻ってええよ"
callback: "アプリケーションに戻っとるで" callback: "アプリケーションに戻っとるで"
denied: "アクセスを拒否ったで" denied: "アクセスを拒否ったで"
pleaseLogin: "アプリにアクセスさせるんやったら、ログインしてや。"
_antennaSources: _antennaSources:
all: "みんなのノート" all: "みんなのノート"
homeTimeline: "フォローしとるユーザーのノート" homeTimeline: "フォローしとるユーザーのノート"
@ -1587,6 +1625,7 @@ _notification:
pollEnded: "アンケートの結果が出たみたいや" pollEnded: "アンケートの結果が出たみたいや"
unreadAntennaNote: "アンテナ {name}" unreadAntennaNote: "アンテナ {name}"
emptyPushNotificationMessage: "プッシュ通知の更新をしといたで" emptyPushNotificationMessage: "プッシュ通知の更新をしといたで"
achievementEarned: "実績を獲得しとるで"
_types: _types:
all: "すべて" all: "すべて"
follow: "フォロー" follow: "フォロー"

View File

@ -99,18 +99,84 @@ followRequestPending: "ປະຕິບັດຕາມຄໍາຮ້ອງຂໍ
enterEmoji: "ປ້ອນອີໂມຈິ" enterEmoji: "ປ້ອນອີໂມຈິ"
renote: "Renote" renote: "Renote"
unrenote: "ເລີກ Renote" unrenote: "ເລີກ Renote"
renoted: "ເກັບບັນທຶກໄວ້"
quote: "ລວມຂໍ້ຄວາມອ້າງອີງ"
pinnedNote: "ບັນທຶກທີ່ປັກໝຸດໄວ້"
pinned: "ປັກໝຸດໄປຫາໂປຣໄຟລ໌" pinned: "ປັກໝຸດໄປຫາໂປຣໄຟລ໌"
you: "ເຈົ້າ"
clickToShow: "ກົດເພື່ອສະແດງໃຫ້ເຫັນ"
sensitive: "NSFW"
add: "ເພີ່ມ"
reaction: "ປະຕິກິລິຍາ"
reactions: "ປະຕິກິລິຍາ"
mute: "ປີດສຽງ"
unmute: "ເປີດສຽງ"
block: "ບ໋ອກ"
unblock: "ຍົກເລີກກາຮົບລັອກ"
suspend: "ລະງັບ"
unsuspend: "ເຊົາ​ລະ​ງັບ"
selectList: "ເລືອກບັນຊີລາຍການ"
selectWidget: "ເລືອກວິກເຈັດ"
editWidgets: "ແກ້ໄຂ Widget"
editWidgetsExit: "ສຳເລັດແລ້ວ"
customEmojis: "ອີໂມຈິແບບກຳນົດເອງ"
emoji: "ອີໂມຈິ"
emojis: "ອີໂມຈິ"
emojiName: "ຊື່ Emoji"
emojiUrl: "URL ອີໂມຈິ"
addEmoji: "ຕື່ມອີໂມຈິ"
flagAsBot: "ໝາຍບັນຊີນີ້ເປັນບັອດ"
flagAsCat: "ໝາຍບັນຊີນີ້ເປັນແມວ"
flagAsCatDescription: "ເປີດໃຊ້ຕົວເລືອກນີ້ເພື່ອໝາຍບັນຊີນີ້ເປັນແມວ"
flagShowTimelineReplies: "ສະແດງການຕອບກັບໃນທາມລາຍ"
flagShowTimelineRepliesDescription: "ສະແດງການຕອບກັບຂອງຜູ້ໃຊ້ຕໍ່ກັບບັນທຶກຂອງຜູ້ໃຊ້ອື່ນໃນທາມລາຍຖ້າເປີດໃຊ້ງານ"
autoAcceptFollowed: "ອະນຸມັດອັດຕະໂນມັດຕາມຄຳຮ້ອງຂໍຈາກຜູ້ໃຊ້ທີ່ທ່ານກຳລັງຕິດຕາມຢູ່"
addAccount: "ເພີ່ມບັນຊີ" addAccount: "ເພີ່ມບັນຊີ"
loginFailed: "ການເຂົ້າສູ່ລະບົບບໍ່ສຳເລັດ" loginFailed: "ການເຂົ້າສູ່ລະບົບບໍ່ສຳເລັດ"
general: "ທົ່ວໄປ" general: "ທົ່ວໄປ"
wallpaper: "ພາບພື້ນຫລັງ" wallpaper: "ພາບພື້ນຫລັງ"
setWallpaper: "ຕັ້ງເປັນພາບພື້ນຫຼັງ" setWallpaper: "ຕັ້ງເປັນພາບພື້ນຫຼັງ"
instances: "ອີນສະແຕນ" instances: "ອີນສະແຕນ"
instanceInfo: "ອີນສະແຕນ"
statistics: "ສະຖິຕິ" statistics: "ສະຖິຕິ"
clearQueue: "ລ້າງຄິວ" clearQueue: "ລ້າງຄິວ"
clearCachedFiles: "ລຶບລ້າງແຄສ" clearCachedFiles: "ລຶບລ້າງແຄສ"
editProfile: "ແກ້ໄຂໂປຣໄຟລ໌" editProfile: "ແກ້ໄຂໂປຣໄຟລ໌"
done: "ສຳເລັດ"
processing: "ກຳລັງປະມວນຜົນ"
preview: "ສະແດງເປັນຕົວຢ່າງ"
default: "ຄ່າເລີ່ມຕົ້ນ"
blocked: "ບລັອກແລ້ວ "
all: "ທັງໝົດ"
subscribing: "ສະໝັກສະມາຊິກແລັວ"
publishing: "ການ​ພິມ​ເຜີຍ​ແຜ່"
notResponding: "ບໍ່ຕອບສະໜອງ"
instanceFollowing: "ກຳລັງຕິດຕາມສຸດຕົວຢ່າງ"
instanceFollowers: "ຜູ້ຕິດຕາມຕົວຢ່າງ"
instanceUsers: "ຜູ້​ຊົມ​ໃຊ້​ຂອງ​ຕົວ​ຢ່າງ​ນີ້​"
changePassword: "ປ່ຽນ​ລະ​ຫັດ​ຜ່ານ"
featured: "ໄຮໄລທ໌"
announcements: "ປະກາດ"
remove: "ລຶບ" remove: "ລຶບ"
messaging: "ແຊ໋ດ"
tos: "ເງື່ອນໄຂການໃຫ້ບໍລິການ"
start: "ເລີ່ມຕົ້ນນຳໃຊ້ເລີຍ"
home: "ໜ້າຫຼັກ"
images: "ຮູບພາບ"
birthday: "ວັນເກີດ"
registeredDate: "ວັນທີ່ເປັນສະມາຊິກ"
location: "ທີ່ຕັ້ງ"
theme: "ແທ໋ມ"
light: "ສະຫວ່າງ"
dark: "ມືດ"
lightThemes: "ຊຸດຮູບແບບສະຫວ່າງ"
darkThemes: "ຮູບແບບສີສັນມືດ"
fileName: "ຊື່ໄຟລ໌"
selectFile: "ເລືອກໄຟລ໌"
selectFiles: "ເລືອກໄຟລ໌"
nsfw: "NSFW"
accept: "ອະນຸຍາດ"
pinnedNotes: "ບັນທຶກທີ່ປັກໝຸດໄວ້"
userList: "ລາຍການ" userList: "ລາຍການ"
smtpUser: "ຊື່ຜູ້ໃຊ້" smtpUser: "ຊື່ຜູ້ໃຊ້"
smtpPass: "ລະຫັດຜ່ານ" smtpPass: "ລະຫັດຜ່ານ"
@ -123,6 +189,8 @@ _email:
title: "ໄດ້ຕິດຕາມທ່ານ" title: "ໄດ້ຕິດຕາມທ່ານ"
_mfm: _mfm:
mention: "ໄດ້ກ່າວມາ" mention: "ໄດ້ກ່າວມາ"
quote: "ລວມຂໍ້ຄວາມອ້າງອີງ"
emoji: "ອີໂມຈິແບບກຳນົດເອງ"
search: "ຄົ້ນຫາ" search: "ຄົ້ນຫາ"
_theme: _theme:
keys: keys:
@ -131,25 +199,39 @@ _theme:
_sfx: _sfx:
note: "ບັນທຶກ" note: "ບັນທຶກ"
notification: "ການແຈ້ງເຕືອນ" notification: "ການແຈ້ງເຕືອນ"
chat: "ແຊ໋ດ"
_widgets: _widgets:
profile: "ໂພຼຟາຍ" profile: "ໂພຼຟາຍ"
instanceInfo: "ອີນສະແຕນ"
notifications: "ການແຈ້ງເຕືອນ" notifications: "ການແຈ້ງເຕືອນ"
timeline: "​ເສັ້ນກຳ​ນົດ​ເວ​ລາ​" timeline: "​ເສັ້ນກຳ​ນົດ​ເວ​ລາ​"
_userList:
chooseList: "ເລືອກບັນຊີລາຍການ"
_cw: _cw:
show: "ໂຫຼດເພີ່ມເຕີມ" show: "ໂຫຼດເພີ່ມເຕີມ"
_visibility: _visibility:
home: "ໜ້າຫຼັກ"
followers: "ຜູ້ຕິດຕາມ" followers: "ຜູ້ຕິດຕາມ"
_profile: _profile:
username: "ຊື່ຜູ້ໃຊ້" username: "ຊື່ຜູ້ໃຊ້"
_exportOrImport: _exportOrImport:
followingList: "ກຳລັງຕິດຕາມ" followingList: "ກຳລັງຕິດຕາມ"
muteList: "ປີດສຽງ"
blockingList: "ບ໋ອກ"
userLists: "ລາຍການ" userLists: "ລາຍການ"
_timelines:
home: "ໜ້າຫຼັກ"
_pages:
blocks:
image: "ຮູບພາບ"
_notification: _notification:
youWereFollowed: "ໄດ້ຕິດຕາມທ່ານ" youWereFollowed: "ໄດ້ຕິດຕາມທ່ານ"
_types: _types:
follow: "ກຳລັງຕິດຕາມ" follow: "ກຳລັງຕິດຕາມ"
mention: "ໄດ້ກ່າວມາ" mention: "ໄດ້ກ່າວມາ"
renote: "Renote" renote: "Renote"
quote: "ລວມຂໍ້ຄວາມອ້າງອີງ"
reaction: "ປະຕິກິລິຍາ"
_actions: _actions:
reply: "ຕອບ​ໄປ​ທີ" reply: "ຕອບ​ໄປ​ທີ"
renote: "Renote" renote: "Renote"

View File

@ -940,6 +940,8 @@ cannotPerformTemporaryDescription: "การดําเนินการน
preset: "พรีเซ็ต" preset: "พรีเซ็ต"
selectFromPresets: "เลือกจากการพรีเซ็ต" selectFromPresets: "เลือกจากการพรีเซ็ต"
achievements: "ความสำเร็จ" achievements: "ความสำเร็จ"
gotInvalidResponseError: "การตอบสนองเซิร์ฟเวอร์ไม่ถูกต้อง"
gotInvalidResponseErrorDescription: "เซิร์ฟเวอร์อาจไม่สามารถเข้าถึงได้หรืออาจจะกำลังอยู่ในระหว่างปรับปรุง กรุณาลองใหม่อีกครั้งในภายหลังนะคะ"
_achievements: _achievements:
earnedAt: "ได้รับเมื่อ" earnedAt: "ได้รับเมื่อ"
_types: _types:
@ -1594,12 +1596,15 @@ _permissions:
"read:gallery-likes": "ดูรายการโพสต์ในแกลเลอรีที่ชอบของคุณ" "read:gallery-likes": "ดูรายการโพสต์ในแกลเลอรีที่ชอบของคุณ"
"write:gallery-likes": "แก้ไขรายการโพสต์ในแกลเลอรีที่ชอบของคุณ" "write:gallery-likes": "แก้ไขรายการโพสต์ในแกลเลอรีที่ชอบของคุณ"
_auth: _auth:
shareAccessTitle: "การให้สิทธิ์แอปพลิเคชัน"
shareAccess: "คุณต้องการอนุญาตให้ \"{name}\" เข้าถึงบัญชีนี้เลยมั้ย?" shareAccess: "คุณต้องการอนุญาตให้ \"{name}\" เข้าถึงบัญชีนี้เลยมั้ย?"
shareAccessAsk: "คุณแน่ใจแล้วจริงๆหรอว่าต้องการอนุญาตให้แอปพลิเคชันนี้เข้าถึงบัญชีของคุณแน่ใจแล้วหรอ?" shareAccessAsk: "คุณแน่ใจแล้วจริงๆหรอว่าต้องการอนุญาตให้แอปพลิเคชันนี้เข้าถึงบัญชีของคุณแน่ใจแล้วหรอ?"
permission: "{name} ได้ขอสิทธิ์การเข้าถึงดังต่อไปนี้"
permissionAsk: "แอปพลิเคชันนี้ขอสิทธิ์ดังต่อไปนี้" permissionAsk: "แอปพลิเคชันนี้ขอสิทธิ์ดังต่อไปนี้"
pleaseGoBack: "กรุณากลับไปที่แอปพลิเคชัน" pleaseGoBack: "กรุณากลับไปที่แอปพลิเคชัน"
callback: "กำลังกลับไปที่แอปพลิเคชัน" callback: "กำลังกลับไปที่แอปพลิเคชัน"
denied: "ปฏิเสธการเข้าใช้" denied: "ปฏิเสธการเข้าใช้"
pleaseLogin: "กรุณาเข้าสู่ระบบเพื่ออนุมัติแอปพลิเคชัน"
_antennaSources: _antennaSources:
all: "โน้ตทั้งหมด" all: "โน้ตทั้งหมด"
homeTimeline: "โน้ตจากผู้ใช้ที่ติดตาม" homeTimeline: "โน้ตจากผู้ใช้ที่ติดตาม"

View File

@ -129,6 +129,7 @@ unblockConfirm: "确定要解除拉黑吗?"
suspendConfirm: "要冻结吗?" suspendConfirm: "要冻结吗?"
unsuspendConfirm: "要解除冻结吗?" unsuspendConfirm: "要解除冻结吗?"
selectList: "选择列表" selectList: "选择列表"
selectChannel: "选择频道"
selectAntenna: "选择天线" selectAntenna: "选择天线"
selectWidget: "选择小工具" selectWidget: "选择小工具"
editWidgets: "编辑部件" editWidgets: "编辑部件"
@ -256,6 +257,8 @@ noMoreHistory: "没有更多的历史记录"
startMessaging: "添加聊天" startMessaging: "添加聊天"
nUsersRead: "{n}人已读" nUsersRead: "{n}人已读"
agreeTo: "勾选则表示已阅读并同意{0}" agreeTo: "勾选则表示已阅读并同意{0}"
agreeBelow: "同意以下观点"
basicNotesBeforeCreateAccount: "基本注意事项"
tos: "服务条款" tos: "服务条款"
start: "开始" start: "开始"
home: "首页" home: "首页"
@ -861,6 +864,8 @@ failedToFetchAccountInformation: "获取账户信息失败"
rateLimitExceeded: "已超過速率限制" rateLimitExceeded: "已超過速率限制"
cropImage: "剪裁图像" cropImage: "剪裁图像"
cropImageAsk: "是否要裁剪图像?" cropImageAsk: "是否要裁剪图像?"
cropYes: "已裁剪"
cropNo: "就这样吧!"
file: "文件" file: "文件"
recentNHours: "最近{n}小时" recentNHours: "最近{n}小时"
recentNDays: "最近{n}天" recentNDays: "最近{n}天"
@ -939,6 +944,8 @@ cannotPerformTemporaryDescription: "因操作过于频繁,暂时不可用,
preset: "預設值" preset: "預設值"
selectFromPresets: "從預設值中選擇" selectFromPresets: "從預設值中選擇"
achievements: "成就" achievements: "成就"
gotInvalidResponseError: "服务器无应答"
gotInvalidResponseErrorDescription: "您的网络连接可能出现了问题, 或是远程服务器暂时不可用. 请稍后重试。"
_achievements: _achievements:
earnedAt: "达成时间" earnedAt: "达成时间"
_types: _types:
@ -1122,7 +1129,7 @@ _achievements:
description: "在0点发布一篇帖子" description: "在0点发布一篇帖子"
flavor: "嘣 嘣 嘣 Biu——" flavor: "嘣 嘣 嘣 Biu——"
_selfQuote: _selfQuote:
title: "自我提及" title: "自我引用"
description: "引用了自己的帖子" description: "引用了自己的帖子"
_htl20npm: _htl20npm:
title: "流动的时间线" title: "流动的时间线"
@ -1593,12 +1600,15 @@ _permissions:
"read:gallery-likes": "读取喜欢的图片" "read:gallery-likes": "读取喜欢的图片"
"write:gallery-likes": "操作喜欢的图片" "write:gallery-likes": "操作喜欢的图片"
_auth: _auth:
shareAccessTitle: "应用程序授权许可"
shareAccess: "您要授权允许“{name}”访问您的帐户吗?" shareAccess: "您要授权允许“{name}”访问您的帐户吗?"
shareAccessAsk: "您确定要授权此应用程序访问您的帐户吗?" shareAccessAsk: "您确定要授权此应用程序访问您的帐户吗?"
permission: "{name}需要以下权限"
permissionAsk: "这个应用程序需要以下权限" permissionAsk: "这个应用程序需要以下权限"
pleaseGoBack: "请返回到应用程序" pleaseGoBack: "请返回到应用程序"
callback: "回到应用程序" callback: "回到应用程序"
denied: "拒绝访问" denied: "拒绝访问"
pleaseLogin: "在对应用进行授权许可之前,请先登录"
_antennaSources: _antennaSources:
all: "所有帖子" all: "所有帖子"
homeTimeline: "已关注用户的帖子" homeTimeline: "已关注用户的帖子"

View File

@ -129,6 +129,7 @@ unblockConfirm: "確定解除封鎖此用戶?"
suspendConfirm: "確定凍結此帳號?" suspendConfirm: "確定凍結此帳號?"
unsuspendConfirm: "確定解凍此帳號?" unsuspendConfirm: "確定解凍此帳號?"
selectList: "選擇清單" selectList: "選擇清單"
selectChannel: "選擇頻道"
selectAntenna: "選擇天線" selectAntenna: "選擇天線"
selectWidget: "選擇小工具" selectWidget: "選擇小工具"
editWidgets: "編輯小工具" editWidgets: "編輯小工具"
@ -256,6 +257,8 @@ noMoreHistory: "沒有更多歷史紀錄"
startMessaging: "開始聊天" startMessaging: "開始聊天"
nUsersRead: "{n}人已讀" nUsersRead: "{n}人已讀"
agreeTo: "我同意{0}" agreeTo: "我同意{0}"
agreeBelow: "同意以下內容"
basicNotesBeforeCreateAccount: "基本注意事項"
tos: "使用條款" tos: "使用條款"
start: "開始" start: "開始"
home: "首頁" home: "首頁"
@ -861,6 +864,8 @@ failedToFetchAccountInformation: "取得帳戶資訊失敗"
rateLimitExceeded: "已超過速率限制" rateLimitExceeded: "已超過速率限制"
cropImage: "圖片裁剪" cropImage: "圖片裁剪"
cropImageAsk: "要剪裁圖片嗎?" cropImageAsk: "要剪裁圖片嗎?"
cropYes: "裁剪"
cropNo: "使用原圖"
file: "檔案" file: "檔案"
recentNHours: "過去{n}小時" recentNHours: "過去{n}小時"
recentNDays: "過去{n}天" recentNDays: "過去{n}天"
@ -1595,12 +1600,15 @@ _permissions:
"read:gallery-likes": "讀取喜歡的圖片" "read:gallery-likes": "讀取喜歡的圖片"
"write:gallery-likes": "操作喜歡的圖片" "write:gallery-likes": "操作喜歡的圖片"
_auth: _auth:
shareAccessTitle: "應用程式的存取權限"
shareAccess: "要授權「“{name}”」存取您的帳戶嗎?" shareAccess: "要授權「“{name}”」存取您的帳戶嗎?"
shareAccessAsk: "您確定要授權這個應用程式使用您的帳戶嗎?" shareAccessAsk: "您確定要授權這個應用程式使用您的帳戶嗎?"
permission: "{name}要求以下的權限"
permissionAsk: "此應用程式需要以下權限" permissionAsk: "此應用程式需要以下權限"
pleaseGoBack: "請返回至應用程式" pleaseGoBack: "請返回至應用程式"
callback: "回到應用程式" callback: "回到應用程式"
denied: "拒絕訪問" denied: "拒絕訪問"
pleaseLogin: "必須登入以提供應用程式的存取權限。"
_antennaSources: _antennaSources:
all: "全部貼文" all: "全部貼文"
homeTimeline: "來自已追隨使用者的貼文" homeTimeline: "來自已追隨使用者的貼文"

View File

@ -1,12 +1,12 @@
{ {
"name": "misskey", "name": "misskey",
"version": "13.5.5", "version": "13.5.6",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/misskey-dev/misskey.git" "url": "https://github.com/misskey-dev/misskey.git"
}, },
"packageManager": "pnpm@7.24.3", "packageManager": "pnpm@7.27.0",
"workspaces": [ "workspaces": [
"packages/frontend", "packages/frontend",
"packages/backend", "packages/backend",

View File

@ -255,8 +255,21 @@ export class FileServerService {
const isConvertibleImage = isMimeImage(file.mime, 'sharp-convertible-image'); const isConvertibleImage = isMimeImage(file.mime, 'sharp-convertible-image');
const isAnimationConvertibleImage = isMimeImage(file.mime, 'sharp-animation-convertible-image'); const isAnimationConvertibleImage = isMimeImage(file.mime, 'sharp-animation-convertible-image');
if (
'emoji' in request.query ||
'avatar' in request.query ||
'static' in request.query ||
'preview' in request.query ||
'badge' in request.query
) {
if (!isConvertibleImage) {
// 画像でないなら404でお茶を濁す
throw new StatusError('Unexpected mime', 404);
}
}
let image: IImageStreamable | null = null; let image: IImageStreamable | null = null;
if (('emoji' in request.query || 'avatar' in request.query) && isConvertibleImage) { if ('emoji' in request.query || 'avatar' in request.query) {
if (!isAnimationConvertibleImage && !('static' in request.query)) { if (!isAnimationConvertibleImage && !('static' in request.query)) {
image = { image = {
data: fs.createReadStream(file.path), data: fs.createReadStream(file.path),
@ -277,16 +290,11 @@ export class FileServerService {
type: 'image/webp', type: 'image/webp',
}; };
} }
} else if ('static' in request.query && isConvertibleImage) { } else if ('static' in request.query) {
image = this.imageProcessingService.convertToWebpStream(file.path, 498, 280); image = this.imageProcessingService.convertToWebpStream(file.path, 498, 280);
} else if ('preview' in request.query && isConvertibleImage) { } else if ('preview' in request.query) {
image = this.imageProcessingService.convertToWebpStream(file.path, 200, 200); image = this.imageProcessingService.convertToWebpStream(file.path, 200, 200);
} else if ('badge' in request.query) { } else if ('badge' in request.query) {
if (!isConvertibleImage) {
// 画像でないなら404でお茶を濁す
throw new StatusError('Unexpected mime', 404);
}
const mask = sharp(file.path) const mask = sharp(file.path)
.resize(96, 96, { .resize(96, 96, {
fit: 'inside', fit: 'inside',

View File

@ -107,6 +107,7 @@ onMounted(() => {
} }
.iconFrame { .iconFrame {
position: relative;
width: 58px; width: 58px;
height: 58px; height: 58px;
padding: 6px; padding: 6px;

View File

@ -1,5 +1,5 @@
<template> <template>
<button class="_button" :class="$style.root" @click="toggle"> <button class="_button" :class="$style.root" @mousedown="toggle">
<b>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}</b> <b>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}</b>
<span v-if="!modelValue" :class="$style.label">{{ label }}</span> <span v-if="!modelValue" :class="$style.label">{{ label }}</span>
</button> </button>

View File

@ -28,8 +28,8 @@
</template> </template>
</MkSelect> </MkSelect>
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> <div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
<MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ (showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt }}</MkButton> <MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
<MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ i18n.ts.cancel }}</MkButton> <MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
</div> </div>
<div v-if="actions" :class="$style.buttons"> <div v-if="actions" :class="$style.buttons">
<MkButton v-for="action in actions" :key="action.text" inline :primary="action.primary" @click="() => { action.callback(); close(); }">{{ action.text }}</MkButton> <MkButton v-for="action in actions" :key="action.text" inline :primary="action.primary" @click="() => { action.callback(); close(); }">{{ action.text }}</MkButton>
@ -82,6 +82,8 @@ const props = withDefaults(defineProps<{
showOkButton?: boolean; showOkButton?: boolean;
showCancelButton?: boolean; showCancelButton?: boolean;
cancelableByBgClick?: boolean; cancelableByBgClick?: boolean;
okText?: string;
cancelText?: string;
}>(), { }>(), {
type: 'info', type: 'info',
showOkButton: true, showOkButton: true,

View File

@ -56,7 +56,7 @@ onMounted(() => {
width: 100%; width: 100%;
border-radius: 4px; border-radius: 4px;
margin-top: 4px; margin-top: 4px;
overflow: clip; // overflow: clip;
--plyr-color-main: var(--accent); --plyr-color-main: var(--accent);
--plyr-audio-controls-background: var(--bg); --plyr-audio-controls-background: var(--bg);
@ -99,7 +99,7 @@ onMounted(() => {
> .audio { > .audio {
border-radius: 8px; border-radius: 8px;
overflow: clip; // overflow: clip;
} }
} }
</style> </style>

View File

@ -50,13 +50,13 @@
<span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span> <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span>
</template> </template>
</MkInput> </MkInput>
<MkSwitch v-if="instance.tosUrl" v-model="ToSAgreement" class="tou"> <MkSwitch v-model="ToSAgreement" class="tou">
<I18n :src="i18n.ts.agreeTo"> <template #label>{{ i18n.ts.agreeBelow }}</template>
<template #0>
<a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.tos }}</a>
</template>
</I18n>
</MkSwitch> </MkSwitch>
<ul style="margin: 0; padding-left: 2em;">
<li v-if="instance.tosUrl"><a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.tos }}</a></li>
<li><a href="https://misskey-hub.net/docs/notes.html" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }}</a></li>
</ul>
<MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/>
<MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
<MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" class="captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" class="captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/>

View File

@ -1,11 +1,19 @@
<template> <template>
<span v-if="!link" v-user-preview="preview ? user.id : undefined" :class="[$style.root, { [$style.cat]: user.isCat, [$style.square]: $store.state.squareAvatars }]" class="_noSelect" :style="{ color }" :title="acct(user)" @click="onClick"> <span v-if="!link" v-user-preview="preview ? user.id : undefined" class="_noSelect" :class="[$style.root, { [$style.cat]: user.isCat, [$style.square]: $store.state.squareAvatars }]" :style="{ color }" :title="acct(user)" @click="onClick">
<img :class="$style.inner" :src="url" decoding="async"/> <img :class="$style.inner" :src="url" decoding="async"/>
<MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/> <MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/>
<template v-if="user.isCat">
<div :class="$style.earLeft"/>
<div :class="$style.earRight"/>
</template>
</span> </span>
<MkA v-else v-user-preview="preview ? user.id : undefined" class="_noSelect" :class="[$style.root, { [$style.cat]: user.isCat, [$style.square]: $store.state.squareAvatars }]" :style="{ color }" :to="userPage(user)" :title="acct(user)" :target="target"> <MkA v-else v-user-preview="preview ? user.id : undefined" class="_noSelect" :class="[$style.root, { [$style.cat]: user.isCat, [$style.square]: $store.state.squareAvatars }]" :style="{ color }" :title="acct(user)" :to="userPage(user)" :target="target">
<img :class="$style.inner" :src="url" decoding="async"/> <img :class="$style.inner" :src="url" decoding="async"/>
<MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/> <MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/>
<template v-if="user.isCat">
<div :class="$style.earLeft"/>
<div :class="$style.earRight"/>
</template>
</MkA> </MkA>
</template> </template>
@ -110,32 +118,41 @@ watch(() => props.user.avatarBlurhash, () => {
} }
.cat { .cat {
&:before, &:after { > .earLeft,
background: #df548f; > .earRight {
border: solid 4px currentColor; contain: strict;
box-sizing: border-box;
content: '';
display: inline-block; display: inline-block;
height: 50%; height: 50%;
width: 50%; width: 50%;
background: currentColor;
&::before {
contain: strict;
content: '';
display: block;
width: 60%;
height: 60%;
margin: 20%;
background: #df548f;
}
} }
&:before { > .earLeft {
border-radius: 0 75% 75%; border-radius: 0 75% 75%;
transform: rotate(37.5deg) skew(30deg); transform: rotate(37.5deg) skew(30deg);
} }
&:after { > .earRight {
border-radius: 75% 0 75% 75%; border-radius: 75% 0 75% 75%;
transform: rotate(-37.5deg) skew(-30deg); transform: rotate(-37.5deg) skew(-30deg);
} }
&:hover { &:hover {
&:before { > .earLeft {
animation: earwiggleleft 1s infinite; animation: earwiggleleft 1s infinite;
} }
&:after { > .earRight {
animation: earwiggleright 1s infinite; animation: earwiggleright 1s infinite;
} }
} }

View File

@ -171,6 +171,8 @@ export function confirm(props: {
type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
title?: string | null; title?: string | null;
text?: string | null; text?: string | null;
okText?: string;
cancelText?: string;
}): Promise<{ canceled: boolean }> { }): Promise<{ canceled: boolean }> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
popup(MkDialog, { popup(MkDialog, {

View File

@ -1,60 +1,66 @@
<template> <template>
<section class=""> <section>
<div class="">{{ $t('_auth.shareAccess', { name: app.name }) }}</div> <div v-if="app.permission.length > 0">
<div class=""> <p>{{ $t('_auth.permission', { name }) }}</p>
<h2>{{ app.name }}</h2> <ul>
<p class="id">{{ app.id }}</p> <li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
<p class="description">{{ app.description }}</p> </ul>
</div> </div>
<div class=""> <div>{{ i18n.t('_auth.shareAccess', { name: `${name} (${app.id})` }) }}</div>
<h2>{{ $ts._auth.permissionAsk }}</h2> <div :class="$style.buttons">
<ul> <MkButton inline @click="cancel">{{ i18n.ts.cancel }}</MkButton>
<li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li> <MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton>
</ul> </div>
</div> </section>
<div class="">
<MkButton inline @click="cancel">{{ $ts.cancel }}</MkButton>
<MkButton inline primary @click="accept">{{ $ts.accept }}</MkButton>
</div>
</section>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue'; import { } from 'vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n';
import { AuthSession } from 'misskey-js/built/entities';
export default defineComponent({ const props = defineProps<{
components: { session: AuthSession;
MkButton, }>();
},
props: ['session'],
computed: {
name(): string {
const el = document.createElement('div');
el.textContent = this.app.name;
return el.innerHTML;
},
app(): any {
return this.session.app;
},
},
methods: {
cancel() {
os.api('auth/deny', {
token: this.session.token,
}).then(() => {
this.$emit('denied');
});
},
accept() { const emit = defineEmits<{
os.api('auth/accept', { (event: 'accepted'): void;
token: this.session.token, (event: 'denied'): void;
}).then(() => { }>();
this.$emit('accepted');
}); const app = $computed(() => props.session.app);
},
}, const name = $computed(() => {
const el = document.createElement('div');
el.textContent = app.name;
return el.innerHTML;
}); });
function cancel() {
os.api('auth/deny', {
token: props.session.token,
}).then(() => {
emit('denied');
});
}
function accept() {
os.api('auth/accept', {
token: props.session.token,
}).then(() => {
emit('accepted');
});
}
</script> </script>
<style lang="scss" module>
.buttons {
margin-top: 16px;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
</style>

View File

@ -1,93 +1,105 @@
<template> <template>
<div v-if="$i && fetching" class=""> <MkStickyContainer>
<MkLoading/> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template>
</div> <MkSpacer :content-max="500">
<div v-else-if="$i"> <div v-if="state == 'fetch-session-error'">
<XForm <p>{{ i18n.ts.somethingHappened }}</p>
v-if="state == 'waiting'" </div>
ref="form" <div v-else-if="$i && !session">
class="form" <MkLoading />
:session="session" </div>
@denied="state = 'denied'" <div v-else-if="$i && session">
@accepted="accepted" <XForm
/> v-if="state == 'waiting'"
<div v-if="state == 'denied'" class="denied"> class="form"
<h1>{{ $ts._auth.denied }}</h1> :session="session"
</div> @denied="state = 'denied'"
<div v-if="state == 'accepted'" class="accepted"> @accepted="accepted"
<h1>{{ session.app.isAuthorized ? $t('already-authorized') : $ts.allowed }}</h1> />
<p v-if="session.app.callbackUrl">{{ $ts._auth.callback }}<MkEllipsis/></p> <div v-if="state == 'denied'">
<p v-if="!session.app.callbackUrl">{{ $ts._auth.pleaseGoBack }}</p> <h1>{{ i18n.ts._auth.denied }}</h1>
</div> </div>
<div v-if="state == 'fetch-session-error'" class="error"> <div v-if="state == 'accepted' && session">
<p>{{ $ts.somethingHappened }}</p> <h1>{{ session.app.isAuthorized ? $t('already-authorized') : i18n.ts.allowed }}</h1>
</div> <p v-if="session.app.callbackUrl">{{ i18n.ts._auth.callback }}
</div> <MkEllipsis />
<div v-else class="signin"> </p>
<MkSignin @login="onLogin"/> <p v-if="!session.app.callbackUrl">{{ i18n.ts._auth.pleaseGoBack }}</p>
</div> </div>
</div>
<div v-else>
<p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p>
<MkSignin @login="onLogin" />
</div>
</MkSpacer>
</MkStickyContainer>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue'; import { onMounted } from 'vue';
import XForm from './auth.form.vue'; import XForm from './auth.form.vue';
import MkSignin from '@/components/MkSignin.vue'; import MkSignin from '@/components/MkSignin.vue';
import * as os from '@/os'; import * as os from '@/os';
import { login } from '@/account'; import { $i, login } from '@/account';
import { definePageMetadata } from '@/scripts/page-metadata';
import { AuthSession } from 'misskey-js/built/entities';
import { i18n } from '@/i18n';
export default defineComponent({ const props = defineProps<{
components: { token: string;
XForm, }>();
MkSignin,
},
props: ['token'],
data() {
return {
state: null,
session: null,
fetching: true,
};
},
mounted() {
if (!this.$i) return;
// Fetch session let state = $ref<'waiting' | 'accepted' | 'fetch-session-error' | 'denied' | null>(null);
os.api('auth/session/show', { let session = $ref<AuthSession | null>(null);
token: this.token,
}).then(session => {
this.session = session;
this.fetching = false;
// function accepted() {
if (this.session.app.isAuthorized) { state = 'accepted';
os.api('auth/accept', { if (session && session.app.callbackUrl) {
token: this.session.token, const url = new URL(session.app.callbackUrl);
}).then(() => { if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url');
this.accepted(); location.href = `${session.app.callbackUrl}?token=${session.token}`;
}); }
} else { }
this.state = 'waiting';
} function onLogin(res) {
}).catch(error => { login(res.i);
this.state = 'fetch-session-error'; }
this.fetching = false;
onMounted(async () => {
if (!$i) return;
try {
session = await os.api('auth/session/show', {
token: props.token,
}); });
},
methods: { //
accepted() { if (session.app.isAuthorized) {
this.state = 'accepted'; await os.api('auth/accept', {
if (this.session.app.callbackUrl) { token: session.token,
const url = new URL(this.session.app.callbackUrl); });
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url'); accepted();
location.href = `${this.session.app.callbackUrl}?token=${this.session.token}`; } else {
} state = 'waiting';
}, onLogin(res) { }
login(res.i); } catch (e) {
}, state = 'fetch-session-error';
}, }
});
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
definePageMetadata({
title: i18n.ts._auth.shareAccessTitle,
icon: 'ti ti-apps',
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.loginMessage {
text-align: center;
margin: 8px 0 24px;
}
</style> </style>

View File

@ -1,41 +1,40 @@
<template> <template>
<MkSpacer :content-max="800"> <MkStickyContainer>
<div v-if="$i"> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template>
<div v-if="state == 'waiting'" class="waiting"> <MkSpacer :content-max="800">
<div class=""> <div v-if="$i">
<div v-if="state == 'waiting'">
<MkLoading/> <MkLoading/>
</div> </div>
</div> <div v-if="state == 'denied'">
<div v-if="state == 'denied'" class="denied">
<div class="">
<p>{{ i18n.ts._auth.denied }}</p> <p>{{ i18n.ts._auth.denied }}</p>
</div> </div>
</div> <div v-else-if="state == 'accepted'" class="accepted">
<div v-else-if="state == 'accepted'" class="accepted">
<div class="">
<p v-if="callback">{{ i18n.ts._auth.callback }}<MkEllipsis/></p> <p v-if="callback">{{ i18n.ts._auth.callback }}<MkEllipsis/></p>
<p v-else>{{ i18n.ts._auth.pleaseGoBack }}</p> <p v-else>{{ i18n.ts._auth.pleaseGoBack }}</p>
</div> </div>
</div> <div v-else>
<div v-else class=""> <div v-if="_permissions.length > 0">
<div v-if="name" class="">{{ $t('_auth.shareAccess', { name: name }) }}</div> <p v-if="name">{{ $t('_auth.permission', { name }) }}</p>
<div v-else class="">{{ i18n.ts._auth.shareAccessAsk }}</div> <p v-else>{{ i18n.ts._auth.permissionAsk }}</p>
<div class=""> <ul>
<p>{{ i18n.ts._auth.permissionAsk }}</p> <li v-for="p in _permissions" :key="p">{{ $t(`_permissions.${p}`) }}</li>
<ul> </ul>
<li v-for="p in _permissions" :key="p">{{ $t(`_permissions.${p}`) }}</li> </div>
</ul> <div v-if="name">{{ $t('_auth.shareAccess', { name }) }}</div>
</div> <div v-else>{{ i18n.ts._auth.shareAccessAsk }}</div>
<div class=""> <div :class="$style.buttons">
<MkButton inline @click="deny">{{ i18n.ts.cancel }}</MkButton> <MkButton inline @click="deny">{{ i18n.ts.cancel }}</MkButton>
<MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton> <MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton>
</div>
</div> </div>
</div> </div>
</div> <div v-else>
<div v-else class="signin"> <p :class="$style.loginMessage">{{ i18n.ts._auth.pleaseLogin }}</p>
<MkSignin @login="onLogin"/> <MkSignin @login="onLogin"/>
</div> </div>
</MkSpacer> </MkSpacer>
</MkStickyContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -45,6 +44,7 @@ import MkButton from '@/components/MkButton.vue';
import * as os from '@/os'; import * as os from '@/os';
import { $i, login } from '@/account'; import { $i, login } from '@/account';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
const props = defineProps<{ const props = defineProps<{
session: string; session: string;
@ -54,7 +54,7 @@ const props = defineProps<{
permission: string; // permission: string; //
}>(); }>();
const _permissions = props.permission.split(','); const _permissions = props.permission ? props.permission.split(',') : [];
let state = $ref<string | null>(null); let state = $ref<string | null>(null);
@ -83,8 +83,27 @@ function deny(): void {
function onLogin(res): void { function onLogin(res): void {
login(res.i); login(res.i);
} }
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
definePageMetadata({
title: 'MiAuth',
icon: 'ti ti-apps',
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" module>
.buttons {
margin-top: 16px;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.loginMessage {
text-align: center;
margin: 8px 0 24px;
}
</style> </style>

View File

@ -150,6 +150,8 @@ function changeAvatar(ev) {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'question', type: 'question',
text: i18n.t('cropImageAsk'), text: i18n.t('cropImageAsk'),
okText: i18n.ts.cropYes,
cancelText: i18n.ts.cropNo,
}); });
if (!canceled) { if (!canceled) {
@ -174,6 +176,8 @@ function changeBanner(ev) {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'question', type: 'question',
text: i18n.t('cropImageAsk'), text: i18n.t('cropImageAsk'),
okText: i18n.ts.cropYes,
cancelText: i18n.ts.cropNo,
}); });
if (!canceled) { if (!canceled) {

View File

@ -35,7 +35,7 @@
<i class="icon ti ti-pencil ti-fw"></i><span class="text">{{ i18n.ts.note }}</span> <i class="icon ti ti-pencil ti-fw"></i><span class="text">{{ i18n.ts.note }}</span>
</button> </button>
<button v-click-anime class="item _button account" @click="openAccountMenu"> <button v-click-anime class="item _button account" @click="openAccountMenu">
<MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/> <MkAvatar :user="$i" class="avatar"/><MkAcct class="text _nowrap" :user="$i"/>
</button> </button>
</div> </div>
</div> </div>
@ -168,20 +168,25 @@ function more() {
display: flex; display: flex;
align-items: center; align-items: center;
padding-left: 30px; padding-left: 30px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 100%; width: 100%;
text-align: left; text-align: left;
box-sizing: border-box; box-sizing: border-box;
margin-top: 16px; margin-top: 16px;
> .avatar { > .avatar {
display: block;
flex-shrink: 0;
position: relative; position: relative;
width: 32px; width: 32px;
aspect-ratio: 1; aspect-ratio: 1;
margin-right: 8px; margin-right: 8px;
} }
> .text {
display: block;
flex-shrink: 1;
padding-right: 8px;
}
} }
} }

View File

@ -45,7 +45,7 @@
<i class="icon ti ti-pencil ti-fw"></i><span class="text">{{ i18n.ts.note }}</span> <i class="icon ti ti-pencil ti-fw"></i><span class="text">{{ i18n.ts.note }}</span>
</button> </button>
<button v-click-anime v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="item _button account" @click="openAccountMenu"> <button v-click-anime v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="item _button account" @click="openAccountMenu">
<MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/> <MkAvatar :user="$i" class="avatar"/><MkAcct class="text _nowrap" :user="$i"/>
</button> </button>
</div> </div>
</div> </div>
@ -217,20 +217,25 @@ function more(ev: MouseEvent) {
display: flex; display: flex;
align-items: center; align-items: center;
padding-left: 30px; padding-left: 30px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 100%; width: 100%;
text-align: left; text-align: left;
box-sizing: border-box; box-sizing: border-box;
margin-top: 16px; margin-top: 16px;
> .avatar { > .avatar {
display: block;
flex-shrink: 0;
position: relative; position: relative;
width: 32px; width: 32px;
aspect-ratio: 1; aspect-ratio: 1;
margin-right: 8px; margin-right: 8px;
} }
> .text {
display: block;
flex-shrink: 1;
padding-right: 8px;
}
} }
} }